Skip to content

Instantly share code, notes, and snippets.

@Trevor-
Created July 9, 2019 05:48
Show Gist options
  • Save Trevor-/3786f6a45ca0e2d0dc3beded82ed8a18 to your computer and use it in GitHub Desktop.
Save Trevor-/3786f6a45ca0e2d0dc3beded82ed8a18 to your computer and use it in GitHub Desktop.
JSX.js For execution of JSX Extended Code from your CEP js files
/*
_ ______ __ _
| / ___\ \/ / (_)___
_ | \___ \\ / | / __|
| |_| |___) / \ _ | \__ \
\___/|____/_/\_(_)/ |___/
|__/
_ ____
/\ /\___ _ __ ___(_) ___ _ __ |___ \
\ \ / / _ \ '__/ __| |/ _ \| '_ \ __) |
\ V / __/ | \__ \ | (_) | | | | / __/
\_/ \___|_| |___/_|\___/|_| |_| |_____|
*/
//////////////////////////////////////////////////////////////////////////////////
// JSX.js © and writtent by Trevor https://creative-scripts.com/jsx-js //
// If you turn over is less the $50,000,000 then you don't have to pay anything //
// License MIT, don't complain, don't sue NO MATTER WHAT //
// If you turn over is more the $50,000,000 then you DO have to pay //
// Contact me https://creative-scripts.com/contact for pricing and licensing //
// Don't remove these commented lines //
// For simple and effective calling of jsx from the js engine //
// Version 2 last modified April 18 2018 //
//////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// Change log: //
// JSX.js V2 is now independent of NodeJS and CSInterface.js <span class="wp-font-emots-emo-happy"></span> //
// forceEval is now by default true //
// It wraps the scripts in a try catch and an eval providing useful error handling //
// One can set in the jsx engine $.includeStack = true to return the call stack in the event of an error //
///////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// JSX.js for calling jsx code from the js engine //
// 2 methods included //
// 1) jsx.evalScript AKA jsx.eval //
// 2) jsx.evalFile AKA jsx.file //
// Special features //
// 1) Allows all changes in your jsx code to be reloaded into your extension at the click of a button //
// 2) Can enable the $.fileName property to work and provides a $.__fileName() method as an alternative //
// 3) Can force a callBack result from InDesign //
// 4) No more csInterface.evalScript('alert("hello "' + title + " " + name + '");') //
// use jsx.evalScript('alert("hello __title__ __name__");', {title: title, name: name}); //
// 5) execute jsx files from your jsx folder like this jsx.evalFile('myFabJsxScript.jsx'); //
// or from a relative path jsx.evalFile('../myFabScripts/myFabJsxScript.jsx'); //
// or from an absolute url jsx.evalFile('/Path/to/my/FabJsxScript.jsx'); (mac) //
// or from an absolute url jsx.evalFile('C:Path/to/my/FabJsxScript.jsx'); (windows) //
// 6) Parameter can be entered in the from of a parameter list which can be in any order or as an object //
// 7) Not camelCase sensitive (very useful for the illiterate) //
// <span class="wp-font-emots-emo-sunglasses"></span> Dead easy to use BUT SPEND THE 3 TO 5 MINUTES IT SHOULD TAKE TO READ THE INSTRUCTIONS //
///////////////////////////////////////////////////////////////////////////////////////////////////////////
/* jshint undef:true, unused:true, esversion:6 */
//////////////////////////////////////
// jsx is the interface for the API //
//////////////////////////////////////
var jsx;
// Wrap everything in an anonymous function to prevent leeks
(function() {
/////////////////////////////////////////////////////////////////////
// Substitute some CSInterface functions to avoid dependency on it //
/////////////////////////////////////////////////////////////////////
var __dirname = (function() {
var path, isMac;
path = decodeURI(window.__adobe_cep__.getSystemPath('extension'));
isMac = navigator.platform[0] === 'M'; // [M]ac
path = path.replace('file://' + (isMac ? '' : '/'), '');
return path;
})();
var evalScript = function(script, callback) {
callback = callback || function() {};
window.__adobe_cep__.evalScript(script, callback);
};
////////////////////////////////////////////
// In place of using the node path module //
////////////////////////////////////////////
// jshint undef: true, unused: true
// A very minified version of the NodeJs Path module!!
// For use outside of NodeJs
// Majorly nicked by Trevor from Joyent
var path = (function() {
var isString = function(arg) {
return typeof arg === 'string';
};
// var isObject = function(arg) {
// return typeof arg === 'object' && arg !== null;
// };
var basename = function(path) {
if (!isString(path)) {
throw new TypeError('Argument to path.basename must be a string');
}
var bits = path.split(/[\/\\]/g);
return bits[bits.length - 1];
};
// jshint undef: true
// Regex to split a windows path into three parts: [*, device, slash,
// tail] windows-only
var splitDeviceRe =
/^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/;
// Regex to split the tail part of the above into [*, dir, basename, ext]
// var splitTailRe =
// /^([\s\S]*?)((?:\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))(?:[\\\/]*)$/;
var win32 = {};
// Function to split a filename into [root, dir, basename, ext]
// var win32SplitPath = function(filename) {
// // Separate device+slash from tail
// var result = splitDeviceRe.exec(filename),
// device = (result[1] || '') + (result[2] || ''),
// tail = result[3] || '';
// // Split the tail into dir, basename and extension
// var result2 = splitTailRe.exec(tail),
// dir = result2[1],
// basename = result2[2],
// ext = result2[3];
// return [device, dir, basename, ext];
// };
var win32StatPath = function(path) {
var result = splitDeviceRe.exec(path),
device = result[1] || '',
isUnc = !!device && device[1] !== ':';
return {
device: device,
isUnc: isUnc,
isAbsolute: isUnc || !!result[2], // UNC paths are always absolute
tail: result[3]
};
};
var normalizeUNCRoot = function(device) {
return '\\\\' + device.replace(/^[\\\/]+/, '').replace(/[\\\/]+/g, '\\');
};
var normalizeArray = function(parts, allowAboveRoot) {
var res = [];
for (var i = 0; i < parts.length; i++) {
var p = parts[i];
// ignore empty parts
if (!p || p === '.')
continue;
if (p === '..') {
if (res.length && res[res.length - 1] !== '..') {
res.pop();
} else if (allowAboveRoot) {
res.push('..');
}
} else {
res.push(p);
}
}
return res;
};
win32.normalize = function(path) {
var result = win32StatPath(path),
device = result.device,
isUnc = result.isUnc,
isAbsolute = result.isAbsolute,
tail = result.tail,
trailingSlash = /[\\\/]$/.test(tail);
// Normalize the tail path
tail = normalizeArray(tail.split(/[\\\/]+/), !isAbsolute).join('\\');
if (!tail && !isAbsolute) {
tail = '.';
}
if (tail && trailingSlash) {
tail += '\\';
}
// Convert slashes to backslashes when `device` points to an UNC root.
// Also squash multiple slashes into a single one where appropriate.
if (isUnc) {
device = normalizeUNCRoot(device);
}
return device + (isAbsolute ? '\\' : '') + tail;
};
win32.join = function() {
var paths = [];
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
if (!isString(arg)) {
throw new TypeError('Arguments to path.join must be strings');
}
if (arg) {
paths.push(arg);
}
}
var joined = paths.join('\\');
// Make sure that the joined path doesn't start with two slashes, because
// normalize() will mistake it for an UNC path then.
//
// This step is skipped when it is very clear that the user actually
// intended to point at an UNC path. This is assumed when the first
// non-empty string arguments starts with exactly two slashes followed by
// at least one more non-slash character.
//
// Note that for normalize() to treat a path as an UNC path it needs to
// have at least 2 components, so we don't filter for that here.
// This means that the user can use join to construct UNC paths from
// a server name and a share name; for example:
// path.join('//server', 'share') -> '\\\\server\\share\')
if (!/^[\\\/]{2}[^\\\/]/.test(paths[0])) {
joined = joined.replace(/^[\\\/]{2,}/, '\\');
}
return win32.normalize(joined);
};
var posix = {};
// posix version
posix.join = function() {
var path = '';
for (var i = 0; i < arguments.length; i++) {
var segment = arguments[i];
if (!isString(segment)) {
throw new TypeError('Arguments to path.join must be strings');
}
if (segment) {
if (!path) {
path += segment;
} else {
path += '/' + segment;
}
}
}
return posix.normalize(path);
};
// path.normalize(path)
// posix version
posix.normalize = function(path) {
var isAbsolute = path.charAt(0) === '/',
trailingSlash = path && path[path.length - 1] === '/';
// Normalize the path
path = normalizeArray(path.split('/'), !isAbsolute).join('/');
if (!path && !isAbsolute) {
path = '.';
}
if (path && trailingSlash) {
path += '/';
}
return (isAbsolute ? '/' : '') + path;
};
win32.basename = posix.basename = basename;
this.win32 = win32;
this.posix = posix;
return (navigator.platform[0] === 'M') ? posix : win32;
})();
////////////////////////////////////////////////////////////////////////////////////////////////////////
// The is the "main" function which is to be prototyped //
// It run a small snippet in the jsx engine that //
// 1) Assigns $.__dirname with the value of the extensions __dirname base path //
// 2) Sets up a method $.__fileName() for retrieving from within the jsx script it's $.fileName value //
// more on that method later //
// At the end of the script the global declaration jsx = new Jsx(); has been made. //
// If you like you can remove that and include in your relevant functions //
// var jsx = new Jsx(); You would never call the Jsx function without the "new" declaration //
////////////////////////////////////////////////////////////////////////////////////////////////////////
var Jsx = function() {
var jsxScript;
// Setup jsx function to enable the jsx scripts to easily retrieve their file location
jsxScript = [
'$.level = 0;',
'if(!$.__fileNames){',
' $.__fileNames = {};',
' $.__dirname = "__dirname__";'.replace('__dirname__', __dirname),
' $.__fileName = function(name){',
' name = name || $.fileName;',
' return ($.__fileNames && $.__fileNames[name]) || $.fileName;',
' };',
'}'
].join('');
evalScript(jsxScript);
return this;
};
/**
* [evalScript] For calling jsx scripts from the js engine
*
* The jsx.evalScript method is used for calling jsx scripts directly from the js engine
* Allows for easy replacement i.e. variable insertions and for forcing eval.
* For convenience jsx.eval or jsx.script or jsx.evalscript can be used instead of calling jsx.evalScript
*
* @param {String} jsxScript
* The string that makes up the jsx script
* it can contain a simple template like syntax for replacements
* 'alert("__foo__");'
* the __foo__ will be replaced as per the replacements parameter
*
* @param {Function} callback
* The callback function you want the jsx script to trigger on completion
* The result of the jsx script is passed as the argument to that function
* The function can exist in some other file.
* Note that InDesign does not automatically pass the callBack as a string.
* Either write your InDesign in a way that it returns a sting the form of
* return 'this is my result surrounded by quotes'
* or use the force eval option
* [Optional DEFAULT no callBack]
*
* @param {Object} replacements
* The replacements to make on the jsx script
* given the following script (template)
* 'alert("__message__: " + __val__);'
* and we want to change the script to
* 'alert("I was born in the year: " + 1234);'
* we would pass the following object
* {"message": 'I was born in the year', "val": 1234}
* or if not using reserved words like do we can leave out the key quotes
* {message: 'I was born in the year', val: 1234}
* [Optional DEFAULT no replacements]
*
* @param {Bolean} forceEval
* If the script should be wrapped in an eval and try catch
* This will 1) provide useful error feedback if heaven forbid it is needed
* 2) The result will be a string which is required for callback results in InDesign
* [Optional DEFAULT true]
*
* Note 1) The order of the parameters is irrelevant
* Note 2) One can pass the arguments as an object if desired
* jsx.evalScript(myCallBackFunction, 'alert("__myMessage__");', true);
* is the same as
* jsx.evalScript({
* script: 'alert("__myMessage__");',
* replacements: {myMessage: 'Hi there'},
* callBack: myCallBackFunction,
* eval: true
* });
* note that either lower or camelCase key names are valid
* i.e. both callback or callBack will work
*
* The following keys are the same jsx || script || jsxScript || jsxscript || file
* The following keys are the same callBack || callback
* The following keys are the same replacements || replace
* The following keys are the same eval || forceEval || forceeval
* The following keys are the same forceEvalScript || forceevalscript || evalScript || evalscript;
*
* @return {Boolean} if the jsxScript was executed or not
*/
Jsx.prototype.evalScript = function() {
var arg, i, key, replaceThis, withThis, args, callback, forceEval, replacements, jsxScript, isBin;
//////////////////////////////////////////////////////////////////////////////////////
// sort out order which arguments into jsxScript, callback, replacements, forceEval //
//////////////////////////////////////////////////////////////////////////////////////
args = arguments;
// Detect if the parameters were passed as an object and if so allow for various keys
if (args.length === 1 && (arg = args[0]) instanceof Object) {
jsxScript = arg.jsxScript || arg.jsx || arg.script || arg.file || arg.jsxscript;
callback = arg.callBack || arg.callback;
replacements = arg.replacements || arg.replace;
forceEval = arg.eval || arg.forceEval || arg.forceeval;
} else {
for (i = 0; i < 4; i++) {
arg = args[i];
if (arg === undefined) {
continue;
}
if (arg.constructor === String) {
jsxScript = arg;
continue;
}
if (arg.constructor === Object) {
replacements = arg;
continue;
}
if (arg.constructor === Function) {
callback = arg;
continue;
}
if (arg === false) {
forceEval = false;
}
}
}
// If no script provide then not too much to do!
if (!jsxScript) {
return false;
}
// Have changed the forceEval default to be true as I prefer the error handling
if (forceEval !== false) {
forceEval = true;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// On Illustrator and other apps the result of the jsx script is automatically passed as a string //
// if you have a "script" containing the single number 1 and nothing else then the callBack will register as "1" //
// On InDesign that same script will provide a blank callBack //
// Let's say we have a callBack function var callBack = function(result){alert(result);} //
// On Ai your see the 1 in the alert //
// On ID your just see a blank alert //
// To see the 1 in the alert you need to convert the result to a string and then it will show //
// So if we rewrite out 1 byte script to '1' i.e. surround the 1 in quotes then the call back alert will show 1 //
// If the scripts planed one can make sure that the results always passed as a string (including errors) //
// otherwise one can wrap the script in an eval and then have the result passed as a string //
// I have not gone through all the apps but can say //
// for Ai you never need to set the forceEval to true //
// for ID you if you have not coded your script appropriately and your want to send a result to the callBack then set forceEval to true //
// I changed this that even on Illustrator it applies the try catch, Note the try catch will fail if $.level is set to 1 //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (forceEval) {
isBin = (jsxScript.substring(0, 10) === '@JSXBIN@ES') ? '' : '\n';
jsxScript = (
// "\n''') + '';} catch(e){(function(e){var n, a=[]; for (n in e){a.push(n + ': ' + e[n])}; return a.join('\n')})(e)}");
// "\n''') + '';} catch(e){e + (e.line ? ('\\nLine ' + (+e.line - 1)) : '')}");
[
"$.level = 0;",
"try{eval('''" + isBin, // need to add an extra line otherwise #targetengine doesn't work ;-]
jsxScript.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/"/g, '\\"') + "\n''') + '';",
"} catch (e) {",
" (function(e) {",
" var line, sourceLine, name, description, ErrorMessage, fileName, start, end, bug;",
" line = +e.line" + (isBin === '' ? ';' : ' - 1;'), // To take into account the extra line added
" fileName = File(e.fileName).fsName;",
" sourceLine = line && e.source.split(/[\\r\\n]/)[line];",
" name = e.name;",
" description = e.description;",
" ErrorMessage = name + ' ' + e.number + ': ' + description;",
" if (fileName.length && !(/[\\/\\\\]\\d+$/.test(fileName))) {",
" ErrorMessage += '\\nFile: ' + fileName;",
" line++;",
" }",
" if (line){",
" ErrorMessage += '\\nLine: ' + line +",
" '-> ' + ((sourceLine.length < 300) ? sourceLine : sourceLine.substring(0,300) + '...');",
" }",
" if (e.start) {ErrorMessage += '\\nBug: ' + e.source.substring(e.start - 1, e.end)}",
" if ($.includeStack) {ErrorMessage += '\\nStack:' + $.stack;}",
" return ErrorMessage;",
" })(e);",
"}"
].join('')
);
}
/////////////////////////////////////////////////////////////
// deal with the replacements //
// Note it's probably better to use ${template} `literals` //
/////////////////////////////////////////////////////////////
if (replacements) {
for (key in replacements) {
if (replacements.hasOwnProperty(key)) {
replaceThis = new RegExp('__' + key + '__', 'g');
withThis = replacements[key];
jsxScript = jsxScript.replace(replaceThis, withThis + '');
}
}
}
try {
evalScript(jsxScript, callback);
return true;
} catch (err) {
////////////////////////////////////////////////
// Do whatever error handling you want here ! //
////////////////////////////////////////////////
var newErr;
newErr = new Error(err);
alert('Error Eek: ' + newErr.stack);
return false;
}
};
/**
* [evalFile] For calling jsx scripts from the js engine
*
* The jsx.evalFiles method is used for executing saved jsx scripts
* where the jsxScript parameter is a string of the jsx scripts file location.
* For convenience jsx.file or jsx.evalfile can be used instead of jsx.evalFile
*
* @param {String} file
* The path to jsx script
* If only the base name is provided then the path will be presumed to be the
* To execute files stored in the jsx folder located in the __dirname folder use
* jsx.evalFile('myFabJsxScript.jsx');
* To execute files stored in the a folder myFabScripts located in the __dirname folder use
* jsx.evalFile('./myFabScripts/myFabJsxScript.jsx');
* To execute files stored in the a folder myFabScripts located at an absolute url use
* jsx.evalFile('/Path/to/my/FabJsxScript.jsx'); (mac)
* or jsx.evalFile('C:Path/to/my/FabJsxScript.jsx'); (windows)
*
* @param {Function} callback
* The callback function you want the jsx script to trigger on completion
* The result of the jsx script is passed as the argument to that function
* The function can exist in some other file.
* Note that InDesign does not automatically pass the callBack as a string.
* Either write your InDesign in a way that it returns a sting the form of
* return 'this is my result surrounded by quotes'
* or use the force eval option
* [Optional DEFAULT no callBack]
*
* @param {Object} replacements
* The replacements to make on the jsx script
* give the following script (template)
* 'alert("__message__: " + __val__);'
* and we want to change the script to
* 'alert("I was born in the year: " + 1234);'
* we would pass the following object
* {"message": 'I was born in the year', "val": 1234}
* or if not using reserved words like do we can leave out the key quotes
* {message: 'I was born in the year', val: 1234}
* By default when possible the forceEvalScript will be set to true
* The forceEvalScript option cannot be true when there are replacements
* To force the forceEvalScript to be false you can send a blank set of replacements
* jsx.evalFile('myFabScript.jsx', {}); Will NOT be executed using the $.evalScript method
* jsx.evalFile('myFabScript.jsx'); Will YES be executed using the $.evalScript method
* see the forceEvalScript parameter for details on this
* [Optional DEFAULT no replacements]
*
* @param {Bolean} forceEval
* If the script should be wrapped in an eval and try catch
* This will 1) provide useful error feedback if heaven forbid it is needed
* 2) The result will be a string which is required for callback results in InDesign
* [Optional DEFAULT true]
*
* If no replacements are needed then the jsx script is be executed by using the $.evalFile method
* This exposes the true value of the $.fileName property <span class="wp-font-emots-emo-sunglasses"></span>
* In such a case it's best to avoid using the $.__fileName() with no base name as it won't work
* BUT one can still use the $.__fileName('baseName') method which is more accurate than the standard $.fileName property <span class="wp-font-emots-emo-happy"></span>
* Let's say you have a Drive called "Graphics" AND YOU HAVE a root folder on your "main" drive called "Graphics"
* You call a script jsx.evalFile('/Volumes/Graphics/myFabScript.jsx');
* $.fileName will give you '/Graphics/myFabScript.jsx' which is wrong
* $.__fileName('myFabScript.jsx') will give you '/Volumes/Graphics/myFabScript.jsx' which is correct
* $.__fileName() will not give you a reliable result
* Note that if your calling multiple versions of myFabScript.jsx stored in multiple folders then you can get stuffed!
* i.e. if the fileName is important to you then don't do that.
* It also will force the result of the jsx file as a string which is particularly useful for InDesign callBacks
*
* Note 1) The order of the parameters is irrelevant
* Note 2) One can pass the arguments as an object if desired
* jsx.evalScript(myCallBackFunction, 'alert("__myMessage__");', true);
* is the same as
* jsx.evalScript({
* script: 'alert("__myMessage__");',
* replacements: {myMessage: 'Hi there'},
* callBack: myCallBackFunction,
* eval: false,
* });
* note that either lower or camelCase key names or valid
* i.e. both callback or callBack will work
*
* The following keys are the same file || jsx || script || jsxScript || jsxscript
* The following keys are the same callBack || callback
* The following keys are the same replacements || replace
* The following keys are the same eval || forceEval || forceeval
*
* @return {Boolean} if the jsxScript was executed or not
*/
Jsx.prototype.evalFile = function() {
var arg, args, callback, fileName, fileNameScript, forceEval, forceEvalScript,
i, jsxFolder, jsxScript, newLine, replacements, success;
success = true; // optimistic <span class="wp-font-emots-emo-happy"></span>
args = arguments;
jsxFolder = path.join(__dirname, 'jsx');
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// $.fileName does not return it's correct path in the jsx engine for files called from the js engine //
// In Illustrator it returns an integer in InDesign it returns an empty string //
// This script injection allows for the script to know it's path by calling //
// $.__fileName(); //
// on Illustrator this works pretty well //
// on InDesign it's best to use with a bit of care //
// If the a second script has been called the InDesing will "forget" the path to the first script //
// 2 work-arounds for this //
// 1) at the beginning of your script add var thePathToMeIs = $.fileName(); //
// thePathToMeIs will not be forgotten after running the second script //
// 2) $.__fileName('myBaseName.jsx'); //
// for example you have file with the following path //
// /path/to/me.jsx //
// Call $.__fileName('me.jsx') and you will get /path/to/me.jsx even after executing a second script //
// Note When the forceEvalScript option is used then you just use the regular $.fileName property //
//////////////////////////////////////////////////////////////////////////////////////////////////////////
fileNameScript = [
// The if statement should not normally be executed
'if(!$.__fileNames){',
' $.__fileNames = {};',
' $.__dirname = "__dirname__";'.replace('__dirname__', __dirname),
' $.__fileName = function(name){',
' name = name || $.fileName;',
' return ($.__fileNames && $.__fileNames[name]) || $.fileName;',
' };',
'}',
'$.__fileNames["__basename__"] = $.__fileNames["" + $.fileName] = "__fileName__";'
].join('');
//////////////////////////////////////////////////////////////////////////////////////
// sort out order which arguments into jsxScript, callback, replacements, forceEval //
//////////////////////////////////////////////////////////////////////////////////////
// Detect if the parameters were passed as an object and if so allow for various keys
if (args.length === 1 && (arg = args[0]) instanceof Object) {
jsxScript = arg.jsxScript || arg.jsx || arg.script || arg.file || arg.jsxscript;
callback = arg.callBack || arg.callback;
replacements = arg.replacements || arg.replace;
forceEval = arg.eval || arg.forceEval || arg.forceeval;
} else {
for (i = 0; i < 5; i++) {
arg = args[i];
if (arg === undefined) {
continue;
}
if (arg.constructor.name === 'String') {
jsxScript = arg;
continue;
}
if (arg.constructor.name === 'Object') {
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// If no replacements are provided then the $.evalScript method will be used //
// This will allow directly for the $.fileName property to be used //
// If one does not want the $.evalScript method to be used then //
// either send a blank object as the replacements {} //
// or explicitly set the forceEvalScript option to false //
// This can only be done if the parameters are passed as an object //
// i.e. jsx.evalFile({file:'myFabScript.jsx', forceEvalScript: false}); //
// if the file was called using //
// i.e. jsx.evalFile('myFabScript.jsx'); //
// then the following jsx code is called $.evalFile(new File('Path/to/myFabScript.jsx', 10000000000)) + ''; //
// forceEval is never needed if the forceEvalScript is triggered //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
replacements = arg;
continue;
}
if (arg.constructor === Function) {
callback = arg;
continue;
}
if (arg === false) {
forceEval = false;
}
}
}
// If no script provide then not too much to do!
if (!jsxScript) {
return false;
}
forceEvalScript = !replacements;
//////////////////////////////////////////////////////
// Get path of script //
// Check if it's literal, relative or in jsx folder //
//////////////////////////////////////////////////////
if (/^\/|[a-zA-Z]+:/.test(jsxScript)) { // absolute path Mac | Windows
jsxScript = path.normalize(jsxScript);
} else if (/^\.+\//.test(jsxScript)) {
jsxScript = path.join(__dirname, jsxScript); // relative path
} else {
jsxScript = path.join(jsxFolder, jsxScript); // files in the jsxFolder
}
if (forceEvalScript) {
jsxScript = jsxScript.replace(/"/g, '\\"');
// Check that the path exist, should change this to asynchronous at some point
if (!window.cep.fs.stat(jsxScript).err) {
jsxScript = fileNameScript.replace(/__fileName__/, jsxScript).replace(/__basename__/, path.basename(jsxScript)) +
'$.evalFile(new File("' + jsxScript.replace(/\\/g, '\\\\') + '")) + "";';
return this.evalScript(jsxScript, callback, forceEval);
} else {
throw new Error(`The file: {jsxScript} could not be found / read`);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Replacements made so we can't use $.evalFile and need to read the jsx script for ourselves //
////////////////////////////////////////////////////////////////////////////////////////////////
fileName = jsxScript.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
try {
jsxScript = window.cep.fs.readFile(jsxScript).data;
} catch (er) {
throw new Error(`The file: ${fileName} could not be read`);
}
// It is desirable that the injected fileNameScript is on the same line as the 1st line of the script
// This is so that the $.line or error.line returns the same value as the actual file
// However if the 1st line contains a # directive then we need to insert a new line and stuff the above problem
// When possible i.e. when there's no replacements then $.evalFile will be used and then the whole issue is avoided
newLine = /^\s*#/.test(jsxScript) ? '\n' : '';
jsxScript = fileNameScript.replace(/__fileName__/, fileName).replace(/__basename__/, path.basename(fileName)) + newLine + jsxScript;
try {
// evalScript(jsxScript, callback);
return this.evalScript(jsxScript, callback, replacements, forceEval);
} catch (err) {
////////////////////////////////////////////////
// Do whatever error handling you want here ! //
////////////////////////////////////////////////
var newErr;
newErr = new Error(err);
alert('Error Eek: ' + newErr.stack);
return false;
}
return success; // success should be an array but for now it's a Boolean
};
////////////////////////////////////
// Setup alternative method names //
////////////////////////////////////
Jsx.prototype.eval = Jsx.prototype.script = Jsx.prototype.evalscript = Jsx.prototype.evalScript;
Jsx.prototype.file = Jsx.prototype.evalfile = Jsx.prototype.evalFile;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Examples //
// jsx.evalScript('alert("foo");'); //
// jsx.evalFile('foo.jsx'); // where foo.jsx is stored in the jsx folder at the base of the extensions directory //
// jsx.evalFile('../myFolder/foo.jsx'); // where a relative or absolute file path is given //
// //
// using conventional methods one would use in the case were the values to swap were supplied by variables //
// csInterface.evalScript('var q = "' + name + '"; alert("' + myString + '" ' + myOp + ' q);q;', callback); //
// Using all the '' + foo + '' is very error prone //
// jsx.evalScript('var q = "__name__"; alert(__string__ __opp__ q);q;',{'name':'Fred', 'string':'Hello ', 'opp':'+'}, callBack); //
// is much simpler and less error prone //
// //
// more readable to use object //
// jsx.evalFile({ //
// file: 'yetAnotherFabScript.jsx', //
// replacements: {"this": foo, That: bar, and: "&&", the: foo2, other: bar2}, //
// eval: true //
// }) //
// Enjoy <span class="wp-font-emots-emo-happy"></span> //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
jsx = new Jsx();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment