Document model - lmparppei/BeatPlugins GitHub Wiki

Beat parser uses Line and Scene objects to store the screenplay content. To manipulate the document, you make direct changes to the plain-text screenplay content and parse your changes. Line object contains useful information about the parsed line, such as line type, position, if it is a Title Page element etc. line.string contains the plain-text contents.

Scene is more of an abstraction. It's used to determine where a scene starts, its total length in characters, its color and if it is visible or not. scene.string property contains the scene heading. A Scene object can be either a normal scene heading, section heading (of any level) or a synopsis line. You can filter out sections and synopsis lines by using Beat.scenes().

You cannot add text by manipulating these objects, and the plugin API won't let you do that, either, as the values are read-only. If you want to add, remove or replace some text in the screenplay, you will need to add it manually using indexes.

Manipulating plain-text content

Beat.addString(String, index) – add string at some index
Beat.replaceRange(index, length, string) – replace a range with a string (which can be empty to remove text)

Any changes you make are directly represented in the Line objects. That's why you can iterate through lines, make changes on the run, and the positions are kept up to date during iteration, as long as you don't add multiple lines.

Accessing parsed content

At the heart of Beat is the parser. You should always look at the parsed content before making any changes to the document.

Lines

PLEASE NOTE: You can't just make changes to the line string objects. Every change to the screenplay has to go through the parser, which means using Beat.addString, Beat.replaceRange etc. to change the document. These values are READ-ONLY, so just read them.

Beat.lines() array contains all the lines in the script as objects. This is not a copy of the array, but the actual line array from parser.

A line object contains multiple values, including but not limited to:

line.string — string content
line.position — starting index of line
line.textRange — range of the line ({ location: ..., range: ... })
line.range — full location and length INCLUDING line break
line.type — returns an integer value, matches Beat.type values
line.typeAsString() — returns type as string, "Heading" / "Action" / "Dialogue" / "Parenthetical" etc.
line.characterName() — returns the character name only, with possible extensions removed
line.uuid — unique identifier for this line, can be used to match line to paginated and exported content
line.isOutlineElement() — true/false for scene headings, sections and synopsis lines
line.isTitlePage() — true/false
line.isInvisible() — true/false
line.cleanedString() — string with non-printing stuff removed
line.stripFormatting() – string with non-printing stuff and any Fountain formatting removed
line.omitted— true/false
line.note — if the line is a note (wrapped in [](/lmparppei/BeatPlugins/wiki/)), true/false
line.clone() — make a copy of the line, detached from the parser
line.uuidString() – returns the unique identifier fo this line, which can be matched against JSON data, for example
line.forSerialization() — returns JSON data for the line
line.setCustomData(key, value) — set custom data for a key
line.getCustomData(key) — get custom data for a key

Examples

Iterate through lines:

for (const line of Beat.lines()) {  
    // Do something  
}  

Finding out line type

for (const line of Beat.lines()) {  
    // Check the line type using constants
    if (line.type == Beat.type.heading) {
        // This line is a scene heading
    }

    // Type is available as a string, too
    if (line.typeAsString() == "Character") {
        // This line is a character cue
    }
}  

Line types

Line type can be retrieved using either line.type (integer value) or line.typeAsString(), which returns a human-readable version of the type, such as "Heading", "Character", "Action" etc.

Integer values can be matched against constants in Beat.type:

if (line.type == Beat.type.action) { }

Valid line types

Note: There are some discrepancies between Beat.type and .typeAsString() because of legacy reasons.

Type Type as String Explanation
Beat.type.empty Empty empty line
Beat.type.heading Heading scene heading
Beat.type.action Action action
Beat.type.character Character character cue
Beat.type.parenthetical Parenthetical parenthetical line
Beat.type.dialogue Dialogue dialogue
Beat.type.dualDialogueCharacter DD Character dual dialogue character cue
Beat.type.dualDialogueParenthetical DD Parenthetical dual dialogue parenthetical line
Beat.type.dualDialogue DD Dialogue dual dialogue
Beat.type.transition Transition transition
Beat.type.lyrics Lyrics lyrics
Beat.type.pageBreak Page Break forced page break (===)
Beat.type.centered Centered centered text (> text <)
Beat.type.synopsis Synopse synopsis line (= Synopsis)
Beat.type.section Section section (# Section)
Title page types:
Beat.type.titlePageTitle Title Page Title title
Beat.type.titlePageAuthor Title Page Author author
Beat.type.titlePageCredit Title Page Credit credit ("by", "par" etc.)
Beat.type.titlePageSource Title Page Source source (based on)
Beat.type.titlePageContact Title Page Contact contact info
Beat.type.titlePageDraftDate Title Page Draft Date draft date
Beat.type.titlePageUnknown Title Page Unknown custom title page field

Scenes

scene.line – line object which begins the scene (ie. heading)
scene.position — starting index
scene.length — scene length in characters
scene.string — scene heading (eg. INT. HOUSE - DAY)
scene.color — scene color as string
scene.omited — true/false
scene.storylines — storylines for the scene
scene.sectionDepth – depth of a section element
scene.typeAsString() — scene type (heading, section, synopse)

Examples

Iterate through the outline (includes sections and synopsis markers):

for (const scene of Beat.outline()) {
    // ...
}

Iterate through scenes only:

const scenes = Beat.scenes()
for (const scene of scenes) {
    // ...
}

NOTE: Avoid calling Beat.scenes() and Beat.outline() too often. Both build the whole outline from scratch and can slow down your performance. If possible, save the structure into a variable in the beginning: const scenes = Beat.scenes()

JSON Serialization

If you want to store or move data around (to a HTML window, for example), Beat has easy serialization methods for scene data.

Beat.outlineAsJSON() — complete outline as JSON
Beat.scenesAsJSON() — scenes as JSON

A serialized scene object contains most of the same properties available the API:

{
	"string": string, // Scene heading line / outline element as string
	"typeAsString": string, // Type as string
	"stringForDisplay": string, // String without formatting data
	"storylines": array, // Storylines in the scene
	"sceneNumber": string, // Scene number (either automatically assigned or forced)
	"color": string, // Color name (ie. "red")
	"sectionDepth": int, // How deep in section hierarchy the scene is
	"markerColors": array, // Colors of all markers inside this scene
	"range": { location: int, length: int }, // Location and length of the scene
	"omitted": bool, // if the scene is inside an omit block, true/false 
	"storybeats": [{ storyline: string, beat: string }], // Array of storyline names and beats
	"line": line // Serialized heading line 
        "ranges": { "bold": [], "underline": [], "italic": [], "notes": [] } // Specific in-line ranges
}

Revision and tagging data

Further reading: Revisions

Revisions and tags live as attributes in the editor text, and are not written into parsed lines until they are "baked" in. Usually the revision data is baked into the lines when exporting, but you can do the baking manually when needed.