×

Text Objects

When the use of text formatting involves more than the use of level styles, and includes the application of character-level styling, text objects provide a mechanism for associating formatting data with text strings.

A text object is a container that holds text and related style information, as well as optional references to attachments, and even other text objects.

Creating Text Objects

To create a text object, instantiate an instance of the Text class by providing the text string and the assigned style object. In the example shown below, the base style for the document is used:

When the following script example is run in the automation console, the result is an object reference to the created text object followed by a properties record displaying the default property values of the created text object.

Create New Text Object


textObj = new Text('How Now Brown Cow', document.outline.baseStyle) //--> [object Text] {attachments: [], attributeRuns: [[object Text]], characters: [[object Text], [object Text], [object Text], [object Text], [object Text], [object Text], [object Text], [object Text], [object Text], [object Text], …], end: [object Text.Position], fileWrapper: null, paragraphs: [[object Text]], range: [object Text.Range], sentences: [[object Text]], start: [object Text.Position], string: "How Now Brown Cow", style: [object Style], words: [[object Text], [object Text], [object Text], [object Text]]}

NOTE: as you can see in the resulting properties record, text objects may contain other text objects.

Instance Properties

As with most scriptable objects, a text object has properties whose values can be accessed. However, in the case of text objects, editing the value of a property will not change the displayed text. To change the display of text objects, the Text.Range class will be used as demonstrated later in this section.

Using the string property of a Text Object and the JavaScript map() function to convert the arrays of returned text objects to arrays of text strings:

Converting Text Objects to String Arrays


textObj = new Text('How Now Brown Cow.', document.outline.baseStyle) textObj.characters.map(txtObj => txtObj.string) //--> ["H","o","w"," ","N","o","w"," ","B","r","o","w","n"," ","C","o","w","."] textObj.words.map(txtObj => txtObj.string) //--> ["How","Now","Brown","Cow"] textObj.sentences.map(txtObj => txtObj.string) //--> ["How Now Brown Cow."] textObj.paragraphs.map(txtObj => txtObj.string) //--> ["How Now Brown Cow."]

Instance Functions

Omni Automation support in OmniOutliner includes a set of functions (commands) for manipulating text objects.

Here’s a simple example of editing a text object by prepending it with another text object assigned with the same style as the target text object. Note the use of the start and style text object properties, and the insert() instance function:

Prepend to Text Object


textObj = new Text('How Now Brown Cow', document.outline.baseStyle) textObj.insert(textObj.start, new Text("Well ", textObj.style)) textObj.string //--> "Well How Now Brown Cow"

And performing an append by using the end property to indicate insertion position:

Append to Text Object


textObj = new Text('How Now Brown Cow', document.outline.baseStyle) textObj.insert(textObj.end, new Text(" Bough", textObj.style)) textObj.string //--> "How Now Brown Cow Bough"

To manipulate the styled content of rows in an outline document, you must first get the text object for the row content by using the TreeNode class of the Editor class:

Prepend Styled Row


editor = document.editors[0] topicColumn = document.outline.outlineColumn node = editor.selection.nodes[0] textObj = node.valueForColumn(topicColumn) textObj.insert(textObj.start, new Text("[12345]", textObj.style))

 1  Store a reference to the current editor object.

 2  Store a reference to the topic column.

 3  Store a reference to the node of the first selected row.

 4  Get a reference to the text object of the row by using the valueForColumn() method, passing in the reference to the topic column.

 5  Prepend text to the row’s text object by using the insert() function.

When the text object of the row is altered, the changes will be reflected in the document:

prepend-styled-row-01 prepend-styled-row-02

As an alternate to using the insert() method, you can use the append() method, as in this example that appends a styled date tag to the end of every row, using the style applied to that row:

Append Date to All Styled Rows


topicColumn = document.outline.outlineColumn rootItem.apply(item => { if (item != rootItem){ textObj = item.valueForColumn(topicColumn) appendStr = ' [' + new Date().toDateString() + ']' appendObj = new Text(appendStr, textObj.style) textObj.append(appendObj) } })
append-date-to-styled-rows

(see the next page for an example of prepending/appending text objects with a specified style)

Iteration Script Templates

Here are script templates for processing the text object content of the main column of rows.

To edit all of the styled rows in an outline document, or all of the selected rows in an outline document, you can use the apply() or forEach() methods with either the rootItem or rootNode objects since both Items and TreeNodes support the valueForColumn() function that returns a text object:

Iterating All Items (rows)


topicColumn = document.outline.outlineColumn rootItem.apply(item => { if (item !== rootItem){ textObj = item.valueForColumn(topicColumn) // processing statements go here } })
Iterating All Nodes (rows)


topicColumn = document.outline.outlineColumn editor = document.editors[0] editor.rootNode.apply(node => { if (!node.isRootNode){ textObj = node.valueForColumn(topicColumn) // processing statements go here } })
Process Selected Items (rows)


topicColumn = document.outline.outlineColumn editor = document.editors[0] editor.selection.items.forEach(item => { textObj = item.valueForColumn(topicColumn) // processing statements go here })
Process Selected Nodes (rows)


topicColumn = document.outline.outlineColumn editor = document.editors[0] editor.selection.nodes.forEach(node => { textObj = node.valueForColumn(topicColumn) // processing statements go here })
 

TextComponent

The TextComponent class is used with the ranges() function to define the delimiter type for the results of the command. For example TextComponent.Words will return the ranges of the words contained in the searched text object.

Ranges for Word Text Objects


wordRanges = textObjToSearch.ranges(TextComponent.Words)

TextComponent Class Properties

 

Text.Range Class

When working with text objects, you use ranges to describe sections within the source text object. You can create new ranges from a source text object, in order to extract data from the originating object.

For example, the following example creates a new text range from the beginning of the third word of the source text object to the end of the source text object, and then converts the new range into a string:

Getting a Section of a Text Object


textObj = new Text('How Now Brown Cow', document.outline.baseStyle) wordRanges = textObj.ranges(TextComponent.Words) sectionRange = new Text.Range(wordRanges[2].start, textObj.end) textObj.textInRange(sectionRange).string //--> "Brown Cow"

Text.Range Properties

Here are the properties of a range:

Changing Style Attributes of Text Objects

Here’s an example demonstrating how to use text components and ranges to change a style attribute of all occurrences of a word:

Change Style Attribute (FontWeight) for All Occurrences of Word


wordToMatch = 'Brown' topicColumn = document.outline.outlineColumn rootItem.apply(item => { if (item !== rootItem){ textObj = item.valueForColumn(topicColumn) wordRanges = textObj.ranges(TextComponent.Words) wordRanges.forEach(range => { if(textObj.textInRange(range).string === wordToMatch){ txtObjStyle = textObj.styleForRange(range) txtObjStyle.set(Style.Attribute.FontWeight, 9) } }) } })
change-font-weight-attribute

And another example of altering a style attribute of a text object by set the value of the link style attribute:

Change Style Attribute (Link) for All Occurrences of Word


wordToMatch = 'iPad' url = URL.fromString('https://www.apple.com/ipad/') topicColumn = document.outline.outlineColumn rootItem.apply(item => { if (item !== rootItem){ textObj = item.valueForColumn(topicColumn) wordRanges = textObj.ranges(TextComponent.Words) wordRanges.forEach(range => { if(textObj.textInRange(range).string === wordToMatch){ txtObjStyle = textObj.styleForRange(range) txtObjStyle.set(Style.Attribute.Link, url) } }) } })
change-link-attribute

Replacing Text Objects

Here's a script demonstrating how use text components and ranges to replace the occurence of specific words with a specified replacement.

Since the script uses ranges, which define a text object’s offset in a container, the array of ranges must be reordered using the the JavaScript reverse() method (line 8), thereby processing from the end to the beginning, avoiding conflicts with the stored ranges of matched word occurrences.

Also note (line 10) that the replacement text object is styled using the same style as the text object it replaces.

Replacing Matched Words


wordToMatch = 'Brown' rWord = 'Red' topicColumn = document.outline.outlineColumn rootItem.apply(item => { if (item !== rootItem){ textObj = item.valueForColumn(topicColumn) wordRanges = textObj.ranges(TextComponent.Words) wordRanges.reverse().forEach(function(range){ if(textObj.textInRange(range).string === wordToMatch){ rObj = new Text(rWord, textObj.styleForRange(range)) textObj.replace(range, rObj) } }) } })
replace-text-objects

Finding Text Strings

As shown above, text object components are used identify and locate specific words in an outline. To identify and locate phrases of words or text strings, the find() method is used.

Text.FindOption

Parameters that determine the manner in which a string search is performed, can be adjusted by including an array of FindOption properties as the value for the optional range parameter of the find() method. Here are the available properties:

For example, a script for finding the occurence of a string that begins (anchored) a searched range, would have a syntax like this:

The find() method with optional parameters


findParams = [Text.FindOption.Anchored, Text.FindOption.CaseInsensitive] range = textObjToSearch.find(stringToFind, findParams, rangeToSearch)

Finding All Occurrences of a String within a Text Object

By default, the find() method returns a reference to the first occurence (if any) of the specified string. A null value is returned when no matches are found. In order to locate all occurrences of a specified string in a text object, a while loop is used with in conjunction with script statements that adjust the range of the searched text to start from the end of the last occurence (below lines 9-10).

Apply Styling to All Occurrences of a String


searchString = 'iPad Pro' topicColumn = document.outline.outlineColumn rootItem.apply(item => { if (item !== rootItem){ textObj = item.valueForColumn(topicColumn) range = textObj.find(searchString) while (range !== null){ textObjStyle = textObj.styleForRange(range) textObjStyle.set(Style.Attribute.FontWeight, 9) searchRange = new Text.Range(range.end, textObj.end) range = textObj.find(searchString, null, searchRange) } } })
styling-matched-strings

Combining Finding Techniques

OutOutliner outlines can provide sources of additional information by attaching web links to their contents, so that readers can learn more through a simple “TAP|CLICK” that automatically loads supporting content in a browser view.

To find and apply styling (and links) to both words and text strings, using the previous techniques of word and string searching is a powerful approach to use.

The following script does just that by searching for both words and strings to apply corresponding web links to the found items. In this example, all occurrences of Apple computing devices in the outline, are converted into “TAP|CLICK” links to corresponding web pages.

Combine Search Techniques
 

var searchStrings = [ {"device":"iPad Pro","link":"https://www.apple.com/ipad-pro/"}, {"device":"iPad mini","link":"https://www.apple.com/ipad-mini-4/"}, {"device":"MacBook Air","link":"https://www.apple.com/macbook-air/"}, {"device":"MacBook Pro","link":"https://www.apple.com/macbook-pro/"}, {"device":"Mac Pro","link":"https://www.apple.com/mac-pro/"}, {"device":"iMac Pro","link":"https://www.apple.com/imac-pro/"}, {"device":"Mac mini","link":"https://www.apple.com/mac-mini/"} ] var searchWords = [ {"device":"iPad","link":"https://www.apple.com/ipad-9.7/"}, {"device":"MacBook","link":"https://www.apple.com/macbook/"}, {"device":"iMac","link":"https://www.apple.com/imac/"} ] var topicColumn = document.outline.outlineColumn findOptions = [Text.FindOption.Literal, Text.FindOption.WidthInsensitive] var textObj rootItem.apply(item => { if (item != rootItem){ textObj = item.valueForColumn(topicColumn) //apply link to search words searchWords.forEach(obj =>{ wordToMatch = obj.device url = URL.fromString(obj.link) wordRanges = textObj.ranges(TextComponent.Words) wordRanges.forEach(range => { if(textObj.textInRange(range).string === wordToMatch){ textObjStyle = textObj.styleForRange(range) textObjStyle.set(Style.Attribute.Link, url) textObjStyle.set(Style.Attribute.FontWeight, 9) } }) }) //apply link to search strings searchStrings.forEach(obj => { searchString = obj.device url = URL.fromString(obj.link) range = textObj.find(searchString,findOptions) while (range !== null){ textObjStyle = textObj.styleForRange(range) textObjStyle.set(Style.Attribute.Link, url) textObjStyle.set(Style.Attribute.FontWeight, 9) searchRange = new Text.Range(range.end, textObj.end) range = textObj.find(searchString, findOptions, searchRange) } }) } })

Here is a link to the Apply Links to All Apple Device Names script saved as an OmniOutliner solitary action: