1/14/2016 - 7:08 AM

Persistent 'properties' for OS X JavaScript for Applications (JXA) Forked from Rob Trew

Persistent 'properties' for OS X JavaScript for Applications (JXA) Forked from Rob Trew

(function () {
    'use strict';
    // OSX JavaScript for Applications lacks the persistent 'properties'
    // of AppleScript (in which global variables and properties persist between script runs)
    // but we can, of course, serialise to JSON or plist at the end of a script
    // parsing it at the start of the next run.
    // Here is one approach to persistence between script runs 
    // using JSON.stringify() and JSON.parse()
    // 1. When you create a new Properties object it reads any JSON file from the last run
    //    (looks for .json file in same folder - and with same name - as the .scpt file)
    // 2. You can supply initial defaults to fill any gaps in the JSON file
    // 3. A write() method, for use at the end of the script, serialises the new 'Properties' state

    var Properties = function (dctDefaults) {
        // read any json in a file that shares the path 
        // (except .json extension) of this script
        var strPath = Application.currentApplication()
            .documents[0].path().split(".")[0] + ".json",
            json = $.NSString.stringWithContentsOfFile(strPath).js || "";

        return {
            // fill any gaps (using dctDefaults) in the json settings
            keys: function (dctA, dctB) {
                for (var key in dctB) {
                    if (!(key in dctA)) dctA[key] = dctB[key];
                return dctA;
            }(json && JSON.parse(json) || {}, dctDefaults),

            // update the json, probably best used at end of script
            write: function () {
                ).writeToFileAtomically(strPath, true);


    // Get a new properties object, specifying any defaults
    // Any json file for this script will be read first, and the defaults
    // will only be used to fill any gaps
    var ps = new Properties({
            perfume: 'cinnamon',
            year: 2017,
            alphabet: [
                'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta',
                'theta', 'iota', 'kappa', 'lambda', 'mu'
        // use p as a brief name for the properties dictionary itself
        p = ps.keys;

    // List the existing properties and their current values
    console.log(Object.keys(p).map(function (k) {
        return k + '=' + p[k];


    // ...
        // update existing property values
        // or create new properties
        p.perfume = "coffee VANILLA";
        p.year = new Date();
        p.festivals = ['spring festival', 'qingming', 'zhongqiu'];
        p.alphabet = 'hay bee sea'.split(/\s+/);
    // ...

    // make the properties and their values available for the next run of this script
    // Copy a formatted JSON version of property state to the clipboard
    var a = Application.currentApplication(),
    sa = (a.includeStandardAdditions = true, a); 

    var strClip = JSON.stringify(p, null, 2);

    return strClip