Skip to content

Instantly share code, notes, and snippets.

@RobTrew
Last active January 30, 2024 09:07
Show Gist options
  • Save RobTrew/6bc1fcc997844faec3cf to your computer and use it in GitHub Desktop.
Save RobTrew/6bc1fcc997844faec3cf to your computer and use it in GitHub Desktop.
Persistent 'properties' for OS X JavaScript for Applications (JXA)
(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 () {
$.NSString.alloc.initWithUTF8String(
JSON.stringify(this.keys)
).writeToFileAtomically(strPath, true);
}
};
};
// EXAMPLE OF USE:
// 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];
}).join('\n'));
// MAIN SCRIPT STARTS
// ...
// 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+/);
// ...
// MAIN SCRIPT ENDS
// make the properties and their values available for the next run of this script
ps.write();
// 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);
sa.setTheClipboardTo(strClip);
return strClip
})();
@RobTrew
Copy link
Author

RobTrew commented Jun 7, 2021

var strPath = Application.currentApplication()
.documents[0].path().split(".")[0] + ".json"

Derived, in other words, from the file path of the front document in an application which exposes a documents collection to the osascript interface.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment