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:

textObj = new Text('How Now Brown Cow', document.outline.baseStyle) //--> [object text]

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 map() function to convert the arrays of returned text objects to arrays of text strings:

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:

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

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

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

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:

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 fo every row, using the style applied to that row:

topicColumn = document.outline.outlineColumn rootItem.apply(function(item){ if (item != rootItem){ textObj = item.valueForColumn(topicColumn) appendStr = ' [' + new Date().toDateString() + ']' appendObj = new Text(appendStr, textObj.style) textObj.append(appendObj) } })
omnioutliner://localhost/omnijs-run?script=topicColumn%20%3D%20document%2Eoutline%2EoutlineColumn%0ArootItem%2Eapply%28function%28item%29%7B%0A%09if%20%28item%20%21%3D%20rootItem%29%7B%0A%09%09textObj%20%3D%20item%2EvalueForColumn%28topicColumn%29%0A%09%09appendStr%20%3D%20%27%20%5B%27%20%2B%20new%20Date%28%29%2EtoDateString%28%29%20%2B%20%27%5D%27%0A%09%09appendObj%20%3D%20new%20Text%28appendStr%2C%20textObj%2Estyle%29%0A%09%09textObj%2Eappend%28appendObj%29%0A%09%7D%0A%7D%29
append-date-to-styled-rows

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

Iteration 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:

topicColumn = document.outline.outlineColumn rootItem.apply(function(item){ if (item != rootItem){ textObj = item.valueForColumn(topicColumn) // processing statements go here } })
topicColumn = document.outline.outlineColumn document.editors[0].rootNode.apply(function(node){ if (! node.isRootNode){ textObj = node.valueForColumn(topicColumn) // processing statements go here } })
topicColumn = document.outline.outlineColumn document.editors[0].selection.items.forEach(function(item){ textObj = item.valueForColumn(topicColumn) // processing statements go here })
topicColumn = document.outline.outlineColumn document.editors[0].selection.nodes.forEach(function(node){ textObj = node.valueForColumn(topicColumn) // processing statements go here })
document.editors[0].selection.nodes.forEach(function(node){ textObj = node.valueForColumn(topicColumn) // processing statements go here })
 

TextComponent

The TextComponent 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.

TextComponent Class Properties

 

Text.Range

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:

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

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:

wordToMatch = 'Brown' topicColumn = document.outline.outlineColumn rootItem.apply(function(item){ if (item != rootItem){ textObj = item.valueForColumn(topicColumn) wordRanges = textObj.ranges(TextComponent.Words) wordRanges.forEach(function(range){ if(textObj.textInRange(range).string === wordToMatch){ textObj.styleForRange(range).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:

var wordToMatch = 'iPad' var url = URL.fromString('https://www.apple.com/ipad/') var topicColumn = document.outline.outlineColumn rootItem.apply(function(item){ if (item != rootItem){ textObj = item.valueForColumn(topicColumn) wordRanges = textObj.ranges(TextComponent.Words) wordRanges.forEach(function(range){ if(textObj.textInRange(range).string === wordToMatch){ textObj.styleForRange(range).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.

var wordToMatch = 'Brown' var rWord = 'Red' var topicColumn = document.outline.outlineColumn rootItem.apply(function(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:

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).

searchString = 'iPad Pro' topicColumn = document.outline.outlineColumn rootItem.apply(function(item){ if (item != rootItem){ textObj = item.valueForColumn(topicColumn) range = textObj.find(searchString) while (range != null){ textObj.styleForRange(range).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.

/*{ "type": "action", "targets": ["omnioutliner"], "author": "Your Name Here", "description": "Links Apple computing devices to their corresponding web pages.", "label": "Link Apple Devices", "paletteLabel": "Link Apple Devices" }*/ var _ = function(){ var action = new PlugIn.Action(function(selection, sender) { 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(function(item){ if (item != rootItem){ textObj = item.valueForColumn(topicColumn) //apply link to search words searchWords.forEach(function(obj){ wordToMatch = obj.device url = URL.fromString(obj.link) wordRanges = textObj.ranges(TextComponent.Words) wordRanges.forEach(function(range){ if(textObj.textInRange(range).string === wordToMatch){ textObj.styleForRange(range).set(Style.Attribute.Link,url) textObj.styleForRange(range).set(Style.Attribute.FontWeight,9) } }) }) //apply link to search strings searchStrings.forEach(function(obj){ searchString = obj.device url = URL.fromString(obj.link) range = textObj.find(searchString,findOptions) while (range != null){ textObj.styleForRange(range).set(Style.Attribute.Link,url) textObj.styleForRange(range).set(Style.Attribute.FontWeight,9) searchRange = new Text.Range(range.end,textObj.end) range = textObj.find(searchString,findOptions,searchRange) } }) } }) }); action.validate = function(selection, sender) { return true }; return action; }(); _;
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(function(item){ if (item != rootItem){ textObj = item.valueForColumn(topicColumn) //apply link to search words searchWords.forEach(function(obj){ wordToMatch = obj.device url = URL.fromString(obj.link) wordRanges = textObj.ranges(TextComponent.Words) wordRanges.forEach(function(range){ if(textObj.textInRange(range).string === wordToMatch){ textObj.styleForRange(range).set(Style.Attribute.Link,url) textObj.styleForRange(range).set(Style.Attribute.FontWeight,9) } }) }) //apply link to search strings searchStrings.forEach(function(obj){ searchString = obj.device url = URL.fromString(obj.link) range = textObj.find(searchString,findOptions) while (range != null){ textObj.styleForRange(range).set(Style.Attribute.Link,url) textObj.styleForRange(range).set(Style.Attribute.FontWeight,9) searchRange = new Text.Range(range.end,textObj.end) range = textObj.find(searchString,findOptions,searchRange) } }) } })
omnioutliner://localhost/omnijs-run?script=var%20searchStrings%20%3D%20%5B%7B%22device%22%3A%22iPad%20Pro%22%2C%22link%22%3A%22https%3A%2F%2Fwww%2Eapple%2Ecom%2Fipad-pro%2F%22%7D%2C%7B%22device%22%3A%22iPad%20mini%22%2C%22link%22%3A%22https%3A%2F%2Fwww%2Eapple%2Ecom%2Fipad-mini-4%2F%22%7D%2C%7B%22device%22%3A%22MacBook%20Air%22%2C%22link%22%3A%22https%3A%2F%2Fwww%2Eapple%2Ecom%2Fmacbook-air%2F%22%7D%2C%7B%22device%22%3A%22MacBook%20Pro%22%2C%22link%22%3A%22https%3A%2F%2Fwww%2Eapple%2Ecom%2Fmacbook-pro%2F%22%7D%2C%7B%22device%22%3A%22Mac%20Pro%22%2C%22link%22%3A%22https%3A%2F%2Fwww%2Eapple%2Ecom%2Fmac-pro%2F%22%7D%2C%7B%22device%22%3A%22iMac%20Pro%22%2C%22link%22%3A%22https%3A%2F%2Fwww%2Eapple%2Ecom%2Fimac-pro%2F%22%7D%2C%7B%22device%22%3A%22Mac%20mini%22%2C%22link%22%3A%22https%3A%2F%2Fwww%2Eapple%2Ecom%2Fmac-mini%2F%22%7D%5D%0Avar%20searchWords%20%3D%20%5B%7B%22device%22%3A%22iPad%22%2C%22link%22%3A%22https%3A%2F%2Fwww%2Eapple%2Ecom%2Fipad-9%2E7%2F%22%7D%2C%7B%22device%22%3A%22MacBook%22%2C%22link%22%3A%22https%3A%2F%2Fwww%2Eapple%2Ecom%2Fmacbook%2F%22%7D%2C%7B%22device%22%3A%22iMac%22%2C%22link%22%3A%22https%3A%2F%2Fwww%2Eapple%2Ecom%2Fimac%2F%22%7D%5D%0Avar%20topicColumn%20%3D%20document%2Eoutline%2EoutlineColumn%0AfindOptions%20%3D%20%5BText%2EFindOption%2ELiteral%2CText%2EFindOption%2EWidthInsensitive%5D%0Avar%20textObj%0ArootItem%2Eapply%28function%28item%29%7B%0A%09if%20%28item%20%21%3D%20rootItem%29%7B%0A%09%09textObj%20%3D%20item%2EvalueForColumn%28topicColumn%29%0A%09%09%2F%2Fapply%20link%20to%20search%20words%0A%09%09searchWords%2EforEach%28function%28obj%29%7B%0A%09%09%09wordToMatch%20%3D%20obj%2Edevice%0A%09%09%09url%20%3D%20URL%2EfromString%28obj%2Elink%29%0A%09%09%09wordRanges%20%3D%20textObj%2Eranges%28TextComponent%2EWords%29%0A%09%09%09wordRanges%2EforEach%28function%28range%29%7B%0A%09%09%09%09if%28textObj%2EtextInRange%28range%29%2Estring%20%3D%3D%3D%20wordToMatch%29%7B%0A%09%09%09%09%09textObj%2EstyleForRange%28range%29%2Eset%28Style%2EAttribute%2ELink%2Curl%29%0A%09%09%09%09%09textObj%2EstyleForRange%28range%29%2Eset%28Style%2EAttribute%2EFontWeight%2C9%29%0A%09%09%09%09%7D%0A%09%09%09%7D%29%0A%09%09%7D%29%0A%09%09%2F%2Fapply%20link%20to%20search%20strings%0A%09%09searchStrings%2EforEach%28function%28obj%29%7B%0A%09%09%09searchString%20%3D%20obj%2Edevice%0A%09%09%09url%20%3D%20URL%2EfromString%28obj%2Elink%29%0A%09%09%09range%20%3D%20textObj%2Efind%28searchString%2CfindOptions%29%0A%09%09%09while%20%28range%20%21%3D%20null%29%7B%0A%09%09%09%09textObj%2EstyleForRange%28range%29%2Eset%28Style%2EAttribute%2ELink%2Curl%29%0A%09%09%09%09textObj%2EstyleForRange%28range%29%2Eset%28Style%2EAttribute%2EFontWeight%2C9%29%0A%09%09%09%09searchRange%20%3D%20new%20Text%2ERange%28range%2Eend%2CtextObj%2Eend%29%0A%09%09%09%09range%20%3D%20textObj%2Efind%28searchString%2CfindOptions%2CsearchRange%29%0A%09%09%09%7D%0A%09%09%7D%29%0A%09%7D%0A%7D%29

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

macOS_deviceTo install on macOS, download and unpack the ZIP archive, then choose “Plug-Ins…” from the OmniOutliner automation menu, and place the file in the PlugIns folder opened on the desktop. The script will be available from the OmniOutliner Automation menu.

macOS_deviceTo install on iOS, tap the link, choosing the “Open” option in forthcoming dialog. Then tap “More…” and choose the “Copy to OmniOutliner” option. The installed action will appear in the Plug-Ins view on your device, and will be available from the OmniOutliner automation menu.

TIP: to create your own action for assigning links to words or phrases, CLICK|TAP the “Action Console” button in the script above to open an action version the script into the action web console window. Edit the searchStrings and searchWords data objects to your liking and save the action as a file with an “omnijs” name extension.

UNDER CONSTRUCTION

This webpage is in the process of being developed. Any content may change and may not be accurate or complete at this time.

DISCLAIMER