OmniJS .tellScript() examples - quoting code - an alternative?

Given the slight complexity of quoting and escaping code text, which you draw attention to at:

https://omni-automation.com/shared/url-app-to-app.html

in the notice which reads:

IMPORTANT: note the inclusion of both the trailing semi-colon AND backslash character () together at the end of each of the lines of calling script code. As the script code text is combined to a single-line, these characters ensure that the script code is read correctly as it is converted from text to code during the script execution.

I wonder whether you think that users might find it easier to skip quoting and escaping, and make use of the fact that the .toString() method works on JavaScript functions ?

e.g. perhaps some variant on the theme of:

function ooPart() {
    return rootItem.children
        .map(function (x) {
            return x.topic;
        });
}

var scriptURL = URL.tellScript('OmniOutliner', ooPart.toString() + '()');

(Where the final ‘()’ creates the function invocation - though I guess one could perhaps adjust the implementation of tellScript to append that itself, and simply expect a function string ?)

1 Like

Maybe URL.tellScript() could notice when the type of its argument is a function rather than a string, and automatically encode that argument with .toString() + ‘()’? I’ll suggest that to @greg.

(And I’ll make sure @Sal knows about this thread, since he’s writing most of our examples.)

1 Like

Good idea! To make the function.toString() concept work with the encoded OmniJS URL, the function call has to be appended to the function string when it gets passed-into the tellScript() function: ‘\nfunctionName()’

Note the new line character and then the full function call. I’ll update the documentation. Thanks! – SAL

function getTopics(){
	var titles = new Array()
	var items = rootItem.children
	for (i = 0; i < items.length; i++) {
		titles[i] = items[i].topic
	}
	return titles
}
var scriptURL = URL.tellScript("omnioutliner", getTopics.toString() + '\ngetTopics()')
scriptURL.call(function(reply){
	var aCanvas = document.windows[0].selection.canvas
	var aCanvasSize = aCanvas.size
	var cW = aCanvasSize.width
	var cH = aCanvasSize.height
	var typeSize = Math.round(cH/cW*100)
	for (i = 0; i < reply.length; i++){
		replyItem = reply[i]
		if(i != 0){
			aCanvas = addCanvas()
			aCanvas.size = aCanvasSize
		}
		aCanvas.name = replyItem
		aShape = aCanvas.newShape()
		aShape.text = replyItem
		aShape.textSize = typeSize
		aShape.autosizing = TextAutosizing.Full
		aShape.textHorizontalAlignment = HorizontalTextAlignment.Center
		aShape.shadowColor = null
		aShape.strokeType = null
		aShape.strokeColor = null
		aShapeGeo = aShape.geometry
		aRect = new Rect(cW/2 - aShapeGeo.width/2, typeSize, aShapeGeo.width, aShapeGeo.height)
		aShape.geometry = aRect
		aShape.text = "<%Canvas%>"
	}
})
1 Like

Thanks !

JS does, of course, have a pattern for combined definition and invocation of named or anonymous functions - (the ‘module’ pattern):

(function (n) {
    'use strict';

   return (function fnName(x) {
        return Math.pow(2, x);
    })(n)

})(8);

// -> 256

or even just:

(function () {
    'use strict';


	// Immediate definition and invocation by appending '()'
   
   	return function namedFn() {
	
		return 512;
		
	}()
      

})();

// -> 512

But perhaps it would, as Ken suggests, make sense to consider adjusting the implementation of .tellScript() ?

Arguments, for example, can already be passed in by assembling strings like those above, but perhaps it might also be worth considering easing the argument-passing mechanics for users with some pattern analogous to that used in the TaskPaper JS Context’s JXA interface ?

TaskPaper’s .evaluate() function, which passes JS code from the outside into a JS Context, expects:

  1. A function string, generated by .toString()
  2. A dictionary (could be stringified with JSON.stringify()), with a name like options, which is passed to the function as an argument for the JS Context run-time, allowing the user to provide some key-value pairs – available as name-bound values inside the function.

e.g.

(() => {
    'use strict';

    // TASKPAPER CONTEXT
    const TaskPaperContext = (editor, options) => {
        // show :: a -> String
        const show = x => JSON.stringify(x, null, 2);

        return show(["The messages passed in with the options dictionary were",
            options.firstMsg,
            'and',
            options.secondMsg
        ])
    };

    // JAVASCRIPT FOR AUTOMATION CONTEXT
    const tp3 = Application('TaskPaper'),
        ds = tp3.documents;

    const varResult = ds.length ? ds[0].evaluate({
        script: TaskPaperContext.toString(),
        withOptions: {
            firstMsg: "JS Contexts execute fast",
            secondMsg: "JS Contexts are also available on iOS"
        }
    }) : undefined;

    let a = Application.currentApplication(),
        sa = (a.includeStandardAdditions = true, a);

    sa.setTheClipboardTo(varResult);
    return varResult;
})();

Which returns:

[
  "The messages passed in with the options dictionary were",
  "JS Contexts execute fast",
  "and",
  "JS Contexts are also available on iOS"
]