UNPKG

choppy

Version:
1,580 lines (1,228 loc) 68.7 kB
#! /usr/bin/env node var photoshop = require('./photoshop-0.5.2-edit/photoshop'); var fs = require('fs'); var path = require('path'); var spawn = require('child_process').spawn; var Choppy = function() { photoshop.invoke(`function closeIfDupe(){ var doc = app.activeDocument; if (doc.name.substr(-5) == ' copy'){ doc.close(SaveOptions.DONOTSAVECHANGES); } }`,[], function(error){ }) var self = this; var now = new Date(); var nowDate = now.getFullYear()+'-'+(now.getMonth()+1)+'-'+now.getDate(); var nowTime = now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds(); self.pubtime = nowDate+' '+nowTime; // `2018-8-3 11:12:40` self.TEMPLATE_DIR_NAME = 'tpl'; self.CONFIG_FILENAME = '.choppy'; self.TEMPLATE_PARTS = ['parse', 'header', 'main', 'inter', 'footer']; // Get the template data from core self.templateFiles = []; self.templateFilesCore = []; var coreTemplateDirPath = __dirname + path.sep + self.TEMPLATE_DIR_NAME + path.sep; Choppy.addFilePathsInDirToArray(coreTemplateDirPath, self.templateFilesCore); // Jsx (Global) self.JSX_DIR_NAME = 'jsx'; self.jsxPathsCore = {}; self.jsxPathsCore.pre = loadJsxPaths(path.join(__dirname, 'jsx', 'pre')); self.jsxPathsCore.post = loadJsxPaths(path.join(__dirname, 'jsx', 'post')); self.jsxPathsCore.standalone = loadJsxPaths(path.join(__dirname, 'jsx', 'standalone')); // Get args self.psdPaths = []; // User wants you to open doc if not already open self.verbose = false; self.envVars = {}; var argv = {_:process.argv.slice(2)}; //require('minimist')(process.argv.slice(2)); self.standaloneCmds = []; for (var k = 0; k < argv._.length; k++){ if (String(argv._[k]).toLowerCase() === 'verbose'){ self.verbose = true; } else if (path.extname(String(argv._[k])).toLowerCase() === '.psd'){ self.psdPaths.push(argv._[k]); } else if (argv._[k].split('=').length == 2){ var varParts = argv._[k].split('=') self.envVars[varParts[0]] = varParts[1]; } else { self.standaloneCmds.push(argv._[k]); } } //if (psdPaths.length === 0){ // psdPaths = ['{active}']; //} //for (var ddd = 0; ddd < psdPaths.length; ddd++){ //} self.psdIndex = -1; self.processNext(); }; Choppy.simpleObjDupe = function(obj){ var dupe = {}; for (var p in obj){ dupe[p] = obj[p]; } return dupe; } Choppy.simpleArrDupe = function(arr){ var dupe = []; for (var i = 0; i < arr.length; i++){ dupe[i] = arr[i]; } return dupe; } Choppy.prototype.onPsdDone = function() { this.processNext(); } Choppy.prototype.processNext = function() { this.psdIndex++; var targetPsdPath = this.psdPaths[this.psdIndex]; var activeMode; if (this.psdPaths.length == 0){ activeMode = true; if (this.psdIndex == 1){ return; } } else { activeMode = false; if (targetPsdPath == null){ //console.log('Processed ' + this.psdPaths.length + ' docs'); return; } var pathSource = targetPsdPath; if (targetPsdPath.charAt(0) === '~' || targetPsdPath.charAt(0) === path.sep){ // Know it's an absolute path } else { // Attempt relative targetPsdPath = process.cwd() + path.sep + targetPsdPath; if (!fs.existsSync(targetPsdPath)){ // Fallback to abs targetPsdPath = pathSource; } } } var psdLabel = !activeMode ? path.basename(targetPsdPath) : 'Active document'; console.log('\nPSD ' + String(this.psdIndex + 1) + '/' + (activeMode ? '1' : this.psdPaths.length) + ' '+psdLabel+' ...'); var self = this; // Get the active doc. photoshop.invoke(ensurePsdIsActiveDocumentJSX, [targetPsdPath, path.sep], function(error, activeDocument){ self.jsxPaths = Choppy.simpleObjDupe(self.jsxPathsCore); // Bypass initial checks if calling standalone JSX without an open PSD if (activeDocument || self.standaloneCmds.length == 0){ var responseBuffer = ''; if (!activeDocument){ throw new Error('Active document seems to be a dupe (activeDocument '+(activeDocument ? 'OK' : 'null')+',self.standaloneCmds.length='+self.standaloneCmds.length+')'); } var psdContainingDir = Choppy.ensureDirPathHasTrailingSlash(activeDocument.path, path.sep); var baseConfigData = {}; // Check for config var configFilePath = psdContainingDir + self.CONFIG_FILENAME; if (fs.existsSync(configFilePath)) { baseConfigData = JSON.parse(fs.readFileSync(configFilePath, 'utf8')); if (baseConfigData.basePath && baseConfigData.basePath.length > 0){ baseConfigData.basePath = Choppy.ensureDirPathHasTrailingSlash(baseConfigData.basePath, path.sep); } } else { baseConfigData = {}; } // Check for scripts var localJSXDir = path.join(psdContainingDir, self.JSX_DIR_NAME); if (fs.existsSync(localJSXDir)) { // Will not fail if subdir not exists. self.jsxPaths.pre = loadJsxPaths(path.join(localJSXDir, 'pre'), self.jsxPaths.pre); self.jsxPaths.post = loadJsxPaths(path.join(localJSXDir, 'post'), self.jsxPaths.post); self.jsxPaths.standalone = loadJsxPaths(path.join(localJSXDir, 'standalone'), self.jsxPaths.standalone); } } // Call `standalone` commands and exit. console.log('\n'); for (var i = 0; i < self.standaloneCmds.length; i++){ // self.jsxPaths.standalone var jsxScriptName = self.standaloneCmds[i]; var extParts = jsxScriptName.split('.'); if (extParts.length > 1 && extParts[1].toLowerCase() == 'jsx'){ // Remove ext if set jsxScriptName = jsxScriptName.substr(0, jsxScriptName.length-1); } if (!self.jsxPaths.standalone[jsxScriptName]){ throw new Error('JSX `standalone` script not found `'+jsxScriptName+'`'); } var jsxPath = self.jsxPaths.standalone[jsxScriptName]; console.log('Running *standalone* cmd `'+jsxScriptName+'`...'); photoshop.invoke(`function (jsxPath){ var jsxFile = new File(jsxPath); if (!jsxFile.exists){ throw new Error('JSX standalone file not found '+jsxScriptName+''); } var halt = false; $.evalFile(jsxFile); }`, [jsxPath], function(err){ if (err){ throw err; } }); } if (self.standaloneCmds.length > 0){ console.log('Standalone cmd/s complete.\n') return; } // Check for templates self.templateFiles = Choppy.simpleArrDupe(self.templateFilesCore); var localTemplateDirPath = psdContainingDir + self.TEMPLATE_DIR_NAME + path.sep; if (fs.existsSync(localTemplateDirPath)) { Choppy.addFilePathsInDirToArray(localTemplateDirPath, self.templateFiles); } var tplData = self.getTemplateDataFromFiles(self.templateFiles); var processStream = photoshop.createStream(processJSX, {tplData:tplData, pathSep:path.sep, baseConfigData:baseConfigData, TEMPLATE_PARTS:self.TEMPLATE_PARTS, jsxPaths:self.jsxPaths, envVars:self.envVars, pubtime: self.pubtime}).on('data', function(data) { var dataStr = data.toString(); if (dataStr.substr(0,6) === 'debug:'){ console.log(dataStr.substr(6)); } else if (dataStr.substr(0,4) === 'log:'){ try { console.log.apply(this, JSON.parse(dataStr.substr(4))) } catch(err) { // console.log('Unable to unpack JSX console.log:') //console.log(dataStr.substr(5)) } } else { responseBuffer += dataStr; } }).on('end', function() { var responseData; try { responseData = JSON.parse(responseBuffer); } catch (e) { console.log(responseBuffer.toString()); return; } if (!responseData.push){ responseData = [responseData]; } var responseDataArr = responseData; for (var rd = 0; rd < responseDataArr.length; rd++){ responseData = responseDataArr[rd]; //console.log(responseData); //return; if (self.verbose){ console.log(JSON.stringify(responseData.outputData, null, 2)); } console.log('\n' + responseData.outputString + '\n'); if (responseData.outputFilePath && responseData.outputFilePath.length > 0){ var outputFileContents = responseData.outputString; if (responseData.outputTags && responseData.outputTags.start && responseData.outputTags.end && responseData.outputTags.start.length > 0 && responseData.outputTags.end.length > 0){ for (var ps = 0; ps < 2; ps++){ var existingContents = fs.readFileSync(psdContainingDir + responseData.outputFilePath, 'utf8'); var searchPattern = new RegExp(responseData.outputTags.start.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') + '(.|[\r\n])*' + responseData.outputTags.end.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'), 'gi'); outputFileContents = existingContents.replace(searchPattern, responseData.outputTags.start+responseData.outputString+responseData.outputTags.end); var tagMatch = existingContents.match(searchPattern); if (tagMatch === null){ if (ps == 1){ throw new Error('Output tags not found.'); } else { responseData.outputTags.start = responseData.outputTags.start.split('\\-').join('-'); responseData.outputTags.end = responseData.outputTags.end.split('\\-').join('-'); //console.log(responseData.outputTags.start); } } else { console.log('Found tags: "'+responseData.outputTags.start+'" and "'+responseData.outputTags.end+'".\n'); break; } } } fs.writeFileSync(psdContainingDir + responseData.outputFilePath, outputFileContents); console.log('Wrote to ' + responseData.outputFilePath + '\n'); } if (rd == responseDataArr.length - 1){ // Last loop only self.onPsdDone(); } } }); }); } // Given an array of template filepaths, build a data object. Choppy.prototype.getTemplateDataFromFiles = function(filePathArr) { var tplData = {}; for (var k = 0; k < filePathArr.length; k++){ var tplFilePath = filePathArr[k]; var tplFileName = path.basename(tplFilePath); var stats = fs.statSync(tplFilePath); if (!stats.isDirectory()){ var tplBaseName = path.basename(tplFilePath, path.extname(tplFilePath)); if (tplBaseName.charAt(0) !== '.'){ var part = 'main'; if (tplBaseName.split('.').length !== 1){ for (var i = 0; i < this.TEMPLATE_PARTS.length; i++){ if (this.TEMPLATE_PARTS[i] !== 'main' && (tplBaseName + '.').split('.'+this.TEMPLATE_PARTS[i] + '.').length === 2){ tplBaseName = (tplBaseName + '.').split('.'+this.TEMPLATE_PARTS[i] + '.').join(''); part = this.TEMPLATE_PARTS[i]; break; } } } if (!tplData[tplBaseName]){ tplData[tplBaseName] = {}; } if (!tplData[tplBaseName][part]){ if (part == 'parse'){ tplData[tplBaseName][part] = tplFilePath; } else { var contents = fs.readFileSync(tplFilePath, 'utf8'); tplData[tplBaseName][part] = contents; } } else { throw new Error('Template part already defined'); } } } } // Check every template has a 'main' part. for (var p in tplData){ if (!tplData[p].main){ throw new Error('Template missing a main part'); } } return tplData; }; // Add file paths within dir path to an existing array. Choppy.addFilePathsInDirToArray = function(dirPath, array) { dirPath = Choppy.ensureDirPathHasTrailingSlash(dirPath, path.sep); var tplDirList = fs.readdirSync(dirPath); for (var pp in tplDirList) { var tplFileName = tplDirList[pp]; var tplFilePath = dirPath + tplFileName; array.push(tplFilePath); } }; // Make sure there's a trailing slash on a dir path. Choppy.ensureDirPathHasTrailingSlash = function(path, sep){ path = String(path); if (path.charAt(path.length - 1) !== sep){ path = path + sep; } return path; }; // JSX // --- function ensurePsdIsActiveDocumentJSX(targetPath, sep){ if (targetPath === null){ return {path : app.activeDocument.path}; } else { var target = new File(targetPath); var activeDoc; try{ activeDoc = app.activeDocument; } catch(e){ activeDoc = null; } if (activeDoc){ var active = new File(app.activeDocument.path + sep + app.activeDocument.name); // Path already open if (target.absoluteURI === active.absoluteURI){ return {path : app.activeDocument.path}; } // Is doc in another tab? if (app.documents.length > 1){ for (var i = 0;i < app.documents.length; i++){ app.activeDocument = app.documents[i]; var active = new File(app.activeDocument.path + sep + app.activeDocument.name); if (target.absoluteURI === active.absoluteURI){ return {path : app.activeDocument.path}; } } } } // Open the doc app.open(target); return {path : app.activeDocument.path}; } } function processJSX(stream, props){ var console = {}; console.log = function(){ stream.writeln('log:'+JSON.stringify(arguments)); } // If a layer comp or layer starts with this char then they will not be included in operatios var IGNORE_PREFIX_CHARS = {'`':true}; var utilMsg = '\n'; var doc = app.activeDocument; //var selLayerLookup = {}; var originalDoc = doc; doc = doc.duplicate(); // Set args var tplData = props.tplData; var pathSep = props.pathSep; var baseConfigData = props.baseConfigData; var TEMPLATE_PARTS = props.TEMPLATE_PARTS; var jsxPaths = props.jsxPaths; var envVars = props.envVars; var pubtime = props.pubtime; //stream.writeln('debug:var tplData=' + JSON.stringify(tplData) +';'); //stream.writeln('debug:var pathSep=' + JSON.stringify(pathSep) +';'); //stream.writeln('debug:var baseConfigData=' + JSON.stringify(baseConfigData) +';'); //stream.writeln('debug:var TEMPLATE_PARTS=' + JSON.stringify(TEMPLATE_PARTS) +';'); // The default image prop fallbacks. var PROP_DEFAULTS = {alt: '', cropToBounds: false, template: 'img', ext: 'jpg', quality: 80, flipX: false, flipY: false, relativePath: './', basePath: './', matte:null, colors:256, scale:1, sizeFileHandle:'', sizeIndex:-1, sizes:null, reg: 'TL', outputValueFactor: 1, regX:0, regY:0, regPercX:0, regPercY:0, forceW:-1, forceH:-1, forceGrid:-1, roundOutputValues:false, boundsComp:'', outputOriginX: 0, outputOriginY: 0, outputOriginLayer:null, placeholder:false, reverseOrder: false, tlX:0, tlY:0, wipeRelativePath: '', pre: '', post:'', parent:'', type:'', tfParams: null, flags: '', pubtime: pubtime, suffix:'',prefix:'',nestlevel:0}; // If obj prop is not set, below will be used as sub prop defaults, should they be referenced by a template var OBJ_PROP_DEFAULTS = {tfParams: {align:'',text:'',font:'',alpha:1.0, color:'#000000',fontStyle:'',fontName:'',fontSize:'', visBoundsTLX:0, visBoundsTLY:0, visBoundsW:0,visBoundsH:0,boxW:0,boxH:0}}; // // Which props are affected by |outputValueFactor| var OUTPUT_VALUE_FACTOR_PROPS = ['width','height','x','y','regX','regY', 'tlX', 'tlY', 'tfParams.fontSize', 'tfParams.visBoundsTLX', 'tfParams.visBoundsTLY', 'tfParams.visBoundsW', 'tfParams.visBoundsH','makeDir','postExecutePath','tfParams.boxW','tfParams.boxW']; // var BOOL_PROPS = ['cropToBounds', 'flipX', 'flipY', 'roundOutputValues', 'placeholder', 'reverseOrder', 'makeDir']; var NUM_PROPS = ['quality','scale','forceW','forceH','forceGrid', 'outputOriginX', 'outputOriginX', 'tlX', 'tlY', 'nestlevel']; var OBJ_PROPS = ['tfParams']; var NEWLINE_PROPS = ['template']; // These will have a trailing slash added if needed. var DIR_PROPS = ['relativePath', 'basePath']; // These will be set to config data if not set. var CONFIG_PROP_DEFAULTS = baseConfigData; var VALID_OUTPUT_EXTS = ['jpg', 'png', 'gif']; var CONFIG_LAYERCOMP_NAME = '{choppy}'; var SIZE_FILEHANDLE_PLACEHOLDER = '{s}'; var TEMPLATE_VAR_PRE = '%'; var TEMPLATE_VAR_POST = '%'; // What chars separate multiple templates when defined with |template| var var TEMPLATE_MULTI_SEP = ','; var REG_LAYER_NAME = '{reg}'; var BOUNDS_COMP_NEXT_KEYWORD = '{next}' var BOUNDS_COMP_PREV_KEYWORD = '{prev}' var DEFAULT_PRE = ['cleanup-comps','nestlevel-shorthand','core-shorthand','parent-from-nestlevel','tf']; var DEFAULT_POST = []; // Get psd info var psdContainingDir = ensureDirPathHasTrailingSlash(originalDoc.path, pathSep); var psdName = originalDoc.name; var psdFullName = originalDoc.fullName; var psdBounds = new Array(0,0,doc.width.value,doc.height.value); // Assuming doc has an extension var psdNameParts = psdName.split('.'); psdNameParts.splice(psdNameParts.length - 1, 1); var psdBase = psdNameParts.join('.'); PROP_DEFAULTS['psdBase'] = psdBase; PROP_DEFAULTS['psdWidth'] = doc.width.value; PROP_DEFAULTS['psdHeight'] = doc.height.value; if (!doc){ throw new Error('No PSD document open.') } app.togglePalettes(); var configData = null; var outputData = []; var compNameCheck = {} var dupeCompNamesPresent = false; var delLayerComps = []; var processLayerComps = function(configMode){ var _configData = null; for (var i =0; i < doc.layerComps.length; i++){ var layerComp = doc.layerComps[i]; if (dupeCompNamesPresent){ // Do nothing } else if (compNameCheck[layerComp.name]){ dupeCompNamesPresent = true; } else { compNameCheck[layerComp.name] = true; } var compData = {}; var totalProps = 0; compData._nextLayerCompName = ''; if (i > 0){ compData._prevLayerCompName = doc.layerComps[i-1].name; } if (i < doc.layerComps.length-1){ compData._nextLayerCompName = doc.layerComps[i+1].name; } if (IGNORE_PREFIX_CHARS[layerComp.name.charAt(0)]){ delLayerComps.push(layerComp); } else if ((configMode && layerComp.name === CONFIG_LAYERCOMP_NAME) || (!configMode && layerComp.name !== CONFIG_LAYERCOMP_NAME)){ // Parse comment vars if (layerComp.comment){ var arr = layerComp.comment.split('\n'); for (var j = 0; j < arr.length; j++){ var chunk = arr[j]; var chunkArr = chunk.split(':'); if (chunkArr.length > 1){ var varName = chunkArr[0].split(' ').join(''); chunkArr.splice(0,1); var varVal = chunkArr.join(':'); while(varVal.charAt(0) === ' '){ varVal = varVal.substr(1); } if (BOOL_PROPS.indexOf(varName) >= 0){ var tmpVarVal = varVal.toLowerCase(0); varVal = false; if (tmpVarVal.charAt(0) === 't' || tmpVarVal.charAt(0) === 'y' || tmpVarVal.charAt(0) === '1'){ varVal = true; } } else if (NUM_PROPS.indexOf(varName) >= 0){ var tmpVarVal = Number(eval(varVal)); if (!isNaN(tmpVarVal)){ varVal = tmpVarVal; } } else if (NEWLINE_PROPS.indexOf(varName) >= 0){ varVal = String(varVal).split('\\n').join('\n'); varVal = String(varVal).split('\\t').join('\t'); } else if (OBJ_PROPS.indexOf(varName) >= 0){ try { var varVal = JSON.parse(varVal); } catch(err) { varVal = PROP_DEFAULTS[varName] } } compData[varName] = varVal; totalProps++; } } } // Default prop-less comment to alt text if (totalProps == 0){ compData = {alt: layerComp.comment}; } // Check relative path if (compData['relativePath']){ compData['relativePath'] = ensureDirPathHasTrailingSlash(compData['relativePath'].split('/').join(pathSep), pathSep); } if (!compData['placeholder'] && (!compData['type'] || compData['type'].length == 0)){ compData['type'] = 'img'; } // The `name` should be the base now. //if (!configMode && layerComp.name !== CONFIG_LAYERCOMP_NAME){ // layerComp.name = compData['prefix'] + layerComp.name + configData['suffix'] //} compData['base'] = layerComp.name; compData.layerCompRef = layerComp; if (layerComp.name === CONFIG_LAYERCOMP_NAME){ if (configMode){ delete compData['base']; _configData = compData; delLayerComps.push(layerComp); } } else { compData.selected = layerComp.selected; outputData.push(compData); } } } if (configMode){ // Delete comment comps for (var i = 0; i < delLayerComps.length; i++){ delLayerComps[i].remove(); } return _configData; } } // Config data // ----------- // Load config data only configData = processLayerComps(true); // Extend config data if (configData === null){ configData = extendObjWithDefaults({}, CONFIG_PROP_DEFAULTS); } else { configData = extendObjWithDefaults(configData, CONFIG_PROP_DEFAULTS); } // Apply defaults to config so they can extend each output img. configData = extendObjWithDefaults(configData, PROP_DEFAULTS); // Read local pre/post JSX script names (based on {choppy} layer (and .choppy json)) var pre = DEFAULT_PRE; if (configData.pre && typeof configData.pre === 'string' && configData.pre.length > 0){ pre = pre.concat(configData.pre.split(',')); // Push certain scripts to the start. var index = pre.indexOf('layers-to-comps') if (index > 0) { pre.splice(index, 1); pre.unshift('layers-to-comps'); } } var post = DEFAULT_POST; if (configData.post && typeof configData.post === 'string' && configData.post.length > 0){ post = post.concat(configData.post.split(',')); } // Call `pre` hooks var anyDebugOutput = false; for (var i = 0; i < pre.length; i++){ var jsxScriptName = pre[i]; var extParts = jsxScriptName.split('.'); if (extParts.length > 1 && extParts[1].toLowerCase() == 'jsx'){ // Remove ext if set jsxScriptName = jsxScriptName.substr(0, jsxScriptName.length-1); } if (!jsxPaths.pre[jsxScriptName]){ throw new Error('JSX `pre` script not found `'+jsxScriptName+'`'); } var jsxPath = jsxPaths.pre[jsxScriptName]; var jsxFile = new File(jsxPath); if (!jsxFile.exists){ throw new Error('JSX `pre` file not found `'+jsxScriptName+'`'); } var halt = false; var outputDebug = DEFAULT_PRE.indexOf(jsxScriptName) == -1; if (outputDebug){ anyDebugOutput=true; stream.writeln('debug:Running *pre* hook `'+jsxScriptName+'`...'); } $.evalFile(jsxFile); if (halt){ return; } } if (anyDebugOutput){ stream.writeln('debug:*pre* hooks complete OK.'); } // Load layercomps w/comments into outputData processLayerComps(); takeSnapshot(); // Flip order of output if (configData.reverseOrder){ outputData.reverse(); } if (configData.wipeRelativePath.length > 0 ){ var wipeRelativePaths = configData.wipeRelativePath.split(','); for (var i = 0; i < wipeRelativePaths.length; i++){ var wipeRelativePath = wipeRelativePaths[i]; wipeRelativePath = applyVarObjToTemplateString(configData, wipeRelativePath, TEMPLATE_VAR_PRE, TEMPLATE_VAR_POST, 1, []); wipeRelativePath = ensureDirPathHasTrailingSlash(wipeRelativePath, pathSep); configData.basePath = ensureDirPathHasTrailingSlash(configData.basePath, pathSep); var wipeDir = new Folder(psdContainingDir + configData.basePath + wipeRelativePath); if (wipeDir.exists){ deleteImgsFromDir(wipeDir); } } } // The number of templates must be defined in {choppy} or base config file. var outputTemplates = configData.template.split(TEMPLATE_MULTI_SEP); var splitProps = ['outputFilePath', 'outputTagStart', 'outputTagEnd']; for (var s1 = 0; s1 < splitProps.length; s1++){ var splitPropName = splitProps[s1]; if (configData[splitPropName]){ configData[splitPropName] = configData[splitPropName].split(TEMPLATE_MULTI_SEP); if (configData[splitPropName].length < outputTemplates.length){ for (var s2 = 0; s2 < outputTemplates.length; s2++){ if (s2 >= configData[splitPropName].length){ configData[splitPropName][s2] = configData[splitPropName][0]; } } } } } // These are declared here because they need to be accessed after the loop // Track which parts you've added so you don't add stuff like '.header.' more than once var output = []; var templatePartsAdded = []; for (var oct = 0; oct < outputTemplates.length; oct++){ output.push({}); templatePartsAdded.push({}); } // Cache custom template `parse` functions var templateParseFnCache = {}; var p; for (p = 0; p < outputData.length; p++){ outputData[p] = extendObjWithDefaults(outputData[p], configData); // Enforce own "!" props. outputData[p] = extendObjWithDefaults(outputData[p], outputData[p]); // Apply prefix and suffix outputData[p].base = outputData[p].prefix + outputData[p].base + outputData[p].suffix // Look for multiple sizes and create new virtual layerComps for these if (outputData[p].sizes) { // Look for size definitions var sizeLookup = {}; var ppp; for (ppp in outputData[p]){ var pppOrig = ppp; ppp = ppp.toLowerCase(); if (ppp.length > 7 && ppp.substr(0,7) === 'sizedef'){ var sizeKey = ppp.substr(7); sizeLookup[sizeKey] = outputData[p][pppOrig]; } } // Look for sizeDefs that reference other size defs var sizeDefsDefined = {}; var iii = 0; var anyUndef = true; // Recurse until all defined (limit 10) if (iii < 10 && anyUndef){ for (ppp in sizeLookup){ if (!sizeDefsDefined[ppp]){ anyUndef = true; var arrSizesInDef = sizeLookup[ppp].split(','); var arrAltered = false; var totUndefProps = 0; for (var ii = 0; ii < arrSizesInDef.length; ii++){ if (arrSizesInDef[ii].split(':').length === 1){ totUndefProps++; var lookupDef = String(arrSizesInDef[ii]).toLowerCase(); if (sizeLookup[lookupDef] && sizeDefsDefined[lookupDef]){ arrSizesInDef[ii] = sizeLookup[lookupDef]; arrAltered = true; } else { break; } } } if (totUndefProps === 0 || arrAltered){ sizeLookup[ppp] = arrSizesInDef.join(','); sizeDefsDefined[ppp] = true; } } } iii++; } var tmpVarValArr = String(outputData[p].sizes).split(','); outputData[p].sizes = []; for (var pp = 0; pp < tmpVarValArr.length; pp++){ var strObj = tmpVarValArr[pp]; if (strObj.split(':').length === 1){ // Inject size def var sizeDef = String(strObj).toLowerCase(); if (sizeLookup[sizeDef]){ var sizePushArr = sizeLookup[sizeDef].split(','); for (var jj = 0; jj < sizePushArr.length; jj++){ strObj = sizePushArr[jj]; outputData[p].sizes.push({fileHandle:strObj.split(':')[0], scale: Number(eval(strObj.split(':')[1])), def:sizeDef}); } } else { throw new Error('Size definition "'+ppp+'" not found'); } } else { outputData[p].sizes.push({fileHandle:strObj.split(':')[0], scale: Number(eval(strObj.split(':')[1]))}); } } if (outputData[p].sizes.length > 0){ // Save the layer comp as the ref will not survive duplicating var layerCompRef = outputData[p].layerCompRef; delete outputData[p].layerCompRef; for (var s = 0; s < outputData[p].sizes.length; s++){ if (!outputData[p].sizes[s]['scale']){ throw new Error('Invalid size scale ['+s+']'); } var dataObj; if (s === 0){ dataObj = outputData[p]; } else { dataObj = dupeObj(outputData[p]); } dataObj.layerCompRef = layerCompRef; dataObj.sizeIndex = s; dataObj.scale = eval(outputData[p].sizes[s]['scale']); dataObj.sizeFileHandle = outputData[p].sizes[s]['fileHandle']; if (s > 0){ outputData.splice(p + s,0,dataObj); } } if (outputData[p].sizes.length > 1){ p+=outputData[p].sizes.length-1; } } } } var layerBoundsCacheLayers = {}; var cacheLayerCompBounds = !dupeCompNamesPresent; var layerCompBoundsCache = {}; var exportDirs = []; var exportDirsTaken = {}; for (p = 0; p < outputData.length; p++){ outputData[p].index = p; // Remove ! props that have a double up. // Remove ! for those that don't. var delProps = []; for (var q in outputData[p]){ if (q.charAt(0) == "!"){ if (qNoEx == q.substr(1)){ // Was single if (!outputData[p][qNoEx]){ outputData[p][qNoEx] = outputData[p][q]; } delProps.push(q); } } } for (var qq = 0; qq < delProps.length ; qq++){ delete outputData[p][delProps[qq]]; } // Make sure trailing slashes are present. ensureDirPropsHaveTrailingSlash(outputData[p], DIR_PROPS, pathSep); // If no alt text then clean up base and use as a fallback. if (!outputData[p].alt || outputData[p].alt.length == 0){ outputData[p].alt = ''; //filenameBaseToAltText(outputData[p].base); } outputData[p].base = cleanUpFileNameBase(outputData[p].base); // applyVarObjToTemplateString(obj,str, pre, post, outputValueFactor, OUTPUT_VALUE_FACTOR_PROPS, roundOutputValues){ outputData[p].relativePath = applyVarObjToTemplateString(outputData[p], outputData[p].relativePath, TEMPLATE_VAR_PRE, TEMPLATE_VAR_POST, outputData[p].outputValueFactor, OUTPUT_VALUE_FACTOR_PROPS, outputData[p].roundOutputValues) // Now you have enough to get src // Inject file size handle into output var fileNameSizeHandlePart = ''; if (outputData[p].sizeIndex >= 0){ var foundFileHandlePlaceholder = false; if (outputData[p].base.split(SIZE_FILEHANDLE_PLACEHOLDER).length == 2){ foundFileHandlePlaceholder = true; outputData[p].base = outputData[p].base.split(SIZE_FILEHANDLE_PLACEHOLDER).join(outputData[p].sizeFileHandle); } if (outputData[p].relativePath.split(SIZE_FILEHANDLE_PLACEHOLDER).length == 2){ foundFileHandlePlaceholder = true; outputData[p].relativePath = outputData[p].relativePath.split(SIZE_FILEHANDLE_PLACEHOLDER).join(outputData[p].sizeFileHandle); } if (!foundFileHandlePlaceholder){ fileNameSizeHandlePart = outputData[p].sizeFileHandle; } } outputData[p].base = outputData[p].base.split(SIZE_FILEHANDLE_PLACEHOLDER).join(''); outputData[p].base = outputData[p].base + fileNameSizeHandlePart outputData[p].relativePath = outputData[p].relativePath.split(SIZE_FILEHANDLE_PLACEHOLDER).join(''); outputData[p].srcFileName = outputData[p].base + '.' + outputData[p].ext; outputData[p].src = outputData[p].placeholder ? '' : outputData[p].relativePath + outputData[p].srcFileName; outputData[p].exportPath = psdContainingDir + outputData[p].basePath + outputData[p].relativePath + outputData[p].srcFileName; if (outputData[p].makeDir){ var containingDir = new Folder(psdContainingDir + outputData[p].basePath + outputData[p].relativePath); if (!containingDir.exists){ containingDir.create(); } } // Save a running list of all export directory paths var exportDirPath = psdContainingDir + outputData[p].basePath + outputData[p].relativePath; if (!exportDirsTaken[exportDirPath]){ exportDirs.push(exportDirPath); exportDirsTaken[exportDirPath] = true; } // outputOriginLayer // ----------------- if (outputData[p].outputOriginLayer != null && outputData[p].outputOriginLayer.length > 0){ if (layerBoundsCacheLayers[outputData[p].outputOriginLayer]){ outputData[p].outputOriginX = layerBoundsCacheLayers[outputData[p].outputOriginLayer][0]; outputData[p].outputOriginY = layerBoundsCacheLayers[outputData[p].outputOriginLayer][1]; } else { var alteredDoc = false; var foundLayer = false; for (var i = 0 ; i < doc.layers.length; i++){ var lyr = doc.layers[i]; if (lyr.name == outputData[p].outputOriginLayer){ if (foundLayer){ throw new Error('|outputOriginLayer| "'+outputData[p].outputOriginLayer+'" is not unique'); } lyr.visible = true; foundLayer = true; alteredDoc = flattenTopLevelLayerAtIndex(i); } else { lyr.visible = false; } } if (!foundLayer){ throw new Error('|outputOriginLayer| "'+outputData[p].outputOriginLayer+'" not found'); } else { var layerBounds = getVisibleBounds(doc); outputData[p].outputOriginX = layerBounds[0]; outputData[p].outputOriginY = layerBounds[1]; layerBoundsCacheLayers[outputData[p].outputOriginLayer] = layerBounds; revertSnapshot(doc); } } } // boundsComp // ---------- var boundsCompBounds = null; if (outputData[p].boundsComp && outputData[p].boundsComp.length > 0){ var targetLayerCompName; if (outputData[p].boundsComp == BOUNDS_COMP_PREV_KEYWORD){ targetLayerCompName = outputData[p]._prevLayerCompName } else if (outputData[p].boundsComp == BOUNDS_COMP_NEXT_KEYWORD){ targetLayerCompName = outputData[p]._nextLayerCompName } else { targetLayerCompName = outputData[p].boundsComp } var cropToBoundsTarget = doc.layerComps.getByName(targetLayerCompName); if (cropToBoundsTarget){ if (!outputData[p].cropToBounds){ outputData[p].cropToBounds = true; } if (cacheLayerCompBounds && layerCompBoundsCache[cropToBoundsTarget.name]){ boundsCompBounds = copyBounds(layerCompBoundsCache[cropToBoundsTarget.name]); } else { cropToBoundsTarget.apply(); // Hide reg layer so it's not included in bounds for (var r = 0 ; r < doc.layers.length; r++){ var lyr = doc.layers[r]; if (lyr.visible && lyr.name && lyr.name.length >= REG_LAYER_NAME.length && lyr.name.substr(0,REG_LAYER_NAME.length) === REG_LAYER_NAME){ lyr.visible = false; } } flattenTopLevelLayers(doc, true, false, null, IGNORE_PREFIX_CHARS); boundsCompBounds = getVisibleBounds(doc); revertSnapshot(doc); layerCompBoundsCache[cropToBoundsTarget.name] = copyBounds(boundsCompBounds); } } } // Apply layer comp var layerComp = outputData[p].layerCompRef; layerComp.apply(); // Look for reg point layer, record position and hide var regPt = null; for (var r = 0 ; r < doc.layers.length; r++){ var lyr = doc.layers[r]; if (lyr.visible && lyr.name && lyr.name.length >= REG_LAYER_NAME.length && lyr.name.substr(0,REG_LAYER_NAME.length) === REG_LAYER_NAME){ lyr.visible = false; regPt = getLayerCenterPoint(lyr); outputData[p].reg = '(custom)'; break; } } var revertRequired = false; var outputBounds; if (outputData[p].cropToBounds){ if (boundsCompBounds){ outputBounds = boundsCompBounds; // References a comp to get bounds } else { if (cacheLayerCompBounds && layerCompBoundsCache[layerComp.name]){ boundsCompBounds = copyBounds(layerCompBoundsCache[layerComp.name]); } else { // Only flatten if cropping flattenTopLevelLayers(doc, true, false, null, IGNORE_PREFIX_CHARS); revertRequired = true; // A revert is required whether a dry-run or not outputBounds = getVisibleBounds(doc); if (outputData[p].forceGrid > 0){ outputBounds[0] = Math.floor(outputBounds[0]/outputData[p].forceGrid) * outputData[p].forceGrid; outputBounds[1] = Math.floor(outputBounds[1]/outputData[p].forceGrid) * outputData[p].forceGrid; outputBounds[2] = Math.ceil(outputBounds[2]/outputData[p].forceGrid) * outputData[p].forceGrid; outputBounds[3] = Math.ceil(outputBounds[3]/outputData[p].forceGrid) * outputData[p].forceGrid; } } } } else { outputBounds = copyBounds(psdBounds); } var cropBounds = copyBounds(outputBounds) outputData[p].outputBounds = outputBounds; // Resize bounds to accomodate cropping - // this way when the canvas is resized to `forceW` / `forceH` the reg point (if based on bounds) will be in the correct position. var forceW; var forceH; if (outputData[p].forceW > 0 || outputData[p].forceH > 0){ var cropW = getBoundsWidth(cropBounds); var cropH = getBoundsHeight(cropBounds); forceW = outputData[p].forceW > 0 ? outputData[p].forceW : cropW; forceH = outputData[p].forceH > 0 ? outputData[p].forceH : cropH; // Assuming canvas resize will be middle center outputBounds[0] = Math.round(outputBounds[0] - (forceW - cropW) * 0.5); outputBounds[1] = Math.round(outputBounds[1] - (forceH - cropH) * 0.5); outputBounds[2] = Math.round(outputBounds[2] + (forceW - cropW) * 0.5); outputBounds[3] = Math.round(outputBounds[3] + (forceH - cropH) * 0.5); } if (!regPt && outputData[p].reg !== '(custom)'){ // Interpret 'reg' data regPt = getRegPtFromRegStringAndBounds(outputData[p].reg, outputData[p].outputBounds); } if (!regPt){ regPt = {x: parseInt(outputBounds[0], 10), y: parseInt(outputBounds[1], 10)}; } // Eg. convert `BC` to 0.5,1.0: // var gridRegScalar = getRegPtFromRegStringAndBounds('C', [0,0,1,1]); // alert(gridRegScalar.x + ',' + gridRegScalar.y) outputData[p].tlX = outputData[p].outputBounds[0] - outputData[p].outputOriginX; outputData[p].tlY = outputData[p].outputBounds[1] - outputData[p].outputOriginY; // Apply reg point data outputData[p].x = regPt.x - outputData[p].outputOriginX; outputData[p].y = regPt.y - outputData[p].outputOriginY; if (outputData[p].tfParams){ outputData[p].tfParams.visBoundsTLX = outputData[p].tfParams.visBoundsTLX - regPt.x;// - outputData[p].outputOriginX; outputData[p].tfParams.visBoundsTLY = outputData[p].tfParams.visBoundsTLY - regPt.y;// - outputData[p].outputOriginY; outputData[p].tfParams.boxW = Math.round(outputData[p].tfParams.boxW); outputData[p].tfParams.boxH = Math.round(outputData[p].tfParams.boxH); } outputData[p].regX = regPt.x - outputData[p].outputBounds[0]; outputData[p].regY = regPt.y - outputData[p].outputBounds[1]; outputData[p].regPercX = (regPt.x - outputData[p].outputBounds[0]) / (outputData[p].outputBounds[2] - outputData[p].outputBounds[0]); outputData[p].regPercY = (regPt.y - outputData[p].outputBounds[1]) / (outputData[p].outputBounds[3] - outputData[p].outputBounds[1]) outputData[p].width = outputBounds[2]-outputBounds[0]; //String(parseInt(outputBounds[2],10)-parseInt(outputBounds[0],10)); outputData[p].height = outputBounds[3]-outputBounds[1]; //String(parseInt(outputBounds[3],10)-parseInt(outputBounds[1],10)); if (!outputData[p].placeholder){ if (!areBoundsEqual(psdBounds, outputData[p].outputBounds)){ revertRequired = true; doc.crop(cropBounds); } if (outputData[p].forceW > 0 || outputData[p].forceH > 0){ doc.resizeCanvas(UnitValue(forceW,"px"),UnitValue(forceH,"px"), AnchorPosition.MIDDLECENTER); // Docs: https://theiviaxx.github.io/photoshop-docs/Photoshop/AnchorPosition.html } if (outputData[p].flipX){ revertRequired = true; doc.flipCanvas(Direction.HORIZONTAL); } if (outputData[p].flipY){ revertRequired = true; doc.flipCanvas(Direction.VERTICAL); } if (outputData[p].scale != 1){ revertRequired = true; doc.resizeImage(UnitValue(doc.width.value * outputData[p].scale,"px"),null,null,ResampleMethod.BICUBIC); // Update dims outputData[p].width = String(doc.width.value); outputData[p].height = String(doc.height.value); outputData[p].x = outputData[p].x * outputData[p].scale; outputData[p].y = outputData[p].y * outputData[p].scale; outputData[p].regX = outputData[p].regX * outputData[p].scale; outputData[p].regY = outputData[p].regY * outputData[p].scale; outputData[p].tlX = outputData[p].tlX * outputData[p].scale; outputData[p].tlY = outputData[p].tlY * outputData[p].scale; } // Ouput image var exportOptions = new ExportOptionsSaveForWeb(); if (outputData[p].matte){ var userMatteColor = hexToRGB(outputData[p].matte); var matteColor = new RGBColor(); matteColor.red = userMatteColor.r; matteColor.green = userMatteColor.g; matteColor.blue = userMatteColor.b; exportOptions.matteColor = matteColor; } if (outputData[p].ext == 'png'){ exportOptions.PNG8 = false; exportOptions.transparency = true; exportOptions.interlaced = false; exportOptions.quality = 100; exportOptions.includeProfile = false; exportOptions.format = SaveDocumentType.PNG;//-8; //SaveDocumentType.PNG; //-24 //JPEG, COMPUSERVEGIF, PNG-8, BMP } else if (outputData[p].ext == 'jpg'){ //exportOptions = new ExportOptionsSaveForWeb(); exportOptions.format = SaveDocumentType.JPEG; exportOptions.quality = outputData[p].quality; } else if (outputData[p].ext == 'gif'){ exportOptions.ditherAmount = 0; exportOptions.dither = Dither.NOISE; // Dither.NONE; // exportOptions.palette = Palette.LOCALPERCEPTUAL; //Palette.LOCALADAPTIVE; exportOptions.format = SaveDocumentType.COMPUSERVEGIF; exportOptions.forced = ForcedColors.BLACKWHITE; exportOptions.interlaced = false; exportOptions.preserverExactColors = true; exportOptions.colors = outputData[p].colors; exportOptions.transparency = true; } else { throw new Error('Export format "'+outputData[p].ext+'" not found.'); } //alert(outputData[p].exportPath); doc.exportDocument(new File(outputData[p].exportPath), ExportType.SAVEFORWEB, exportOptions); } else { if (outputData[p].scale != 1){ // Update dims outputData[p].width = String(Math.round(Number(outputData[p].width) * outputData[p].scale)); outputData[p].height = String(Math.round(Number(outputData[p].height) * outputData[p].scale)); outputData[p].x = outputData[p].x * outputData[p].scale; outputData[p].y = outputData[p].y * outputData[p].scale; outputData[p].regX = outputData[p].regX * outputData[p].scale; outputData[p].regY = outputData[p].regY * outputData[p].scale; } } if (revertRequired){ revertSnapshot(doc); } } // Make each child x,y relative to parent's x,y var nestLookup = []; for (var mm = 0; mm < outputData.length; mm++){ if (!nestLookup[outputData[mm].nestlevel]){ nestLookup[outputData[mm].nestlevel] = {}; } nestLookup[outputData[mm].nestlevel][outputData[mm].base] = mm; } if (nestLookup.length > 1){ for (var nn = nestLookup.length - 1; nn >= 1; nn--){ for (var base in nestLookup[nn]){ var child = outputData[nestLookup[nn][base]]; var parent = outputData[nestLookup[nn-1][child.parent]]; outputData[nestLookup[nn][base]].x -= parent.x; outputData[nestLookup[nn][base]].y -= parent.y; } } } // Inject output data into template for (p = 0; p < outputData.length; p++){ var compTemplateSplit = outputData[p].template.split(TEMPLATE_MULTI_SEP); if (compTemplateSplit.length != outputTemplates.length){ throw new Error('Layer comp template list must be same length as config comp'); } for (var ote = 0; ote < compTemplateSplit.length; ote++){ var template = compTemplateSplit[ote]; // from config var templateIsRawString = template.split(TEMPLATE_VAR_PRE).length > 1 && template.split(TEMPLATE_VAR_POST).length > 1; var templateFound = false; if (tplData[template] !== undefined){ for (var t = 0; t < TEMPLATE_PARTS.length; t++){ var part = TEMPLATE_PARTS[t]; if (tplData[template][part] !== undefined){ if (part == 'parse'){ // Load function if needed and cache for next output item if (typeof templateParseFnCache[template] === 'undefined'){ var parse = null; $.evalFile(tplData[template][part]); if (parse === null){ throw new Error('Custom `parse` function not found at `'+tplData[template][part]+'`') } templateParseFnCache[template] = parse; } } else { if (output[ote][part] === undefined){ output[ote][part] = []; } templateFound = true; if (part == 'main'){ var mainData = applyVarObjToTemplateString(outputData[p], tplData[template][part], TEMPLATE_VAR_PRE, TEMPLATE_VAR_POST, outputData[p].outputValueFactor, OUTPUT_VALUE_FACTOR_PROPS, outputData[p].roundOutputValues, templateParseFnCache[template]); if (tplData[template]['inter'] !== undefined && p > 0){ mainData = tplData[template]['inter'] + mainData; } output[ote][part].push(mainData); } else if (part != 'inter'){ // Only output once var templatePartID = template + ':' + part; if (!templatePartsAdded[ote][templatePartID]){ var templateContent = tplData[template][part]; if (configData){ templateContent = applyVarObjToTemplateString(configData, templateContent, TEMPLATE_VAR_PRE, TEMPLATE_VAR_POST, 1, [], false, templateParseFnCache[template]); } output[ote][part].push(templateContent); templatePartsAdded[ote][templatePartID] = true; } } } } } } if (!templateFound && templateIsRawString){ if (output[ote]['main'] === undefined){ output[ote]['main'] = []; } output[ote]['main'].push(applyVarObjToTemplateString(outputData[p], template, TEMPLATE_VAR_PRE, TEMPLATE_VAR_POST, outputData[p].outputValueFactor, OUTPUT_VALUE_FACTOR_PROPS, outputData[p].roundOutputValues)); } } } // Revert doc revertSnapshot(doc); doc.close(SaveOptions.DONOTSAVECHANGES); // Close dupe doc doc = originalDoc; var responseDataAll = []; // Loop templates for (var ote = 0; ote < outputTemplates.length; ote++){ // Make output out of template data var outputString = ''; for (var t = 0; t < TEMPLATE_PARTS.length; t++){ if (output[ote][TEMPLATE_PARTS[t]] !== undefined){ outputString += output[ote][TEMPLATE_PARTS[t]].join(''); } } // Look for output file path var outputFilePath = ''; var outputTags = {}; if (configData && configData.outputFilePath && configData.outputFilePath[ote].length > 0){ configData.basePath = ensureDirPathHasTrailingSlash(configData.basePath, pathSep); // You can inject config props into this path var injectedOutputFilePath = applyVarObjToTemplateString(configData, configData.outputFilePath[ote], TEMPLATE_VAR_PRE, TEMPLATE_VAR_POST, 1, []); outputFilePath = configData.basePath + injectedOutputFilePath; //configData.outputFilePath; if (configData.outputTagStart && configData.outputTagEnd && configData.outputTagStart[ote].length > 0 && configData.outputTagEnd[ote].length > 0){ outputTags = {start:configData.outputTagStart[ote], end:configData.outputTagEnd[ote]}; } } // Write response back to node var responseData = {outputData: cleanupOutputDataForOutput(outputData,['layerCompRef']), outputString: outputString, outputFilePath: outputFilePath, outputTags: outputTags} responseDataAll.push(responseData); } // Call `post` hooks var anyDebugOutput = false; for (var i = 0; i < post.length; i++){ var jsxScriptName = post[i]; var extParts = jsxScriptName.split('.'); if (extParts.length > 1 && extParts[1].toLowerCase() == 'jsx'){ // Remove ext if set jsxScriptName = jsxScriptName.substr(0, jsxScriptName.length-1); } if (!jsxPaths.post[jsxScriptName]){ throw new Error('JSX `post` script not found `'+jsxScriptName+'`'); } var jsxPath = jsxPaths.post[jsxScriptName]; var jsxFile = new File(jsxPath); if (!jsxFile.exists){ throw new Error('JSX `post` file not found `'+jsxScriptName+'`'); } // No halt: script concluding anyway. var outputDebug = DEFAULT_POST.indexOf(jsxScriptName) == -1; if (outputDebug){ anyDebugOutput = true; stream.writeln('debug:Running *post* hook `'+jsxScriptName+'`...'); } $.evalFile(jsxFile); } if (anyDebugOutput){ stream.writeln('debug:*post* hooks complete OK.'); } // Call `postExecutePath` if set. if (configData.postExecutePath && trim(configData.postExecutePath).length > 0){ var exePaths = trim(configData.postExecutePath).split(','); for (var ep = 0; ep < exePaths.length; ep++){ var exePath = trim(exePaths[ep]) var execFile = new File(psdContainingDir + exePath); if (!execFile.exists){ alert('postExecutePath not found: `'+psdContainingDir + exePath+'`'); } else { stream.writeln('debug:Executing `'+exePath+'`'); execFile.execute(); } } } app.togglePalettes(); stream.writeln( JSON.stringify(responseDataAll) ); // JSX functions // ------------- // folderRef = Folder(pathDocSrc.fullName.parent + '/' + docBaseName + '/' + IMG_FOLDER_NAME); function deleteImgsFromDir(dir){ if (!dir.exists){ alert('Img dir not found: `'+dir.fullName+'`'); } var files = dir.getFiles(/.+\.(?:gif|jpg|jpeg|bmp|png)$/i); for (var i = 0; i < files.length; i++){ files[i].remove() } } function getSelectedLayerLookup(layerRef){ var lookup = {}; // var selLayers = _getSelectedLayers(); // for (var zzz = 0; zzz < selLayers.length; zzz++){ // lookup['name:' + selLayers[zzz].name + ',parent:' + selLayers[zzz].parent.name] = true // } return lookup; } function isLayerSelected(layerRef, lookup){ return lookup['name:' + layerRef.name + ',parent:' + layerRef.parent.name]; } function _getSelectedLayers(){ var idGrp = stringIDToTypeID( "groupLayersEvent" ); var descGrp = new ActionDescriptor(); var refGrp = new ActionReference(); refGrp.putEnumerated(charIDToTypeID( "Lyr " ),charIDToTypeID( "Ordn" ),charIDToTypeID( "Trgt" )); descGrp.putReference(charIDToTypeID( "null" ), refGrp ); executeAction( idGrp, descGrp, DialogModes.ALL ); var resultLayers=new Array(); for (var ix=0;ix<app.activeDocument.activeLayer.layers.length;ix++){resultLayers.push(app.activeDocument.activeLayer.layers[ix])} var id8 = charIDToTypeID( "slct" ); var desc5 = new ActionDescriptor(); var id9 = charIDToTypeID( "null" ); var ref2 = new ActionReference(); var id10 = charIDToTypeID( "HstS" ); var id11 = charIDToTypeID( "Ordn" ); var id12 = charIDToTypeID( "Prvs" ); ref2.putEnumerated( id10, id11, id12 ); desc5.putReference( id9, ref2 ); executeAction( id8, desc5, DialogModes.NO ); return resultLayers; } function dupeObj(obj){ return JSON.parse(JSON.stringify(obj)); } function hexToRGB(hex){ var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null; } function getExt(strPathOrFileName){ var ext = ''; strPathOrFileName = String(strPathOrFileName); var arr = strPathOrFileName.split('.'); if (arr.length > 1){ ext = arr[arr.length - 1]; } return ext; } function cleanUpFileNameBase(base){ //base = base.toLowerCase(); //