Omni Automation Libraries

For those wishing to automate their Omni applications, libraries are your best friend.

Omni Automation Libraries provide the means for storing, using, and sharing your favorite Omni Automation functions. They can make your automation tasks easier by dramatically shortening your scripts, and they enable the ability to share useful routines with other computers and tablets.

Script Libraries exist as JavaScript script files (.js) placed within the Resources folder in plugin bundles, OR as stand-alone single file plug-ins (see Library Plug-Ins below). The functions contained within a Omni Automation Library can be called from within the host plug-in’s actions, or by scripts executed outside of the host plugin.

This section describes the structure of an Omni Automation Library, how it is registered in its host plugin, and how it is referenced by plugin actions and external scripts.

The Structure of an Omni Automation Library

An Omni Automation Library is essentially a JavaScript script, comprised of a single function that is executed when the library is called (line 16). Within the main function (line 1 ~ line 15) a library object is created (line 2), and the various library functions are added to the library object (line 4, line 9). The main script finishes by returning the created library object populated with the handlers you added (line 14).

(() => { var myLibrary = new PlugIn.Library(new Version("0.1")); myLibrary.myFirstFunction = function(passedParameter){ // function code return myFirstFunctionResult } myLibrary.mySecondFunction = function(passedParameter){ // function code return mySecondFunctionResult } return myLibrary; })();

Libraries can contain as many functions as you find useful to include. The example shown above contains two library functions that can be called from within your plugins or scripts.

Library files are saved with the file extension: .js (which stands for JavaScript) and are placed in the Resources folder in the plugin bundle. IMPORTANT: it is best to avoid using spaces in the filenames of your libraries.

Registering an Omni Automation Library

In order to be available to the user, libraries must be registered by including them in the plugin’s manifest.json file. This file contains metadata about the plugin, as well an array of plugin actions (line 7), and an optional array of one or more libraries (line 17).

Each action element is comprised of a key:value pair for the action’s identifier (filename) (lines 9, 13) and the filename of its corresponding toolbar icon image file (lines 10, 14).

Each library element is comprised of a key:value pair (lines 19, 22) for the library’s identifier, which is the library’s filename without the file extension.

{ "defaultLocale":"en", "identifier": "com.YourIdentifier.NameOfApp", "author": "Your Name or Organization", "description": "A collection of actions (scripts) for NameOfApp.", "version": "1.0", "actions": [ { "identifier": "myFirstAction", "image": "toolbar-icon.png" }, { "identifier": "mySecondAction", "image": "toolbar-icon.png" } ], "libraries": [ { "identifier": "MyFirstLibraryFileName" }, { "identifier": "MySecondLibraryFileName" } ] }

Calling Omni Automation Libraries within a Plugin Action

The example code shown below is of a plugin action. It is comprised of two sections: the validation code (line 14), and the execution code (line 4).

The validation section (line 14) is used to determine if the action should execute, based upon whether or not there is a selection in the frontmost document. If your action does not rely upon a selection, simply return true (15) as shown in the example.

(() => { var action = new PlugIn.Action(function(selection, sender){ // reference libraries within host plug-in bundle var stencilLib = this.StencilLib; var shapeLib = this.ShapeLib; // call library functions using references var names = stencilLib.getStencilNames() aGraphic = shapeLib.shapeWithContentsOfArray(names) // select the created graphic document.windows[0].selection.view.select([aGraphic]) }); // validation function determines if menu item is enabled action.validate = function(selection, sender){ return true }; return action; })();

Loading a library is accomplished by simply appending the library identifier (filename) to the global object: this. In the example shown above two libraries are loaded into variables (line 4, 5).

Once the libraries have been loaded, any library functions can be called by appending the name of the function to the variable representing the host library (lines 7, 8).

Calling an Omni Automation Library within a Script

Calling into libraries within an external script is a slightly more involved process in that you must first identify the plugin that contains the library whose functions you wish to access.

In the example below, the plugin is located using the find method on the Plugin class, and the plugin’s identifier. If the plugin is installed, then an object reference to the plugin is returned and stored in a variable (line 2). If the plugin is not installed, a value of null will be returned and the next line in the script (line 3) will throw an error indicating its status as missing.

Once the plugin has been successfully identified, any libraries can be loaded using the library method on the plugin reference stored previously in a variable (lines 5, 7).

Once the library or libraries have been loaded, their functions can be accessed by appending the function name to the variable containing the loaded library (lines 9, 11).

// locate the plugin by identifier var aPlugin = PlugIn.find("com.YourIdentifier.NameOfApp") if (aPlugin == null){throw new Error("Plugin is not installed.")} // load a library identified by name var firstLibrary = aPlugin.library("NameOfFirstLibrary") // load a library identified by name var secondLibrary = aPlugin.library("NameOfSecondLibrary") // call a library function by name var aVariable = firstLibrary.nameOfLibraryFunction() // call a library function by name secondLibrary.nameOfLibraryFunction(aVariable)

Example Library

As an example, here’s a library for working with OmniGraffle stencils (see below). It contains the following functions that can be called from within console scripts and web and Omni object-links:

NOTE: notice how library functions can call other functions in the library by preceding the function title with the “this” keyword: this.getStencilNames()

(() => { var StencilLib = new PlugIn.Library(new Version("1.0")); StencilLib.getLibraryFunctions = function(){ functionTitles = ["getLibraryFunctions()","getStencilNames()","isStencilInstalled(stencilName)","getStencilObject(stencilName)","countOfGraphicsInStencil(stencilName)","getGraphicNamesInStencil(stencilName)","isGraphicInStencil(graphicName,stencilName)","importGraphicFromStencil(graphicName,stencilName)","importAllGraphicsFromStencil(stencilName)"]; return "###### StencilLib Functions #####\n\t--> " + functionTitles.join("\n\t--> "); //methods = Object.getOwnPropertyNames(this); //console.log(methods); }; StencilLib.getStencilNames = function(){ return app.stencils.map(function(aStencil){return aStencil.name}); }; StencilLib.isStencilInstalled = function(stencilName){ stencilNames = this.getStencilNames(); var status = false; stencilNames.forEach(function(aName){ if (aName.localeCompare(stencilName) === 0){status = true}; }); return status; }; StencilLib.getStencilObject = function(stencilName){ if(this.isStencilInstalled(stencilName) === false){ displayErrorMessage("There is no stencil named: " + stencilName) } for(i = 0; i < app.stencils.length; i++){ if (app.stencils[i].name.localeCompare(stencilName) === 0){ return app.stencils[i] } } } StencilLib.countOfGraphicsInStencil = function(stencilName){ var targetStencil = this.getStencilObject(stencilName); var graphicCount; targetStencil.load(function(stencilRef){ console.log("Loaded stencil: “" + stencilRef.name + "”"); graphicCount = stencilRef.graphics.length; return graphicCount; }); if (graphicCount){ return graphicCount; } else { displayErrorMessage("Problem loading stencil: " + stencilName); }; }; StencilLib.getGraphicNamesInStencil = function(stencilName){ var targetStencil = this.getStencilObject(stencilName); var graphicNames; targetStencil.load(function(stencilRef){ console.log("Loaded stencil: “" + stencilRef.name + "”"); graphicNames = stencilRef.graphics.map(function(aGraphic){ return aGraphic.name; }); return graphicNames; }); if (graphicNames){ return graphicNames; } else { displayErrorMessage("Problem reading graphic names from stencil: " + stencilName); }; }; StencilLib.isGraphicInStencil = function(graphicName, stencilName){ var targetStencil = this.getStencilObject(stencilName); var graphicNames; targetStencil.load(function(stencilRef){ console.log("Loaded stencil: “" + stencilRef.name + "”"); graphicNames = stencilRef.graphics.map(function(aGraphic){ return aGraphic.name; }); return graphicNames; }); if (graphicNames){ queryResult = (graphicNames.includes(graphicName)) ? true : false; return queryResult } else { displayErrorMessage("Problem reading graphic names from stencil: " + stencilName); }; }; StencilLib.importGraphicFromStencil = function(graphicName,stencilName){ var targetStencil = this.getStencilObject(stencilName); targetStencil.load(function(s){ console.log("Loaded stencil: “" + s.name + "”") for(q = 0; q < s.graphics.length; q++){ aGraphicName = s.graphics[q].name if (aGraphicName != null){ if (aGraphicName.localeCompare(graphicName) == 0){ console.log("found graphic: “" + graphicName + "”") aStencilGraphic = s.graphics[q] cnvs = document.windows[0].selection.canvas rsltArray = cnvs.duplicate([aStencilGraphic]) aGraphic = rsltArray[0] aGraphic.geometry = new Rect(0, 0, aGraphic.geometry.width, aGraphic.geometry.height) document.windows[0].selection.view.select([aGraphic]) console.log("added graphic: “" + graphicName + "”") return aGraphic } } } displayErrorMessage("Graphic “" + graphicName + "” is not in stencil “" + stencilName + "”") }); }; StencilLib.importAllGraphicsFromStencil = function(stencilName){ var targetStencil = this.getStencilObject(stencilName); targetStencil.load(function(s){ console.log("Loaded stencil: “" + s.name + "”"); document.windows[0].selection.canvas.duplicate(s.graphics); }); }; return StencilLib; })(); function displayErrorMessage(errorString){ new Alert('ERROR', errorString).show(function(result){}) throw new Error(errorString) }
(() => { var StencilLib = new PlugIn.Library(new Version("1.0")); // returns an array of the names of the installed stencils StencilLib.getStencilNames = function() { stencilNames = new Array() app.stencils.forEach(function(aStencil){ stencilNames.push(aStencil.name) }) return stencilNames } // imports a specified graphic from the specified stencil to the canvas StencilLib.importNamedGraphicFromNamedStencil = function(graphicName, stencilName){ for(i = 0; i < app.stencils.length; i++) { let stencil = app.stencils[i]; if (stencil.name.localeCompare(stencilName) == 0) { stencil.load(function(s) { console.log("Loaded stencil: “" + s.name + "”") for(q = 0; q < s.graphics.length; q++){ aGraphicName = s.graphics[q].name if (aGraphicName != null){ if (aGraphicName.localeCompare(graphicName) == 0){ console.log("found graphic: “" + graphicName + "”") aStencilGraphic = s.graphics[q] rsltArray = canvases[0].duplicate([aStencilGraphic]) aGraphic = rsltArray[0] aGraphic.geometry = new Rect(0, 0, aGraphic.geometry.width, aGraphic.geometry.height) document.windows[0].selection.view.select([aGraphic]) console.log("added graphic: “" + graphicName + "”") return aGraphic } } } throw new Error("Graphic “" + graphicName + "” is not in stencil “" + stencilName + "”") }) } } } // imports all graphics from the specified stencil to the canvas StencilLib.importAllGraphicsFromNamedStencil = function(stencilName){ for(i = 0; i < app.stencils.length; i++) { let stencil = app.stencils[i] if (stencil.name.localeCompare(stencilName) == 0) { stencil.load(function(s) { console.log("Loaded stencil: “" + s.name + "”") canvases[0].duplicate(s.graphics) return true }) } } } // returns the names of all graphics from the specified stencil StencilLib.getGraphicNamesForNamedStencil = function(stencilName){ var graphicNames = new Array() for(i = 0; i < app.stencils.length; i++) { let stencil = app.stencils[i] if (stencil.name.localeCompare(stencilName) == 0) { stencil.load(function(s) { console.log("Loaded stencil: “" + s.name + "”") stencil.graphics.forEach(function(aGraphic){ graphicNames.push(aGraphic.name) }) return graphicNames }) } } return graphicNames } return StencilLib; })();

Here’s an example of calling the previous example Stencil Library:

aPlugin = PlugIn.find("com.NyhthawkProductions.OmniGraffle") stencilLib = aPlugin.library("StencilLib") stencilLib.importGraphicFromStencil('Tablet Mac','Google Material Design Icons')
import-from-stencil

Library Plug-In

As with Plug-In actions, libraries can be created in a simple single-file Plug-In format that are installed in the application’s Plug-Ins folder. As with bundled Plug-In libraries, they are called from an action using their filename (without extension).

NOTE: An important difference between libraries that are elements of a Plug-In bundle, and those libraries that are single-file plug-ins, is the inclusion of identifying metadata at the top of the the single-file library (lines 2-7 below):

// COPY & PASTE into editor app. EDIT & SAVE with “.omnijs” file extension. /*{ "type": "library", "targets": ["omnigraffle","omnioutliner"], "identifier": "com.youOrCompany.libraryName", "version": "1.0" }*/ (() => { var myLibrary = new PlugIn.Library(new Version("1.0")); myLibrary.myFirstFunction = function(passedParameter){ // function code return myFirstFunctionResult } myLibrary.mySecondFunction = function(passedParameter){ // function code return mySecondFunctionResult } return myLibrary; })();

Here's an example library for OmniFocus:

/*{ "type": "library", "targets": ["omnifocus"], "identifier": "com.omni-automation.of.exampleLibrary", "author": "Otto Automator", "version": "1.0", "description": "An example library for OmniFocus" }*/ (() => { var lib = new PlugIn.Library(new Version("1.0")); lib.countOfInbox = function(){ return inbox.length } lib.toggleFlagsForTasks = function(tasks){ tasks.forEach(task => { task.flagged = !task.flagged }) } return lib; })(); /* How to call from scripts // NOTE: the following assumes the library was saved as: "omnifocus-lib.omnijs" var libFile = PlugIn.find("com.omni-automation.of.exampleLibrary") var lib = libFile.library("omnifocus-lib") lib.countOfInbox() */

Here's how to call functions in the example library for OmniFocus. NOTE: the following script assumes the library was saved as: "omnifocus-lib.omnijs"

// NOTE: the following assumes the library was saved as: "omnifocus-lib.omnijs" var libFile = PlugIn.find("com.omni-automation.of.exampleLibrary") var lib = libFile.library("omnifocus-lib") lib.countOfInbox()

Example: Stencil Library

Here’s an example solitary library file for working with stencils in OmniGraffle:

/*{ "type": "library", "targets": ["omnigraffle"], "identifier": "com.omni-automation.libraries.StencilLib", "version": "1.0" }*/ (() => { var StencilLib = new PlugIn.Library(new Version("1.0")); StencilLib.getLibraryFunctions = function(){ functionTitles = ["getLibraryFunctions()","getStencilNames()","isStencilInstalled(stencilName)","getStencilObject(stencilName)","countOfGraphicsInStencil(stencilName)","getGraphicNamesInStencil(stencilName)","isGraphicInStencil(graphicName,stencilName)","importGraphicFromStencil(graphicName,stencilName)","importAllGraphicsFromStencil(stencilName)"]; return "###### StencilLib Functions #####\n\t--> " + functionTitles.join("\n\t--> "); //methods = Object.getOwnPropertyNames(this); //console.log(methods); }; StencilLib.getStencilNames = function(){ return app.stencils.map(function(aStencil){return aStencil.name}); }; StencilLib.isStencilInstalled = function(stencilName){ stencilNames = this.getStencilNames(); var status = false; stencilNames.forEach(function(aName){ if (aName.localeCompare(stencilName) === 0){status = true}; }); return status; }; StencilLib.getStencilObject = function(stencilName){ if(this.isStencilInstalled(stencilName) === false){ displayErrorMessage("There is no stencil named: " + stencilName) } for(i = 0; i < app.stencils.length; i++){ if (app.stencils[i].name.localeCompare(stencilName) === 0){ return app.stencils[i] } } } StencilLib.countOfGraphicsInStencil = function(stencilName){ var targetStencil = this.getStencilObject(stencilName); var graphicCount; targetStencil.load(function(stencilRef){ console.log("Loaded stencil: “" + stencilRef.name + "”"); graphicCount = stencilRef.graphics.length; return graphicCount; }); if (graphicCount){ return graphicCount; } else { displayErrorMessage("Problem loading stencil: " + stencilName); }; }; StencilLib.getGraphicNamesInStencil = function(stencilName){ var targetStencil = this.getStencilObject(stencilName); var graphicNames; targetStencil.load(function(stencilRef){ console.log("Loaded stencil: “" + stencilRef.name + "”"); graphicNames = stencilRef.graphics.map(function(aGraphic){ return aGraphic.name; }); return graphicNames; }); if (graphicNames){ return graphicNames; } else { displayErrorMessage("Problem reading graphic names from stencil: " + stencilName); }; }; StencilLib.isGraphicInStencil = function(graphicName, stencilName){ var targetStencil = this.getStencilObject(stencilName); var graphicNames; targetStencil.load(function(stencilRef){ console.log("Loaded stencil: “" + stencilRef.name + "”"); graphicNames = stencilRef.graphics.map(function(aGraphic){ return aGraphic.name; }); return graphicNames; }); if (graphicNames){ queryResult = (graphicNames.includes(graphicName)) ? true : false; return queryResult } else { displayErrorMessage("Problem reading graphic names from stencil: " + stencilName); }; }; StencilLib.importGraphicFromStencil = function(graphicName,stencilName){ var targetStencil = this.getStencilObject(stencilName); targetStencil.load(function(s){ console.log("Loaded stencil: “" + s.name + "”") for(q = 0; q < s.graphics.length; q++){ aGraphicName = s.graphics[q].name if (aGraphicName != null){ if (aGraphicName.localeCompare(graphicName) == 0){ console.log("found graphic: “" + graphicName + "”") aStencilGraphic = s.graphics[q] cnvs = document.windows[0].selection.canvas rsltArray = cnvs.duplicate([aStencilGraphic]) aGraphic = rsltArray[0] aGraphic.geometry = new Rect(0, 0, aGraphic.geometry.width, aGraphic.geometry.height) document.windows[0].selection.view.select([aGraphic]) console.log("added graphic: “" + graphicName + "”") return aGraphic } } } displayErrorMessage("Graphic “" + graphicName + "” is not in stencil “" + stencilName + "”") }); }; StencilLib.importAllGraphicsFromStencil = function(stencilName){ var targetStencil = this.getStencilObject(stencilName); targetStencil.load(function(s){ console.log("Loaded stencil: “" + s.name + "”"); document.windows[0].selection.canvas.duplicate(s.graphics); }); }; return StencilLib; })(); function displayErrorMessage(errorString){ new Alert('ERROR', errorString).show(function(result){}) throw new Error(errorString) }

To access the library functions, use the value of the library plugin identifier attribute to locate the plugin, and then use the library name (in this case “StencilLib”) to store a reference to the library. The library functions can be called on the stored reference, like this example that calls the library function for returning a list of the functions it contains:

aPlugin = PlugIn.find("com.omni-automation.libraries.StencilLib") stencilLib = aPlugin.library("StencilLib") stencilLib.getLibraryFunctions()

The “name” used as the parameter the library loading call is the “file name” (without name extension) of the library, whether it is a library within a plug-in bundle, or a solitary library file.

library-call-console

DISCLAIMER