Using Javascript in pre-Yosemite 'Execute' actions

Javascript has a richer library (and more flexible records) than Applescript, but the JXA JavaScript for Applications version is only available from OS X Yosemite onwards.

Code that doesn’t automate applications can, nevertheless run on earlier versions of OS X, and may be useful for things like encodeURI() and decodeURI() etc;

The following example should, I think, work both on Yosemite and on previous versions:

#!/bin/bash

function runJS () {
	local OSX_VER=$(sw_vers -productVersion | awk -F '.' '{print $2}')
	if [ "$OSX_VER" -lt 10 ]; then
		echo "$(
			/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc -e "print($1)"
		)"
	else
		echo "$(osascript -l JavaScript -e "$1")"
	fi
}

ENCODED_URI=$(runJS "'file://' + encodeURI('/Users/houthakker/Desktop/Some file that we want to print.txt')")
printf "Encoded:\n\n"
echo "$ENCODED_URI"

DECODED_URI=$(runJS "decodeURI('$ENCODED_URI').slice(7)")
printf "\n\nand then unencoded:\n\n"
printf "\t$DECODED_URI"

1 Like

osascript -l JavaScript does not appear to work on 10.8. Maybe 10.9? But I thought it only came in for 10.10.

That’s right – JXA only comes in with 10, but if [ "$OSX_VER" -lt 10 ]; then although you can’t use Javascript as an osascript language, you can still submit generic .js code to JSC, as in

echo "$(/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc -e "print($1)")

above, where $1 can be any generic Javascript code ( like the calls to decodeURI() etc) but doesn’t offer any of the JXA automation objects.

( Note that you have to wrap any return value in print() when you use JSC )

( For example, one could use calls to JSC to create an (OS X version independent) custom action for URL encoding and decoding )

1 Like

An example might be this variation on the roman numeral theme in the other thread. It uses JSC Javascript, and should I think, work in earlier versions of OS X, as well as in Yosemite.

Roman Integers (preserving separators).kmmacros (3.7 KB)

// SEQUENCE OF SEPARATED INTEGER STRINGS -->
// ROMANISED REWRITE (PRESERVING SEPARATOR)
(function (strIntegers) {
    'use strict';
    // DICTIONARY OF GLYPH:VALUE MAPPINGS
    var dctGlyphs = {
        M: 1000,
        CM: 900,
        D: 500,
        CD: 400,
        C: 100,
        XC: 90,
        L: 50,
        XL: 40,
        X: 10,
        IX: 9,
        V: 5,
        IV: 4,
        I: 1
    };

    // LIST OF INTEGER STRINGS, WITH ANY SEPARATOR
    var strNums = typeof strIntegers === 'string' ?
            strIntegers :
            strIntegers.toString(),
        lstParts = strNums.split(/\d+/),
        strSeparator = lstParts.length > 1 ? lstParts[1] : '',
        lstDecimal = strSeparator ?
            strIntegers.split(strSeparator) :
            [strNums];


    // REWRITE OF DECIMAL INTEGER AS ROMAN
    function roman(strN) {
        var s = '',
            n = Number(strN),
            v;

    /*  Starting with the highest-valued glyph:
        take as many bites as we can with it
        (decrementing residual value with each bite,
        and appending a corresponding glyph copy to the string)
        before moving down to the next most expensive glyph */
    
        for (var g in dctGlyphs)
            for (v = dctGlyphs[g]; n >= v;)
                s += g, n -= v;

        return s;
    }

    // ALL REWRITTEN, WITH SEPARATOR RESTORED
    return lstDecimal.map(roman).join(strSeparator);
    
})("2015-06-29") // --> "MMXV-VI-XXIX"
1 Like

Thank you ComplexPoint,

Haven't used JSC Javascript before. Looks very interesting.
Looks easer the applescript in the terms of wording...
Ill be hacking JSC Javascript too... :stuck_out_tongue_winking_eye:

1 Like

Do I need to install JSON Helper on OX X Yosemite?

No need for any installations:

/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc

Is a built-in part of OS X.

It won't, however, be on your shell path by default. You could put it there, but I tend to simply paste the whole path.

You need to follow the jsc command (in a KM Execute Shell Script action), with the -e switch and the quoted text of your .js code, wrapping the return value in the print() function.

One reference might be:

Full example:

/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc -e "
print(
// SEQUENCE OF SEPARATED INTEGER STRINGS -->
// ROMANISED REWRITE (PRESERVING SEPARATOR)
(function (strIntegers) {
	'use strict';
	// DICTIONARY OF GLYPH:VALUE MAPPINGS
	var dctGlyphs = {
		M: 1000,
		CM: 900,
		D: 500,
		CD: 400,
		C: 100,
		XC: 90,
		L: 50,
		XL: 40,
		X: 10,
		IX: 9,
		V: 5,
		IV: 4,
		I: 1
	};

	// LIST OF INTEGER STRINGS, WITH ANY SEPARATOR
	var strNums = typeof strIntegers === 'string' ? strIntegers : strIntegers.toString(),
		lstParts = strNums.split(/\d+/),
		strSeparator = lstParts.length > 1 ? lstParts[1] : '',
		lstDecimal = strSeparator ? strIntegers.split(strSeparator) : [strNums];


	// REWRITE OF DECIMAL INTEGER AS ROMAN
	function roman(strN) {
		var s = '',
			n = Number(strN),
			v;

	/*	Starting with the highest-valued glyph:
		take as many bites as we can with it
		(decrementing residual value with each bite,
		and appending a corresponding glyph copy to the string)
		before moving down to the next most expensive glyph */
	
		for (var g in dctGlyphs)
			for (v = dctGlyphs[g]; n >= v;)
				s += g, n -= v;

		return s;
	}

	// ALL REWRITTEN, WITH SEPARATOR RESTORED
	return lstDecimal.map(roman).join(strSeparator);
	
})(\"$KMVAR_Decimal_integers\"))"
1 Like