@extendscript/sui.module.jaxon
Version:
ExtendScript Preset Manager
918 lines (769 loc) • 37.3 kB
JavaScript
(function () {
var VERSION = 1.0;
var MODULE_PATH = "jaxon";
var thisModule = Sky.getModule(MODULE_PATH);
if( thisModule && thisModule.version >= VERSION) {
return;
};
//--------------------------
// Start jaxon class
function moduleClass() {
var jaxon = this;
jaxon.version = VERSION;
jaxon.description = "ExtendScript Preset Manager";
var LoadCallback = function ( err, module ){
// Throws an error when dependency could not be loaded...
if( err instanceof Error || err instanceof TypeError ) {
throw new TypeError( err.message, $.fileName, $.line );
};
return module;
};
// Load any needed dependencies
var Jaw = Sky.getUtil("jaw", LoadCallback);
//- - - - - - - - - - - - - - - - - - - - - -
// Module code from here...
//- - - - - - - - - - - - - - - - - - - - - -
/* -------------------------------------------------------------------------------
@param fileName : String
Name of file to be saved in user data folder
@param Schema : Object
A schema to validate against. Also used to generate defaults
@param standardPresets : Array
Initial presets that are loaded if no presetsFile is found at filePath
------------------------------------------------------------------------------- */
var presetManager = function( fileName, Schema, standardPresets ) {
// ref to self
var PM = this;
// standard file path
var filePath = Folder.userData + "/" + String(fileName);
var valid = true;
var errors = [];
function userException(message) {
this.message = message;
this.name = 'Error';
}
// S C H E M A S
//-------------------------
// Load Jaw for preset and validate preset Schema
var presetJaw = Jaw.init( Schema );
if(!presetJaw.isValid()) {
throw presetJaw.getErrors()[0];
}
// Create Schema for standardPresets
// This preset manager works on the premise
// that presets are objects collected in an array
var PresetsSchema = { "type": "array", "items": Schema };
var presetsJaws = Jaw.init( PresetsSchema );
if(!presetsJaws.isValid()) {
throw userException(presetsJaws.getErrors());
}
// Validate standardPresets
//-------------------------
if ( typeof standardPresets === 'undefined') standardPresets = [presetsJaws.getTemplate(true)];
if ( Array.isArray( standardPresets ) ) {
standardPresets = JSON.clone( standardPresets );
} else {
throw new userException("Param standardPresets needs to be type of array but is " + String(typeof standardPresets));
}
if(!presetsJaws.wrap(standardPresets).isValid()) {
throw userException(presetsJaws.getErrors());
}
//-------------------------------------------------
// P R I V A T E F U N C T I O N S
//-------------------------------------------------
function createMsg ( bool, comment ) {
// Standard return obj
return {success: Boolean(bool), comment: String( comment ) };
}
function not_in_array ( arr, element ) {
for(var i=0; i<arr.length; i++) {
if (arr[i] == element) return false;
}
return true;
}
function fileExist ( filePath ) {
var f = File(filePath);
if(f.exists){
return true;
} else {
return false;
}
}
function writeFile ( filePath, contentString ) {
// This function will (over) write a file to file path
// filePath does not need to exist
var alertUser = true;
function error( bool, message ) {
if(alertUser) {
alert( "Preset Manager\n" + String(message) );
}
try {
f.close();
} catch ( err ) {
// Do nothing
}
return createMsg ( bool, message );
}
var f = File(filePath);
try {
// Set character encoding
f.encoding = "UTF-16";
// Open the file
if( ! f.open('w') ){
return error( false, "Error opening file at " + filePath +": "+ f.error );
}
// Write to file
if( ! f.write( String(contentString) ) ){
return error( false, "Error writing to file at " + filePath +": "+ f.error );
}
// Close the file
if( ! f.close() ){
return error( false, "Error closing file at " + filePath +": "+ f.error );
}
} catch ( r ) {
return error( false, r.error );
}
return createMsg ( true, "Done" );
}
function updateObj ( Old_Obj, New_Obj, ignoreKeys ) {
// This function will try and copy all values
// from Old_Obj to New_Obj
for ( var key in New_Obj ) {
if( Old_Obj.hasOwnProperty(key) ) {
if ( not_in_array( ignoreKeys, key ) ) {
New_Obj[key] = Old_Obj[key];
}
}
}
return JSON.clone( New_Obj );
}
function updatePreset ( oldPreset, ignoreKeys ) {
var ignoreKeys = ignoreKeys || [];
if(! ignoreKeys instanceof Array) {
throw "The function updatePreset expects ignoreKeys to be instance of Array."
}
if( oldPreset === undefined ) {
return presetJaw.getTemplate( true );
}
if(! oldPreset instanceof Object) {
throw "The function updatePreset expects Preset to be instance of Object."
}
// Create a copy of the standard preset
var newPreset = presetJaw.getTemplate( true );
return updateObj( oldPreset, newPreset, ignoreKeys );
}
function calcIndex( pos, len ) {
// Calculate the actual preset index
// Done to catch negative values
var i = pos;
if ( pos < 0 ) {
i = len - Math.abs(pos);
}
return Math.abs(i);
}
// P R E S E T C O N T R O L L E R
//-------------------------------------------------
function presetController( Preset ) {
// This preset controller handles a single preset
// And will be attached to any preset
var PresetController = this;
// Create a fresh template
var _Preset = Jaw.init( Schema );
var temporaryState = false;
var _hasProp = function( propName ) {
var Preset = _Preset.get();
if( Preset.hasOwnProperty( propName ) ){
return true;
} else {
alert("UiPreset does not have property " + propName);
return false;
}
}
// Public
//-------
PresetController.setTemporaryState = function( bool ) {
temporaryState = bool == true;
return temporaryState;
}
PresetController.getTemporaryState = function( bool ) {
return temporaryState;
}
PresetController.getTemplate = function( allProperties ) {
// allProperties = undefined = false
return presetJaw.getTemplate( allProperties );
}
PresetController.get = function() {
return _Preset.get();
}
PresetController.load = function( Preset ) {
_Preset.wrap( updatePreset( Preset ) );
if(_Preset.getErrors().length > 0) {
alert("Could not load preset.\n" + JSON.stringify(_Preset.getErrors()) );
}
return _Preset.get();
}
// Get and set preset properties
PresetController.getProp = function( propName ) {
return _Preset.get( propName );
}
PresetController.setProp = function( propName, val ) {
var prop = String(propName);
if( _hasProp( prop ) ) {
_Preset.set(prop, val);
return JSON.clone( _Preset.get(prop) );
}
alert("Could not set preset property.\nProperty " + prop + " does not exist.");
return undefined;
}
// init
PresetController.load( Preset );
} // End of presetController
// P R E S E T S C O N T R O L L E R
//-------------------------------------------------
function presetsController( presets ) {
// The presets controller handles the presets array
var PresetsController = this;
function infuse( presets ) {
var holder = new Array();
var len = presets.length;
for (var i = 0; i < len; i++) {
holder[i] = new presetController( presets[i] );
}
return holder;
}
var _Presets = infuse( presets );
function clean() {
var holder = new Array();
var len = _Presets.length;
for (var i = 0; i < len; i++) {
holder[i] = _Presets[i].get();
}
return holder;
}
function cleanSave_presets(){
// This function removes any temporary preset before saving to disk
var holder = new Array();
var len = _Presets.length;
for (var i = 0; i < len; i++) {
if(! _Presets[i].getTemporaryState() ) {
holder.push( _Presets[i].get() );
}
}
return holder;
}
function presetExist( key, val ) {
var len = _Presets.length;
for (var i = len-1; i >= 0; i--) {
if (_Presets[i].getProp(key) == val) {
return true;
}
}
return false;
}
function outOfRange( pos, len ) {
var pos = parseInt(pos);
var len = parseInt(len);
if( len == 0 ) {
// Everything is out of range :)
return true;
}
if(pos > len) {
return true;
}
if( pos < -1-len ) {
return true;
}
return false;
}
//------------------------------------------
// Public access
PresetsController.get = function () {
return clean();
}
PresetsController.getTemplate = function() {
return presetJaw.getTemplate( true );
}
PresetsController.getByKey = function ( key, val ) {
// Sample usage: PM.Presets.getByKey('id',3);
// Please note that this function returns the first
// preset it can find
var len = _Presets.length;
for (var i = len-1; i >= 0; i--) {
if (_Presets[i].getProp(key) === val) {
return _Presets[i].get();
}
}
return false;
}
PresetsController.getIndex = function ( key, val ) {
// Sample usage: PM.Presets.getIndex('name','this');
// returns array with matches
var matches = new Array();
var len = _Presets.length;
for (var i = len-1; i >= 0; i--) {
if (_Presets[i].getProp(key) == val) {
matches.unshift(i);
}
}
return matches;
}
PresetsController.getByIndex = function ( position ) {
// Sample usage: PM.getPresetByIndex( 3 );
var len = _Presets.length;
if( outOfRange( position, len ) ) {
alert("Preset Manager\nThere is no preset at index " + position);
return false;
}
var i = calcIndex( parseInt(position), len );
return _Presets[i].get();
}
PresetsController.getPropList = function ( key ) {
if( !PM.UiPreset.get().hasOwnProperty( key ) ) {
alert("Preset Manager\nCan't create propertylist with key " + key);
return [];
}
var len = _Presets.length;
var propList = new Array();
for (var i = 0; i < len; i++) {
propList[i] = _Presets[i].getProp(key);
}
return propList;
}
PresetsController.reset = function () {
_Presets = infuse( standardPresets );
}
PresetsController.load = function ( presets ) {
_Presets = infuse( presets );
return clean();
}
PresetsController.add = function ( preset, options ) {
// options { position: integer, temporary preset: boolean }
// para position; index that can handle negative numbers
// that are calculated from the back -1 == last
var len = _Presets.length;
var pos = len;
if(options && options.hasOwnProperty('position')) {
pos = options.position;
if ( isNaN(pos) ) {
pos = len;
}
if( outOfRange(pos, len) ) {
pos = len;
}
}
var i = calcIndex( pos, len+1 );
var infusedPreset = new presetController( preset );
if(options && options.hasOwnProperty('temporary')) {
infusedPreset.setTemporaryState( options.temporary == true );
}
_Presets.splice(i, 0, infusedPreset);
return clean();
}
PresetsController.addUnique = function ( Preset, key, options ) {
// Sample usage: PresetManager.Presets.addUnique( Preset, 'name' );
var silently = false;
var position = -1;
if(options && options.hasOwnProperty('position')) {
if( !isNaN(options.position) ) position = parseInt(options.position);
}
if(options && options.hasOwnProperty('silently')) {
silently = options.silently == true;
}
var exist = presetExist(key, Preset[key]);
if(exist){
if(silently) {
var overwrite = true;
} else {
var overwrite = confirm("Do you want to overwrite the existing preset?");
}
if (overwrite) {
PresetsController.removeWhere( key, Preset[key] );
} else {
return false;
}
}
var newLen = _Presets.length+1;
PresetsController.add( Preset, {position: position} );
return _Presets.length == newLen;
}
PresetsController.remove = function ( position ) {
var len = _Presets.length;
// Check for outside range
if( outOfRange(position, len) ) {
alert("Could not remove preset\nOut of range: presets length: " + len + " index to be removed: " + position);
return false;
}
var i = calcIndex( parseInt(position), len );
_Presets.splice( i, 1 );
return true;
}
PresetsController.overwriteIndex = function ( position, Preset ) {
PresetsController.remove( position );
PresetsController.add( Preset, {position: position} );
return clean();
}
PresetsController.removeWhere = function ( key, val ) {
// Sample usage: PM.Presets.removeWhere('id',3);
// This function removes any preset that contains key - val match
// It returns true if any presets have been removed
var success = false;
var len = _Presets.length;
for (var i = len-1; i >= 0; i--) {
if (_Presets[i].getProp(key) === val) {
_Presets.splice( i, 1 );
success = true;
}
}
return success;
}
PresetsController.saveToDisk = function ( ) {
var presetStr = JSON.stringify( cleanSave_presets() );
return writeFile(filePath, presetStr);
}
PresetsController.loadFromDisk = function () {
if( !fileExist(filePath) ){
alert("Cannot load presets.\nNo preset file found at " + filePath);
return false;
}
var PresetsFile = File(filePath);
PresetsFile.open('r');
var content = PresetsFile.read();
PresetsFile.close();
try {
var NewPresets = JSON.parse(content);
_Presets = infuse( NewPresets );
return true;
} catch(e) {
alert("Error reading JSON\n" + e.description);
return false;
}
}
PresetsController.removeFromDisk = function () {
if( fileExist(filePath) ){
var PresetsFile = File(filePath);
PresetsFile.remove();
return true;
}
return false;
}
} // End of presetsController
// W I D G E T
//-------------------------------------------------
function widgetCreator( SUI_Group ) {
// Global widgetCreator vars
var WidgetCreator = this;
var DataPort = { getData: undefined, renderUiPreset: undefined };
var ButtonText = {save: "Save Preset", clear: "Clear Preset"};
var newName = "New Preset";
var lastUsedName = "Last Used";
var lockChars = ['[',']']; // Any preset that starts with a locking character can't be deleted by the user
// The following variables track which preset this preset is based on
// This makes it easy to over-ride an existing preset
var basedOnPresetName = newName;
var newPresetName = "";
var lastUsedPresetName = "";
// The following variables make it possible to update UI every time UiPreset is changed
// Even when the widget is not loaded
var presetsDrop = { selection: 0 };
var presetBut = { text: "" };
var presetDropList = [];
var updateUI = true;
var listKey = "";
WidgetCreator.get = function () {
if( DataPort.hasOwnProperty('getData') && typeof DataPort.getData === 'function' ) {
return DataPort.getData();
} else {
return createMsg ( false, "No data port defined" );
}
}
WidgetCreator.getLockChars = function () {
return lockChars;
}
WidgetCreator.getButtonText = function () {
return ButtonText;
}
WidgetCreator.getNewPresetName = function () {
return newName;
}
WidgetCreator.getLastUsedPresetName = function () {
return lastUsedName;
}
function updatePresetNames() {
newPresetName = String(lockChars[0] + " " + newName + " " + lockChars[1]);
lastUsedPresetName = String(lockChars[0] + " " + lastUsedName + " " + lockChars[1]);
}
function getDropDownIndex( index ) {
var len = PM.Presets.getPropList( listKey ).length;
var i = calcIndex( parseInt(index), len );
return i+1; // [New Preset]
};
function createDropDownList(){
// Check listKey and load dropDown content
presetDropList = PM.Presets.getPropList( listKey );
// Add new (clear) preset to dropdown list
presetDropList.unshift( newPresetName );
}
WidgetCreator.activateNew = function () {
// This function resets the dropdown to first (New Preset)
updateUI = false;
presetsDrop.selection = 0;
presetBut.text = ButtonText.save;
updateUI = true;
return createMsg ( true, "Done" );
}
WidgetCreator.activateLastUsed = function () {
// This function resets the drop-down to last (Last Used)
presetsDrop.selection = PM.Presets.getIndex( listKey, lastUsedPresetName )[0]+1;
presetBut.text = ButtonText.save;
return createMsg ( true, "Done" );
}
WidgetCreator.saveUiPreset = function () {
PM.UiPreset.load( DataPort.getData() );
return createMsg ( true, "Done" );
}
WidgetCreator.savePreset = function ( options ) {
WidgetCreator.saveUiPreset();
// Process Options
if(options && options.hasOwnProperty('updateProps')) {
for ( var i = 0; i < options.updateProps.length; i++ ) {
PM.UiPreset.setProp( options.updateProps[i].key, options.updateProps[i].value );
}
}
var position = -1;
if( options && options.hasOwnProperty('position') ) {
position = parseInt(options.position);
}
PM.UiPreset.save( position );
PM.Presets.saveToDisk();
return createMsg ( true, "Done" );
}
WidgetCreator.overwritePreset = function( key, val, options ) {
// Save SUI data
WidgetCreator.saveUiPreset();
PM.UiPreset.setProp( key, val );
// Process Options
var index = -1;
if(options && options.hasOwnProperty('position')) {
index = parseInt(options.position);
} else {
index = PM.Presets.getIndex( key, val );
}
PM.Presets.addUnique( PM.UiPreset.get(), key, {position: index, silently: true} );
PM.Presets.saveToDisk();
return createMsg ( true, "Done" );
}
WidgetCreator.saveLastUsed = function() {
try {
var originalName = PM.UiPreset.get()[listKey];
WidgetCreator.overwritePreset( listKey, lastUsedPresetName, {position: -1} );
PM.UiPreset.setProp( listKey, originalName );
} catch ( err ) {
alert(err)
}
return PM.UiPreset.get();
}
WidgetCreator.reset = function() {
return createMsg( false, "Widget is not loaded.");
}
WidgetCreator.loadIndex = function( index ) {
// Load data in UiPreset
PM.UiPreset.loadIndex( index );
// Update SUI
DataPort.renderUiPreset();
presetsDrop.selection = getDropDownIndex( index );
return true;
}
WidgetCreator.attachTo = function ( SUI_Group, listKeyID, Port, Options ) {
var onloadIndex = null;
listKey = String(listKeyID);
if(! (Port && Port.hasOwnProperty('renderData') && Port.hasOwnProperty('getData')) ) {
return createMsg( false, "Could not establish data port.");
}
DataPort.renderUiPreset = function () {
basedOnPresetName = String( PM.UiPreset.getProp(listKey) );
Port.renderData( PM.UiPreset.get() );
}
DataPort.getData = Port.getData;
// Process Options
if(Options && Options.hasOwnProperty('onloadIndex')) {
onloadIndex = parseInt(Options.onloadIndex);
}
if(Options && Options.hasOwnProperty('lockChars')) {
if(lockChars.length == 2) {
lockChars[0] = String(Options.lockChars[0]);
lockChars[1] = String(Options.lockChars[1]);
}
}
if(Options && Options.hasOwnProperty('newPresetName')) {
newName = String(Options.newPresetName);
}
if(Options && Options.hasOwnProperty('lastUsedPresetName')) {
lastUsedName = String(Options.lastUsedPresetName);
}
if(Options && Options.hasOwnProperty('buttonTextSave')) {
ButtonText.save = String(Options.buttonTextSave);
}
if(Options && Options.hasOwnProperty('buttonTextClear')) {
ButtonText.clear = String(Options.buttonTextClear);
}
function updatePresetData() {
// Update newPresetName
updatePresetNames();
createDropDownList( listKey );
}
updatePresetData();
// Attach new widget to SUI_Group
presetsDrop = SUI_Group.add('dropdownlist', undefined, presetDropList);
presetsDrop.alignment = 'fill';
presetsDrop.selection = getDropDownIndex( onloadIndex );
presetBut = SUI_Group.add('button', undefined, ButtonText.save);
presetsDrop.onChange = function () {
if(updateUI) {
// Load data in UiPreset
if(this.selection.index == 0) {
PM.UiPreset.reset();
} else {
PM.UiPreset.loadIndex( this.selection.index-1 );
}
DataPort.renderUiPreset();
// Update button
if( this.selection.text.indexOf(lockChars[0]) == 0 ){
presetBut.text = ButtonText.save;
} else {
presetBut.text = ButtonText.clear;
}
}
}
WidgetCreator.updatePresetsDrop = function( selectIndex ) {
// This function will update the drop down without updating UI input values
updateUI = false;
updatePresetData();
presetsDrop.removeAll();
var len = presetDropList.length;
for (var i=0; i<len; i++) {
presetsDrop.add('item', presetDropList[i] );
}
presetsDrop.selection = isNaN(selectIndex) ? 0 : calcIndex( selectIndex, len );
updateUI = true;
return createMsg( true, "Done");
}
WidgetCreator.reset = function() {
// Update Presets Dropdown
WidgetCreator.updatePresetsDrop();
// Clear UI and update button states
presetsDrop.selection = 0;
return createMsg( true, "Done");
}
function _addUiPresetToPresets( defaultName ) {
var defaultName = defaultName || basedOnPresetName;
defaultName = String( defaultName );
var presetName = prompt("Name: ", defaultName, "Save Preset");
if ( presetName != null ) {
if ( presetName.indexOf(lockChars[0]) == 0 ) {
alert( "You can't start a preset name with: " + lockChars[0] );
// Recurse
return _addUiPresetToPresets();
}
PM.UiPreset.setProp( listKey, presetName );
// Add preset to end
PM.Presets.addUnique( PM.UiPreset.get(), listKey, {position:-1} );
WidgetCreator.reset();
presetsDrop.selection = presetsDrop.items.length-1;
}
}
presetBut.onClick = function () {
if( presetBut.text == ButtonText.clear ) {
PM.Presets.remove( presetsDrop.selection.index - 1 );
WidgetCreator.reset();
presetBut.text = ButtonText.save;
} else { // Save preset
PM.UiPreset.load( DataPort.getData() );
_addUiPresetToPresets();
}
PM.Presets.saveToDisk();
}
// Init UI filler
if( isNaN(onloadIndex) ) {
// Render session state
DataPort.renderUiPreset();
} else {
// Load selected index
WidgetCreator.loadIndex( onloadIndex );
}
return createMsg( true, "Done");
}
}; // End Widget
//-------------------------------------------------
// S T A R T P U B L I C A P I
//-------------------------------------------------
PM.getPresetsFilePath = function () {
return filePath;
}
PM.getErrors = function() {
// Always return an array of errors
if(Array.isArray(errors)) {
return errors;
} else {
return [errors];
}
}
// current preset (The presets we manipulate)
// We need to buils these
PM.Presets = new presetsController( standardPresets );
// create a data controller for UiPreset
PM.UiPreset = new presetController( presetJaw.getTemplate( true ) );
// create widget builder
PM.Widget = new widgetCreator();
// Extend presetController UiPreset
PM.UiPreset.save = function( position ) {
// position or index, negative numbers are calculated from the back -1 == last
return PM.Presets.add( PM.UiPreset.get(), {position: position} );
}
PM.UiPreset.loadIndex = function ( index ) {
var len = PM.Presets.get().length;
if(Math.abs(parseInt(index)) > len) {
alert("Preset Manager\nLoad index is not a valid preset index. ( " + index + " )");
return createMsg ( false, "Not a valid preset index." );
}
PM.UiPreset.load( PM.Presets.getByIndex( index ) );
return createMsg ( true, "Done" );
}
PM.UiPreset.reset = function ( ) {
PM.UiPreset.load( presetJaw.getTemplate( true ) );
}
PM.reset = function( hard ) {
var hard = (hard == true);
if( hard ) {
PM.Presets.reset();
PM.Presets.saveToDisk();
} else {
PM.Presets.loadFromDisk();
}
PM.UiPreset.reset();
PM.Widget.reset();
}
PM.format = function ( preset ) {
return updatePreset ( preset );
}
//-------------------------------------------------
// E N D P U B L I C A P I
//-------------------------------------------------
// I N I T
//---------
// Save the standard presets if not allready exist
if(!fileExist( filePath ) ){
if( ! PM.Presets.saveToDisk() ){
throw("Failed to start PM\nUnable to save presets to " + filePath);
}
}
// Load the presets
PM.Presets.loadFromDisk();
};
jaxon.init = function( fileName, Schema, standardPresets ) {
return new presetManager( fileName, Schema, standardPresets );
};
};
//--------------------------
// End jaxon class
Sky.setUtil(MODULE_PATH, new moduleClass() );
})();