1 //------------------------------------------------------------------------------
  2 // File: jamStyles.jsxinc
  3 // Version: 4.5
  4 // Release Date: 2016-09-29
  5 // Copyright: © 2011-2016 Michel MARIANI <http://www.tonton-pixel.com/blog/>
  6 // Licence: GPL <http://www.gnu.org/licenses/gpl.html>
  7 //------------------------------------------------------------------------------
  8 // This program is free software: you can redistribute it and/or modify
  9 // it under the terms of the GNU General Public License as published by
 10 // the Free Software Foundation, either version 3 of the License, or
 11 // (at your option) any later version.
 12 // 
 13 // This program is distributed in the hope that it will be useful,
 14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 // GNU General Public License for more details.
 17 // 
 18 // You should have received a copy of the GNU General Public License
 19 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 20 //------------------------------------------------------------------------------
 21 // Version History:
 22 //  4.5:
 23 //  - Improved error handling.
 24 //  4.4.2:
 25 //  - Added ignoreScale parameter to function jamStyles.setLayerStyle ().
 26 //  - Caught exception in function jamStyles.getLayerStyle () when it is not 
 27 //    possible to apply styles/effects to a layer group (before Photoshop CS6).
 28 //  4.4:
 29 //  - Normalized error messages.
 30 //  4.2:
 31 //  - Cleaned up code.
 32 //  - Fixed bug in setLayerStyle (): in Photoshop CS, color space information 
 33 //    returned by getDocumentMode () includes depth as well!
 34 //    For instance: "RGB48" instead of "RGBColor".
 35 //  4.1:
 36 //  - Simplified test in jamStyles.isStylesPalette ().
 37 //  - Replaced decodeURI () with File.decode () for the sake of consistency.
 38 //  4.0:
 39 //  - Removed reference to 'this' for main global object.
 40 //  3.6:
 41 //  - Moved internal function simplifyObject () to public member of jamEngine.
 42 //  - Cleaned up code.
 43 //  3.5.4:
 44 //  - Added undocumented parameter to jamStyles.getLayerStyle () to get actual 
 45 //    patterns data as well.
 46 //  - Added functions jamStyles.patternsFromStylesFile () and
 47 //    jamStyles.patternsFileFromPatterns ().
 48 //  - Modified jamStyles.setApplicationGlobalAngle () and 
 49 //    jamStyles.setDocumentGlobalAngle (): globalAltitude is now optional.
 50 //  3.5.3:
 51 //  - Added handling of duotone document mode.
 52 //  - Fixed jamStyles.setLayerStyle (): the array "channelRestrictions" cannot
 53 //    be empty.
 54 //  - Added new functions:
 55 //    jamStyles.applyLayerStyle (),
 56 //    jamStyles.copyLayerStyle (),
 57 //    jamStyles.pasteLayerStyle (),
 58 //    jamStyles.scaleLayerEffects ().
 59 //  - Fixed jamStyles.setLayerStyle (): scaling must take into account the
 60 //    resolution of the document, to be consistent with the behavior of
 61 //    copyStyle () and pasteStyle ().
 62 //  - Added break statement in loop in getPresetStylesCount ().
 63 //  - Moved restored "chokeMatte" key to a value using percentUnit instead of
 64 //    pixelsUnit.
 65 //  - Normalized JSON AM data format for all calls to jamEngine.jsonPlay ().
 66 //  3.5.2:
 67 //  - Modified descriptor restoring functions: unknown fields are now ignored.
 68 //  - Added new functions:
 69 //    jamStyles.removeAllLayerEffects (),
 70 //    jamStyles.removeLayerEffects (),
 71 //    jamStyles.removeLayerEffect (),
 72 //    jamStyles.showHideAllDocumentEffects (),
 73 //    jamStyles.showHideAllLayerEffects (),
 74 //    jamStyles.showHideLayerEffects (),
 75 //    jamStyles.showHideLayerEffect (),
 76 //    jamStyles.setApplicationGlobalAngle (),
 77 //    jamStyles.setDocumentGlobalAngle ().
 78 //  3.5.1:
 79 //  - Modified jamStyles.setLayerStyle (): the effects property "scale" is taken
 80 //    into account.
 81 //  - Modified jamStyles.getLayerStyle (): the effects property "masterFXSwitch"
 82 //    is discarded, and a unique name is generated for each new temporary layer
 83 //    style.
 84 //  - Expanded jamHelpers.toChannelReference () inline to get rid of dependency.
 85 //  - Fixed a potential problem in jamStyles.dataFromStylesFile (): current
 86 //    values of jamEngine global options are now saved then restored after use.
 87 //  - Fixed imageModes array (5 and 6 are unsupported values).
 88 //  3.5:
 89 //  - Initial release.
 90 //------------------------------------------------------------------------------
 91 
 92 /**
 93  * @fileOverview
 94  * @name jamStyles.jsxinc
 95  * @author Michel MARIANI
 96  */
 97 
 98 //------------------------------------------------------------------------------
 99 
100 if (typeof jamStyles !== 'object')
101 {
102     /**
103      * Global object (used to simulate a namespace in JavaScript) containing
104      * a set of layer styles-related functions for scripts written with the
105      * <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-action-manager/">JSON Action Manager</a> engine.<br />
106      * Uses information found in the document
107      * <a href="http://www.tonton-pixel.com/Photoshop%20Additional%20File%20Formats/styles-file-format.html">Photoshop Styles File Format</a>.
108      * @author Michel MARIANI
109      * @version 4.5
110      * @namespace
111      */
112     var jamStyles = { };
113     //
114     (function ()
115     {
116         /**
117          * @description Test if a given file is a styles file (*.asl).
118          * @param {Object} file File object
119          * @returns {Boolean} true if styles file
120          * @see jamStyles.isStylesPalette
121          * @example
122          * function stylesFileFilter (f)
123          * {
124          *     return (f instanceof Folder) || jamStyles.<strong>isStylesFile</strong> (f);
125          * }
126          * var select = (File.fs === "Macintosh") ? stylesFileFilter : "Styles Files:*.asl,All Files:*";
127          * var stylesFile = File.openDialog ("Select a styles file:", select);
128          * if (stylesFile !== null)
129          * {
130          *     alert ("OK!");
131          * }
132          */
133         jamStyles.isStylesFile = function (file)
134         {
135             return (file.type === '8BSL') || file.name.match (/\.asl$/i);
136         };
137         //
138         /**
139          * @description Test if a given file is a styles palette file (Styles.psp).
140          * @param {Object} file File object
141          * @returns {Boolean} true if styles palette file
142          * @see jamStyles.isStylesFile
143          * @example
144          * function stylesPaletteFilter (f)
145          * {
146          *     return (f instanceof Folder) || jamStyles.<strong>isStylesPalette</strong> (f);
147          * }
148          * var select = (File.fs === "Macintosh") ? stylesPaletteFilter : "Styles Palette File:Styles.psp,All Files:*.*";
149          * var stylesPaletteFile = File.openDialog ("Select a styles palette file:", select);
150          * if (stylesPaletteFile !== null)
151          * {
152          *     alert ("OK!");
153          * }
154          */
155         jamStyles.isStylesPalette = function (file)
156         {
157             return file.name.match (/^Styles.psp$/i);
158         };
159         //
160         /**
161          * @description Get a layer effects object in JSON AM Data Format from a simplified layer effects JSON object.
162          * @param {Object} layerEffects Simplified layer effects JSON object
163          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/layer-effects-object-simplified-format/">Layer Effects Object Simplified Format</a>)
164          * @returns {Object|Array} Layer effects object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
165          * @see jamStyles.fromLayerEffectsObject
166          * @example
167          * var layerEffects =
168          * {
169          *     "scale": 100,
170          *     "dropShadow":
171          *     {
172          *         "enabled": true,
173          *         "mode": "multiply",
174          *         "color": { "red": 0, "green": 0, "blue": 0 },
175          *         "opacity": 70,
176          *         "localLightingAngle": 135,
177          *         "useGlobalAngle": false,
178          *         "distance": 10,
179          *         "chokeMatte": 20,
180          *         "blur": 15,
181          *         "transparencyShape":
182          *         {
183          *             "name": "Linear",
184          *             "curve":
185          *             [
186          *                 { "horizontal": 0, "vertical": 0 },
187          *                 { "horizontal": 255, "vertical": 255 }
188          *             ]
189          *         },
190          *         "antiAlias": false,
191          *         "noise": 8,
192          *         "layerConceals": true
193          *     }
194          * };
195          * jamEngine.jsonPlay
196          * (
197          *     "set",
198          *     {
199          *         "target":
200          *         {
201          *             "<reference>":
202          *             [
203          *                 { "property": { "<property>": "layerEffects" } },
204          *                 { "layer": { "<enumerated>": { "ordinal": "targetEnum" } } }
205          *             ]
206          *         },
207          *         "to": jamStyles.<strong>toLayerEffectsObject</strong> (layerEffects)
208          *     }
209          * );
210          */
211         jamStyles.toLayerEffectsObject = function (layerEffects)
212         {
213             function restoreDesc (desc, hintData)
214             {
215                 var restoredDesc = { };
216                 for (var key in desc)
217                 {
218                     if (desc.hasOwnProperty (key))
219                     {
220                         var value = desc[key];
221                         var typedValue = null;
222                         var restoredList;
223                         switch (key)
224                         {
225                             case "align":
226                             case "antiAlias":
227                             case "antialiasGloss":
228                             case "continuity":
229                             case "dither":
230                             case "enabled":
231                             case "invert":
232                             case "invertTexture":
233                             case "layerConceals":
234                             case "linked":
235                             case "reverse":
236                             case "showTransparency":
237                             case "useGlobalAngle":
238                             case "useShape":
239                             case "useTexture":
240                             case "vectorColor":
241                                 typedValue = [ "<boolean>", value ];
242                                 break;
243                             case "book":
244                             case "ID":
245                             case "name":
246                                 typedValue = [ "<string>", localize (value) ];
247                                 break;
248                             case "bookKey":
249                                 typedValue = [ "<data>", value ];
250                                 break;
251                             case "bookID":
252                             case "location":
253                             case "midpoint":
254                             case "randomSeed":
255                             case "smoothness":
256                                 typedValue = [ "<integer>", value ];
257                                 break;
258                             case "a":
259                             case "b":
260                             case "black":
261                             case "blue":
262                             case "brightness":
263                             case "cyan":
264                             case "gray":
265                             case "green":
266                             case "interpolation":
267                             case "luminance":
268                             case "magenta":
269                             case "red":
270                             case "saturation":
271                             case "yellowColor":
272                                 typedValue = [ "<double>", value ];
273                                 break;
274                             case "angle":
275                             case "hue":
276                             case "localLightingAngle":
277                             case "localLightingAltitude":
278                                 typedValue = [ "<unitDouble>", [ "angleUnit", value ] ];
279                                 break;
280                             case "chokeMatte":
281                             case "highlightOpacity":
282                             case "inputRange":
283                             case "noise":
284                             case "opacity":
285                             case "scale":
286                             case "shadingNoise":
287                             case "shadowOpacity":
288                             case "strengthRatio":
289                             case "textureDepth":
290                                 typedValue = [ "<unitDouble>", [ "percentUnit", value ] ];
291                                 break;
292                             case "blur":
293                             case "distance":
294                             case "size":
295                             case "softness":
296                                 typedValue = [ "<unitDouble>", [ "pixelsUnit", value ] ];
297                                 break;
298                             case "horizontal":
299                             case "vertical":
300                                 typedValue = (hintData) ? [ "<unitDouble>", [ hintData, value ] ] : [ "<double>", value ];
301                                 break;
302                             case "type":
303                                 var enumType;
304                                 switch (value)
305                                 {
306                                     case "linear":
307                                     case "radial":
308                                     case "angle":
309                                     case "reflected":
310                                     case "diamond":
311                                     case "shapeburst":
312                                         enumType = "gradientType";
313                                         break;
314                                     case "foregroundColor":
315                                     case "backgroundColor":
316                                     case "userStop":
317                                         enumType = "colorStopType";
318                                         break;
319                                 }
320                                 typedValue = [ "<enumerated>", [ enumType, value ] ];
321                                 break;
322                             case "colorSpace":
323                                 typedValue = [ "<enumerated>", [ "colorSpace", value ] ];
324                                 break;
325                             case "gradientForm":
326                                 typedValue = [ "<enumerated>", [ "gradientForm", value ] ];
327                                 break;
328                             case "paintType":
329                                 typedValue = [ "<enumerated>", [ "frameFill", value ] ];
330                                 break;
331                             case "bevelDirection":
332                                 typedValue = [ "<enumerated>", [ "bevelEmbossStampStyle", value ] ];
333                                 break;
334                             case "bevelStyle":
335                                 typedValue = [ "<enumerated>", [ "bevelEmbossStyle", value ] ];
336                                 break;
337                             case "bevelTechnique":
338                                 typedValue = [ "<enumerated>", [ "bevelTechnique", value ] ];
339                                 break;
340                             case "glowTechnique":
341                                 typedValue = [ "<enumerated>", [ "matteTechnique", value ] ];
342                                 break;
343                             case "innerGlowSource":
344                                 typedValue = [ "<enumerated>", [ "innerGlowSourceType", value ] ];
345                                 break;
346                             case "style":
347                                 typedValue = [ "<enumerated>", [ "frameStyle", value ] ];
348                                 break;
349                             case "highlightMode":
350                             case "mode":
351                             case "shadowMode":
352                                 typedValue = [ "<enumerated>", [ "blendMode", value ] ];
353                                 break;
354                             case "bevelEmboss":
355                             case "chromeFX":
356                             case "dropShadow":
357                             case "frameFX":
358                             case "gradientFill":
359                             case "innerGlow":
360                             case "innerShadow":
361                             case "outerGlow":
362                             case "pattern":
363                             case "patternFill":
364                             case "solidFill":
365                                 typedValue = [ "<object>", [ key, restoreDesc (value) ] ];
366                                 break;
367                             case "color":
368                             case "highlightColor":
369                             case "shadowColor":
370                                 var colorClass;
371                                 if ((("book" in value) && ("name" in value)) || (("bookID" in value) && ("bookKey" in value)))
372                                 {
373                                     colorClass = "bookColor";
374                                 }
375                                 else if (("cyan" in value) && ("magenta" in value) && ("yellowColor" in value) && ("black" in value))
376                                 {
377                                     colorClass = "CMYKColorClass";
378                                 }
379                                 else if ("gray" in value)
380                                 {
381                                     colorClass = "grayscale";
382                                 }
383                                 else if (("hue" in value) && ("saturation" in value) && ("brightness" in value))
384                                 {
385                                     colorClass = "HSBColorClass";
386                                 }
387                                 else if (("luminance" in value) && ("a" in value) && ("b" in value))
388                                 {
389                                     colorClass = "labColor";
390                                 }
391                                 else if (("red" in value) && ("green" in value) && ("blue" in value))
392                                 {
393                                     colorClass = "RGBColor";
394                                 }
395                                 typedValue = [ "<object>", [ colorClass, restoreDesc (value) ] ];
396                                 break;
397                             case "gradient":
398                                 typedValue = [ "<object>", [ "gradientClassEvent", restoreDesc (value) ] ];
399                                 break;
400                             case "mappingShape":
401                             case "transparencyShape":
402                                 typedValue = [ "<object>", [ "shapingCurve", restoreDesc (value) ] ];
403                                 break;
404                             case "offset":
405                                 typedValue = [ "<object>", [ "point", restoreDesc (value, "percentUnit") ] ];
406                                 break;
407                             case "phase":
408                                 typedValue = [ "<object>", [ "point", restoreDesc (value) ] ];
409                                 break;
410                             case "minimum":
411                             case "maximum":
412                                 restoredList = [ ];
413                                 for (var i = 0; i < value.length; i++)
414                                 {
415                                     restoredList.push ([ "<integer>", value[i] ]);
416                                 }
417                                 typedValue = [ "<list>", restoredList ];
418                                 break;
419                             case "colors":
420                                 restoredList = [ ];
421                                 for (var i = 0; i < value.length; i++)
422                                 {
423                                     restoredList.push ([ "<object>", [ "colorStop", restoreDesc (value[i]) ] ]);
424                                 }
425                                 typedValue = [ "<list>", restoredList ];
426                                 break;
427                             case "transparency":
428                                 restoredList = [ ];
429                                 for (var i = 0; i < value.length; i++)
430                                 {
431                                     restoredList.push ([ "<object>", [ "transparencyStop", restoreDesc (value[i]) ] ]);
432                                 }
433                                 typedValue = [ "<list>", restoredList ];
434                                 break;
435                             case "curve":
436                                 restoredList = [ ];
437                                 for (var i = 0; i < value.length; i++)
438                                 {
439                                     restoredList.push ([ "<object>", [ "curvePoint", restoreDesc (value[i]) ] ]);
440                                 }
441                                 typedValue = [ "<list>", restoredList ];
442                                 break;
443                             case "layerEffects":
444                                 typedValue = [ "<object>", [ "layerEffects", restoreDesc (value) ] ];
445                                 break;
446                         }
447                         if (typedValue)
448                         {
449                             restoredDesc[key] = typedValue;
450                         }
451                     }
452                 }
453                 return restoredDesc;
454             }
455             //
456             return restoreDesc ({ "layerEffects": layerEffects })["layerEffects"];
457         };
458         //
459         /**
460          * @description Get a simplified layer effects JSON object from a layer effects object in JSON AM Data Format.
461          * @param {Object|Array} layerEffectsObject Layer effects object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
462          * @returns {Object} Simplified layer effects JSON object
463          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/layer-effects-object-simplified-format/">Layer Effects Object Simplified Format</a>)
464          * @see jamStyles.toLayerEffectsObject
465          * @example
466          * if (parseInt (app.version) >= 10) // CS3
467          * {
468          *     jamEngine.meaningfulIds = true;
469          *     jamEngine.parseFriendly = true;
470          *     try
471          *     {
472          *         var resultDescObj = jamEngine.jsonGet
473          *         (
474          *             [
475          *                 [ "property", [ "<property>", "layerEffects" ] ],
476          *                 [ "layer", [ "<enumerated>", [ "ordinal", "targetEnum" ] ] ]
477          *             ]
478          *         );
479          *         var layerEffects = jamStyles.<strong>fromLayerEffectsObject</strong> (resultDescObj["layerEffects"]);
480          *     }
481          *     catch (e)
482          *     {
483          *         var layerEffects = null;
484          *     }
485          *     if (layerEffects)
486          *     {
487          *         $.writeln (jamJSON.stringify (layerEffects, '\t'));
488          *     }
489          *     else
490          *     {
491          *         alert ("No layer effects selected.");
492          *     }
493          * }
494          */
495         jamStyles.fromLayerEffectsObject = function (layerEffectsObject)
496         {
497             return jamEngine.simplifyObject (layerEffectsObject);
498         };
499         //
500         /**
501          * @description Get a blending options object in JSON AM Data Format from a simplified blending options JSON object.
502          * @param {Object} blendOptions Simplified blending options JSON object
503          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/blending-options-object-simplified-format/">Blending Options Object Simplified Format</a>)
504          * @returns {Object|Array} Blending options object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
505          * @see jamStyles.fromBlendOptionsObject
506          * @example
507          * var blendOptions =
508          * {
509          *     "mode": "overlay",
510          *     "opacity": 75,
511          *     "fillOpacity": 50
512          * };
513          * jamEngine.jsonPlay
514          * (
515          *     "set",
516          *     {
517          *         "target":
518          *         {
519          *             "<reference>":
520          *             [
521          *                 { "layer": { "<enumerated>": { "ordinal": "targetEnum" } } }
522          *             ]
523          *         },
524          *         "to": jamStyles.<strong>toBlendOptionsObject</strong> (blendOptions)
525          *     }
526          * );
527          */
528         jamStyles.toBlendOptionsObject = function (blendOptions)
529         {
530             function restoreDesc (desc)
531             {
532                 var restoredDesc = { };
533                 for (var key in desc)
534                 {
535                     if (desc.hasOwnProperty (key))
536                     {
537                         var value = desc[key];
538                         var typedValue = null;
539                         var restoredList;
540                         switch (key)
541                         {
542                             case "blendClipped":
543                             case "blendInterior":
544                             case "layerMaskAsGlobalMask":
545                             case "transparencyShapesLayer":
546                             case "vectorMaskAsGlobalMask":
547                                 typedValue = [ "<boolean>", value ];
548                                 break;
549                             case "srcBlackMin":
550                             case "srcBlackMax":
551                             case "srcWhiteMin":
552                             case "srcWhiteMax":
553                             case "destBlackMin":
554                             case "destBlackMax":
555                             case "destWhiteMin":
556                             case "destWhiteMax":
557                                 typedValue = [ "<integer>", value ];
558                                 break;
559                             case "fillOpacity":
560                             case "opacity":
561                                 typedValue = [ "<unitDouble>", [ "percentUnit", value ] ];
562                                 break;
563                             case "mode":
564                                 typedValue = [ "<enumerated>", [ "blendMode", value ] ];
565                                 break;
566                             case "knockout":
567                                 typedValue = [ "<enumerated>", [ "knockout", value ] ];
568                                 break;
569                             case "channel":
570                                 typedValue = [ "<reference>", [ [ "channel", [ "<enumerated>", [ "channel", value ] ] ] ] ]; // Special handling
571                                 break;
572                             case "blendRange":
573                                 restoredList = [ ];
574                                 for (var i = 0; i < value.length; i++)
575                                 {
576                                     restoredList.push ([ "<object>", [ "blendRange", restoreDesc (value[i]) ] ]);
577                                 }
578                                 typedValue = [ "<list>", restoredList ];
579                                 break;
580                             case "channelRestrictions":
581                                 restoredList = [ ];
582                                 for (var i = 0; i < value.length; i++)
583                                 {
584                                     restoredList.push ([ "<enumerated>", [ "channel", value[i] ] ]);
585                                 }
586                                 typedValue = [ "<list>", restoredList ];
587                                 break;
588                             case "blendOptions":
589                                 typedValue = [ "<object>", [ "blendOptions", restoreDesc (value) ] ];
590                                 break;
591                         }
592                         if (typedValue)
593                         {
594                             restoredDesc[key] = typedValue;
595                         }
596                     }
597                 }
598                 return restoredDesc;
599             }
600             //
601             return restoreDesc ({ "blendOptions": blendOptions })["blendOptions"];
602         };
603         //
604         /**
605          * @description Get a simplified blending options JSON object from a blending options object in JSON AM Data Format.
606          * @param {Object|Array} blendOptionsObject Blending options object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
607          * @returns {Object} Simplified blending options JSON object
608          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/blending-options-object-simplified-format/">Blending Options Object Simplified Format</a>)
609          * @see jamStyles.toBlendOptionsObject
610          * @example
611          * jamEngine.meaningfulIds = true;
612          * jamEngine.parseFriendly = true;
613          * var style = { };
614          * var actionDescriptor;
615          * var jsonDesc;
616          * actionDescriptor = jamActions.readActionDescriptor (stylesFile);
617          * jsonDesc = jamEngine.classIdAndActionDescriptorToJson (0, actionDescriptor)["<descriptor>"];
618          * style["name"] = jsonDesc["name"][1];
619          * style["ID"] = jsonDesc["ID"][1];
620          * actionDescriptor = jamActions.readActionDescriptor (stylesFile);
621          * jsonDesc = jamEngine.classIdAndActionDescriptorToJson (0, actionDescriptor)["<descriptor>"];
622          * if ("blendOptions" in jsonDesc)
623          * {
624          *     style["blendOptions"] = jamStyles.<strong>fromBlendOptionsObject</strong> (jsonDesc["blendOptions"]);
625          * }
626          * $.writeln (jamJSON.stringify (style, '\t'));
627          */
628         jamStyles.fromBlendOptionsObject = function (blendOptionsObject)
629         {
630             var replaceChannelHook = function (desc, key, getDefaultValue)
631             {
632                 var replacedValue = undefined;
633                 if (key === "channel")  // Special handling
634                 {
635                     var value = getDefaultValue (desc, key);
636                     replacedValue = value[0]["channel"];
637                 }
638                 return replacedValue;
639             };
640             return jamEngine.simplifyObject (blendOptionsObject, replaceChannelHook);
641         };
642         //
643         /**
644          * @description Get a document mode object in JSON AM Data Format from a simplified document mode JSON object.
645          * @param {Object} documentMode Simplified document mode JSON object
646          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/document-mode-object-simplified-format/">Document Mode Object Simplified Format</a>)
647          * @returns {Object|Array} Document mode object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
648          * @see jamStyles.fromDocumentModeObject
649          * @example
650          * var documentMode =
651          * {
652          *     "colorSpace": "RGBColor",
653          *     "depth": 8
654          * };
655          * alert (jamJSON.stringify (jamStyles.<strong>toDocumentModeObject</strong> (documentMode), '\t'));
656          * // ->
657          * // [
658          * //     "<object>",
659          * //     [
660          * //         "documentMode",
661          * //         {
662          * //             "colorSpace":
663          * //             [
664          * //                 "<enumerated>",
665          * //                 [
666          * //                     "colorSpace",
667          * //                     "RGBColor"
668          * //                 ]
669          * //             ],
670          * //             "depth":
671          * //             [
672          * //                 "<integer>",
673          * //                 8
674          * //             ]
675          * //         }
676          * //     ]
677          * // ]
678          */
679         jamStyles.toDocumentModeObject = function (documentMode)
680         {
681             function restoreDesc (desc)
682             {
683                 var restoredDesc = { };
684                 for (var key in desc)
685                 {
686                     if (desc.hasOwnProperty (key))
687                     {
688                         var value = desc[key];
689                         var typedValue = null;
690                         switch (key)
691                         {
692                             case "colorSpace":
693                                 typedValue = [ "<enumerated>", [ "colorSpace", value ] ];
694                                 break;
695                             case "depth":
696                                 typedValue = [ "<integer>", value ];
697                                 break;
698                             case "documentMode":
699                                 typedValue = [ "<object>", [ "documentMode", restoreDesc (value) ] ];
700                                 break;
701                         }
702                         if (typedValue)
703                         {
704                             restoredDesc[key] = typedValue;
705                         }
706                     }
707                 }
708                 return restoredDesc;
709             }
710             //
711             return restoreDesc ({ "documentMode": documentMode })["documentMode"];
712         };
713         //
714         /**
715          * @description Get a simplified document mode JSON object from a document mode object in JSON AM Data Format.
716          * @param {Object|Array} documentModeObject Document mode object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
717          * @returns {Object} Simplified document mode JSON object
718          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/document-mode-object-simplified-format/">Document Mode Object Simplified Format</a>)
719          * @see jamStyles.toDocumentModeObject
720          * @example
721          * jamEngine.meaningfulIds = true;
722          * jamEngine.parseFriendly = true;
723          * var style = { };
724          * var actionDescriptor;
725          * var jsonDesc;
726          * actionDescriptor = jamActions.readActionDescriptor (stylesFile);
727          * jsonDesc = jamEngine.classIdAndActionDescriptorToJson (0, actionDescriptor)["<descriptor>"];
728          * style["name"] = jsonDesc["name"][1];
729          * style["ID"] = jsonDesc["ID"][1];
730          * actionDescriptor = jamActions.readActionDescriptor (stylesFile);
731          * jsonDesc = jamEngine.classIdAndActionDescriptorToJson (0, actionDescriptor)["<descriptor>"];
732          * if ("documentMode" in jsonDesc)
733          * {
734          *     style["documentMode"] = jamStyles.<strong>fromDocumentModeObject</strong> (jsonDesc["documentMode"]);
735          * }
736          * $.writeln (jamJSON.stringify (style, '\t'));
737          */
738         jamStyles.fromDocumentModeObject = function (documentModeObject)
739         {
740             return jamEngine.simplifyObject (documentModeObject);
741         };
742         //
743         function getDocumentMode ()
744         {
745             var documentMode = { };
746             var saveMeaningfulIds = jamEngine.meaningfulIds;
747             var saveParseFriendly = jamEngine.parseFriendly;
748             jamEngine.meaningfulIds = true;
749             jamEngine.parseFriendly = true;
750             var resultDescObj;
751             resultDescObj = jamEngine.jsonGet
752             (
753                 [
754                     [ "property", [ "<property>", "mode" ] ],
755                     [ "document", [ "<enumerated>", [ "ordinal", "targetEnum" ] ] ]
756                 ]
757             );
758             documentMode["colorSpace"] = resultDescObj["mode"][1][1];
759             resultDescObj = jamEngine.jsonGet
760             (
761                 [
762                     [ "property", [ "<property>", "depth" ] ],
763                     [ "document", [ "<enumerated>", [ "ordinal", "targetEnum" ] ] ]
764                 ]
765             );
766             documentMode["depth"] = resultDescObj["depth"][1];
767             jamEngine.meaningfulIds = saveMeaningfulIds;
768             jamEngine.parseFriendly = saveParseFriendly;
769             return documentMode;
770         }
771         //
772         function getDocumentResolution ()
773         {
774             var saveMeaningfulIds = jamEngine.meaningfulIds;
775             var saveParseFriendly = jamEngine.parseFriendly;
776             jamEngine.meaningfulIds = true;
777             jamEngine.parseFriendly = true;
778             var resultDescObj = jamEngine.jsonGet
779             (
780                 [
781                     [ "property", [ "<property>", "resolution" ] ],
782                     [ "document", [ "<enumerated>", [ "ordinal", "targetEnum" ] ] ]
783                 ]
784             );
785             jamEngine.meaningfulIds = saveMeaningfulIds;
786             jamEngine.parseFriendly = saveParseFriendly;
787             return resultDescObj["resolution"][1][1];
788         }
789         //
790         /**
791          * @description Set the current layer style (blending options and layer effects).
792          * @param {Object|Null} layerStyleObj Simplified layer style JSON object made of two optional members: <br>
793          * "blendOptions" (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/blending-options-object-simplified-format/">Blending Options Object Simplified Format</a>)<br>
794          * "layerEffects" (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/layer-effects-object-simplified-format/">Layer Effects Object Simplified Format</a>)<br>
795          * If null, apply default style (none), i.e., reset blending options to default and remove all layer effects
796          * @param {Boolean} [ignoreScale] Ignore scale property of layer effects; false by default
797          * @see jamStyles.getLayerStyle
798          * @example
799          * jamStyles.<strong>setLayerStyle</strong>
800          * (
801          *     {
802          *         "blendOptions":
803          *         {
804          *             "mode": "difference",
805          *             "opacity": 100,
806          *             "fillOpacity": 70,
807          *             "blendInterior": true
808          *         },
809          *         "layerEffects":
810          *         {
811          *             "scale": 100,
812          *             "solidFill":
813          *             {
814          *                 "enabled": true,
815          *                 "mode": "normal",
816          *                 "opacity": 100,
817          *                 "color": { "red": 125, "green": 0, "blue": 215 }
818          *             },
819          *             "dropShadow":
820          *             {
821          *                 "enabled": true,
822          *                 "mode": "multiply",
823          *                 "color": { "red": 0, "green": 0, "blue": 0 },
824          *                 "opacity": 70,
825          *                 "localLightingAngle": 135,
826          *                 "useGlobalAngle": false,
827          *                 "distance": 10,
828          *                 "chokeMatte": 20,
829          *                 "blur": 15,
830          *                 "transparencyShape":
831          *                 {
832          *                     "name": "Linear",
833          *                     "curve":
834          *                     [
835          *                         { "horizontal": 0, "vertical": 0 },
836          *                         { "horizontal": 255, "vertical": 255 }
837          *                     ]
838          *                 },
839          *                 "antiAlias": false,
840          *                 "noise": 8,
841          *                 "layerConceals": true
842          *             }
843          *         }
844          *     }
845          * );
846          */
847         jamStyles.setLayerStyle = function (layerStyleObj, ignoreScale)
848         {
849             if (layerStyleObj && (("blendOptions" in layerStyleObj) || ("layerEffects" in layerStyleObj)))
850             {
851                 var layerDesc = { };
852                 //
853                 if ("blendOptions" in layerStyleObj)
854                 {
855                     defaultBlendOptionsObj =
856                     {
857                         "mode": "normal",
858                         "opacity": 100,
859                         "fillOpacity": 100,
860                         "channelRestrictions": [ ],
861                         "knockout": "none",
862                         "blendInterior": false,
863                         "blendClipped": true,
864                         "transparencyShapesLayer": true,
865                         "layerMaskAsGlobalMask": false,
866                         "vectorMaskAsGlobalMask": false,
867                         "blendRange": [ ]
868                     };
869                     var documentMode = getDocumentMode ();
870                     var channelRestrictions;
871                     var blendRangeChannels;
872                     switch (documentMode["colorSpace"])
873                     {
874                         case "CMYKColorEnum":
875                         case "CMYK64":  // In CS !!
876                             channelRestrictions = [ "cyan", "magenta", "yellow", "black" ];
877                             blendRangeChannels = [ "gray", "cyan", "magenta", "yellow", "black" ];
878                             break;
879                         case "duotone":
880                         case "grayScale":
881                         case "gray16":  // In CS !!
882                             channelRestrictions = [ "black" ];
883                             blendRangeChannels = [ "black" ];
884                             break;
885                         case "labColor":
886                         case "lab48":   // In CS !!
887                             channelRestrictions = [ "lightness", "a", "b" ];
888                             blendRangeChannels = [ "lightness", "a", "b" ];
889                             break;
890                         case "RGBColor":
891                         case "RGB48":   // In CS !!
892                             channelRestrictions = [ "red", "green", "blue" ];
893                             blendRangeChannels = [ "gray", "red", "green", "blue" ];
894                             break;
895                     }
896                     defaultBlendOptionsObj["channelRestrictions"] = channelRestrictions;
897                     for (var i = 0; i < blendRangeChannels.length; i++)
898                     {
899                         defaultBlendRangeObj =
900                         {
901                             "channel": blendRangeChannels[i],
902                             "srcBlackMin": 0,
903                             "srcBlackMax": 0,
904                             "srcWhiteMin": 255,
905                             "srcWhiteMax": 255,
906                             "destBlackMin": 0,
907                             "destBlackMax": 0,
908                             "destWhiteMin": 255,
909                             "destWhiteMax": 255
910                         };
911                         defaultBlendOptionsObj["blendRange"].push (defaultBlendRangeObj);
912                     }
913                     var blendOptionsObj = jamUtils.mergeData (layerStyleObj["blendOptions"], defaultBlendOptionsObj);
914                     var blendOptionsDesc = this.toBlendOptionsObject (blendOptionsObj)[1][1];
915                     for (var key in blendOptionsDesc)
916                     {
917                         if (blendOptionsDesc.hasOwnProperty (key))
918                         {
919                             layerDesc[key] = blendOptionsDesc[key];
920                         }
921                     }
922                 }
923                 //
924                 var layerEffects;
925                 //
926                 if ("layerEffects" in layerStyleObj)
927                 {
928                     layerEffects = layerStyleObj["layerEffects"];
929                     layerDesc["layerEffects"] = this.toLayerEffectsObject (layerEffects);
930                 }
931                 //
932                 jamEngine.jsonPlay
933                 (
934                     "set",
935                     {
936                         "target": [ "<reference>", [ [ "layer", [ "<enumerated>", [ "ordinal", "targetEnum" ] ] ] ] ],
937                         "to": [ "<object>", [ "layer", layerDesc ] ]
938                     }
939                 );
940                 //
941                 if (layerEffects)
942                 {
943                     if (("scale" in layerEffects) && (!ignoreScale))
944                     {
945                         this.scaleLayerEffects ((getDocumentResolution () / 72) / (layerEffects["scale"] / 100) * 100);
946                     }
947                 }
948             }
949             else
950             {
951                 this.clearLayerStyle ();
952             }
953         };
954         //
955         function getPresetStylesCount ()
956         {
957             var saveMeaningfulIds = jamEngine.meaningfulIds;
958             var saveParseFriendly = jamEngine.parseFriendly;
959             jamEngine.meaningfulIds = true;
960             jamEngine.parseFriendly = true;
961             var resultDescObj = jamEngine.jsonGet
962             (
963                 [
964                     [ "property", [ "<property>", "presetManager" ] ],
965                     [ "application", [ "<enumerated>", [ "ordinal", "targetEnum" ] ] ]
966                 ]
967             );
968             var presetManagerArr = resultDescObj["presetManager"][1];
969             var presetStylesCount;
970             for (var i = 0; i < presetManagerArr.length; i++)
971             {
972                 var preset = presetManagerArr[i][1];
973                 if (preset[0] === "styleClass")
974                 {
975                     presetStylesCount = preset[1]["name"][1].length;
976                     break;
977                 }
978             }
979             jamEngine.meaningfulIds = saveMeaningfulIds;
980             jamEngine.parseFriendly = saveParseFriendly;
981             return presetStylesCount;
982         }
983         //
984         function isStyledLayer ()
985         {
986             var saveMeaningfulIds = jamEngine.meaningfulIds;
987             var saveParseFriendly = jamEngine.parseFriendly;
988             jamEngine.meaningfulIds = true;
989             jamEngine.parseFriendly = true;
990             var isLayer = false;
991             try
992             {
993                 var resultDescObj = jamEngine.jsonGet
994                 (
995                     [
996                         [ "property", [ "<property>", "background" ] ],
997                         [ "layer", [ "<enumerated>", [ "ordinal", "targetEnum" ] ] ]
998                     ]
999                 );
1000                 isLayer = !resultDescObj["background"][1];
1001             }
1002             catch (e)
1003             {
1004             }
1005             var saveMeaningfulIds = jamEngine.meaningfulIds;
1006             var saveParseFriendly = jamEngine.parseFriendly;
1007             return isLayer;
1008         }
1009         //
1010         /**
1011          * @description Get the current layer style (blending options and layer effects) [available in CS2 or later].
1012          * @returns {Object|Null} Simplified layer style JSON object made of two optional members: <br>
1013          * "blendOptions" (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/blending-options-object-simplified-format/">Blending Options Object Simplified Format</a>)<br>
1014          * "layerEffects" (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/layer-effects-object-simplified-format/">Layer Effects Object Simplified Format</a>)<br>
1015          * or null if not available
1016          * @see jamStyles.setLayerStyle
1017          * @example
1018          * var layerStyleObj = jamStyles.<strong>getLayerStyle</strong> ();
1019          * if (layerStyleObj)
1020          * {
1021          *     if ("layerEffects" in layerStyleObj)
1022          *     {
1023          *         var layerEffectsObj = layerStyleObj["layerEffects"];
1024          *         if ("dropShadow" in layerEffectsObj)
1025          *         {
1026          *             var dropShadowObj = layerEffectsObj["dropShadow"];
1027          *             alert ("Drop shadow distance: " + dropShadowObj["distance"]);
1028          *         }
1029          *         else
1030          *         {
1031          *             alert ("No drop shadow");
1032          *         }
1033          *     }
1034          *     else
1035          *     {
1036          *         alert ("No layer effects");
1037          *     }
1038          * }
1039          */
1040         jamStyles.getLayerStyle = function ()
1041         {
1042             var layerStyleObj = null;
1043             //
1044             if (isStyledLayer ())
1045             {
1046                 var presetStylesCountBefore = getPresetStylesCount ();
1047                 //
1048                 var date = new Date ();
1049                 var tempStyleName = "Temp-Layer-Style-" + date.getTime ();
1050                 //
1051                 try
1052                 {
1053                     jamEngine.jsonPlay
1054                     (
1055                         "make",
1056                         {
1057                             "target": [ "<reference>", [ [ "style", [ "<class>", null ] ] ] ],
1058                             "name": [ "<string>", tempStyleName ],
1059                             "using": [ "<reference>", [ [ "layer", [ "<enumerated>", [ "ordinal", "targetEnum" ] ] ] ] ],
1060                             "blendOptions": [ "<boolean>", true ],
1061                             "layerEffects": [ "<boolean>", true ]
1062                         }
1063                     );
1064                 }
1065                 catch (e)
1066                 {
1067                 }
1068                 //
1069                 var presetStylesCount = getPresetStylesCount ();
1070                 //
1071                 // To confirm that a layer was actually *selected* in the layer palette, or that a layer group can get styled...
1072                 if (presetStylesCount === (presetStylesCountBefore + 1))
1073                 {
1074                     var tempStylesFile = new File (Folder.temp + "/" + tempStyleName + ".asl");
1075                     jamEngine.jsonPlay
1076                     (
1077                         "set",
1078                         {
1079                             "target": [ "<path>", tempStylesFile.fsName ],
1080                             "to": [ "<list>", [ [ "<reference>", [ [ "style", [ "<index>", presetStylesCount ] ] ] ] ] ]
1081                         }
1082                     );
1083                     //
1084                     jamEngine.jsonPlay
1085                     (
1086                         "delete",
1087                         {
1088                             "target": [ "<list>", [ [ "<reference>", [ [ "style", [ "<index>", presetStylesCount ] ] ] ] ] ]
1089                         }
1090                     );
1091                     //
1092                     var tempStylesFileData = this.dataFromStylesFile (tempStylesFile);
1093                     if (typeof tempStylesFileData === 'string')
1094                     {
1095                         alert (tempStylesFileData + "\n" + "Styles file: “" + File.decode (tempStylesFile.name) + "”");
1096                     }
1097                     else
1098                     {
1099                         layerStyleObj = tempStylesFileData["styles"][0];
1100                         if ("name" in layerStyleObj)
1101                         {
1102                             delete layerStyleObj["name"];
1103                         }
1104                         if ("ID" in layerStyleObj)
1105                         {
1106                             delete layerStyleObj["ID"];
1107                         }
1108                         if ("documentMode" in layerStyleObj)
1109                         {
1110                             delete layerStyleObj["documentMode"];
1111                         }
1112                         if ("layerEffects" in layerStyleObj)
1113                         {
1114                             var layerEffects = layerStyleObj["layerEffects"];
1115                             if ("masterFXSwitch" in layerEffects)
1116                             {
1117                                 delete layerEffects["masterFXSwitch"];
1118                             }
1119                         }
1120                     }
1121                     // Let's keep this undocumented for now
1122                     if (arguments.length > 0)
1123                     {
1124                         var extraInfo = arguments[0];
1125                         if (extraInfo && (extraInfo.constructor === Object))
1126                         {
1127                             if ("patterns" in extraInfo)
1128                             {
1129                                 var tempStylesFilePatterns = this.patternsFromStylesFile (tempStylesFile);
1130                                 if (typeof tempStylesFilePatterns === 'string')
1131                                 {
1132                                     alert (tempStylesFilePatterns + "\n" + "Styles file: “" + File.decode (tempStylesFile.name) + "”");
1133                                 }
1134                                 else
1135                                 {
1136                                     extraInfo["patterns"] = tempStylesFilePatterns;
1137                                 }
1138                             }
1139                         }
1140                     }
1141                     //
1142                     tempStylesFile.remove ();
1143                 }
1144             }
1145             //
1146             return layerStyleObj;
1147         };
1148         //
1149         /**
1150          * @description Copy a layer style.
1151          * @see jamStyles.pasteLayerStyle
1152          * @example
1153          * jamStyles.<strong>copyLayerStyle</strong> ();
1154          */
1155         jamStyles.copyLayerStyle = function ()
1156         {
1157             try
1158             {
1159                 jamEngine.jsonPlay ("copyEffects", null);
1160             }
1161             catch (e)
1162             {
1163             }
1164         };
1165         //
1166         /**
1167          * @description Paste a layer style.
1168          * @see jamStyles.copyLayerStyle
1169          * @example
1170          * jamStyles.<strong>pasteLayerStyle</strong> ();
1171          */
1172         jamStyles.pasteLayerStyle = function ()
1173         {
1174             try
1175             {
1176                 jamEngine.jsonPlay ("pasteEffects", { });
1177             }
1178             catch (e)
1179             {
1180             }
1181         };
1182         //
1183         /**
1184          * @description Clear a layer style (reset blending options to default and remove all layer effects).
1185          * @example
1186          * jamStyles.<strong>clearLayerStyle</strong> ();
1187          */
1188         jamStyles.clearLayerStyle = function ()
1189         {
1190             try
1191             {
1192                 jamEngine.jsonPlay
1193                 (
1194                     "disableLayerStyle",
1195                     {
1196                         "target": [ "<reference>", [ [ "layer", [ "<enumerated>", [ "ordinal", "targetEnum" ] ] ] ] ]
1197                     }
1198                 );
1199             }
1200             catch (e)
1201             {
1202             }
1203         };
1204         //
1205         /**
1206          * @description Apply a layer style preset.
1207          * @param {String} styleName Layer style preset name
1208          * @param {Boolean} [merge] Merge with current layer style
1209          * @example
1210          * jamUtils.loadPreset ("style", "Logo X-Aqua in Blue Glass (Button)", "Logo-X-Aqua.asl");
1211          * jamStyles.<strong>applyLayerStyle</strong> ("Logo X-Aqua in Blue Glass (Button)");
1212          */
1213         jamStyles.applyLayerStyle = function (styleName, merge)
1214         {
1215             var descriptor =
1216             {
1217                 "target": [ "<reference>", [ [ "style", [ "<name>", styleName ] ] ] ],
1218                 "to": [ "<reference>", [ [ "layer", [ "<enumerated>", [ "ordinal", "targetEnum" ] ] ] ] ]
1219             };
1220             if ((typeof merge !== 'undefined') && merge)
1221             {
1222                 descriptor["merge"] = [ "<boolean>", merge ];
1223             }
1224             jamEngine.jsonPlay ("applyStyle", descriptor);
1225         };
1226         //
1227         /**
1228          * @description Scale layer effects.
1229          * @param {Number} scale Layer effects scaling factor (percentage: 1% to 1000%)
1230          * @example
1231          * jamStyles.<strong>scaleLayerEffects</strong> (200);
1232          */
1233         jamStyles.scaleLayerEffects = function (scale)
1234         {
1235             jamEngine.jsonPlay ("scaleEffectsEvent", { "scale": [ "<unitDouble>", [ "percentUnit", scale ] ] });
1236         };
1237         //
1238         /**
1239          * @description Remove (delete) a layer effect.
1240          * @param {String} effect Layer effect to remove (delete), among:<br>
1241          * <ul>
1242          * <li>"bevelEmboss"</li>
1243          * <li>"frameFX"</li>
1244          * <li>"innerShadow"</li>
1245          * <li>"innerGlow"</li>
1246          * <li>"chromeFX"</li>
1247          * <li>"solidFill"</li>
1248          * <li>"gradientFill"</li>
1249          * <li>"patternFill"</li>
1250          * <li>"outerGlow"</li>
1251          * <li>"dropShadow"</li>
1252          * </ul>
1253          * @see jamStyles.removeLayerEffects
1254          * @see jamStyles.removeAllLayerEffects
1255          * @example
1256          * jamStyles.<strong>removeLayerEffect</strong> ("dropShadow");
1257          */
1258         jamStyles.removeLayerEffect = function (effect)
1259         {
1260             try
1261             {
1262                 jamEngine.jsonPlay
1263                 (
1264                     "disableSingleFX",
1265                     {
1266                         "target":
1267                         [
1268                             "<reference>",
1269                             [
1270                                 [ effect, [ "<class>", null ] ],
1271                                 [ "layer", [ "<enumerated>", [ "ordinal", "targetEnum" ] ] ]
1272                             ]
1273                         ]
1274                     }
1275                 );
1276             }
1277             catch (e)
1278             {
1279             }
1280         };
1281         //
1282         /**
1283          * @description Remove (delete) several layer effects.
1284          * @param {Array} effects Array of layer effects to remove (delete), among:<br>
1285          * <ul>
1286          * <li>"bevelEmboss"</li>
1287          * <li>"frameFX"</li>
1288          * <li>"innerShadow"</li>
1289          * <li>"innerGlow"</li>
1290          * <li>"chromeFX"</li>
1291          * <li>"solidFill"</li>
1292          * <li>"gradientFill"</li>
1293          * <li>"patternFill"</li>
1294          * <li>"outerGlow"</li>
1295          * <li>"dropShadow"</li>
1296          * </ul>
1297          * @see jamStyles.removeLayerEffect
1298          * @see jamStyles.removeAllLayerEffects
1299          * @example
1300          * jamStyles.<strong>removeLayerEffects</strong> ([ "dropShadow", "bevelEmboss" ]);
1301          */
1302         jamStyles.removeLayerEffects = function (effects)
1303         {
1304             for (var i = 0; i < effects.length; i++)
1305             {
1306                 this.removeLayerEffect (effects[i]);
1307             }
1308         };
1309         //
1310         /**
1311          * @description Remove (delete) all layer effects.
1312          * @see jamStyles.removeLayerEffect
1313          * @see jamStyles.removeLayerEffects
1314          * @example
1315          * jamStyles.<strong>removeAllLayerEffects</strong> ();
1316          */
1317         jamStyles.removeAllLayerEffects = function ()
1318         {
1319             try
1320             {
1321                 jamEngine.jsonPlay
1322                 (
1323                     "disableLayerFX",
1324                     {
1325                         "target": [ "<reference>", [ [ "layer", [ "<enumerated>", [ "ordinal", "targetEnum" ] ] ] ] ]
1326                     }
1327                 );
1328             }
1329             catch (e)
1330             {
1331             }
1332         };
1333         //
1334         /**
1335          * @description Show or hide several layer effects.
1336          * @param {Array} effects Array of layer effects to show or hide, among:<br>
1337          * <ul>
1338          * <li>"bevelEmboss"</li>
1339          * <li>"frameFX"</li>
1340          * <li>"innerShadow"</li>
1341          * <li>"innerGlow"</li>
1342          * <li>"chromeFX"</li>
1343          * <li>"solidFill"</li>
1344          * <li>"gradientFill"</li>
1345          * <li>"patternFill"</li>
1346          * <li>"outerGlow"</li>
1347          * <li>"dropShadow"</li>
1348          * </ul>
1349          * @param {Boolean} [show] Show (instead of hide) the layer effects; false by default<br>
1350          * @see jamStyles.showHideLayerEffect
1351          * @see jamStyles.showHideAllLayerEffects
1352          * @see jamStyles.showHideAllDocumentEffects
1353          * @example
1354          * jamStyles.<strong>showHideLayerEffects</strong> ([ "dropShadow", "bevelEmboss" ]);                       // Hide
1355          * jamStyles.<strong>showHideLayerEffects</strong> ([ "solidFill", "gradientFill", "patternFill" ], true);  // Show
1356          */
1357         jamStyles.showHideLayerEffects = function (effects, show)
1358         {
1359             var references = [ ];
1360             for (var i = 0; i < effects.length; i++)
1361             {
1362                 references.push
1363                 (
1364                     [
1365                         "<reference>",
1366                         [
1367                             [ effects[i], [ "<class>", null ] ],
1368                             [ "layer", [ "<enumerated>", [ "ordinal", "targetEnum" ] ] ]
1369                         ]
1370                     ]
1371                 );
1372             }
1373             try
1374             {
1375                 jamEngine.jsonPlay ((show) ? "show" : "hide", { "target": [ "<list>", references ] }, DialogModes.NO);
1376             }
1377             catch (e)
1378             {
1379             }
1380         };
1381         //
1382         /**
1383          * @description Show or hide a layer effect.
1384          * @param {String} effect Layer effect to show or hide, among:<br>
1385          * <ul>
1386          * <li>"bevelEmboss"</li>
1387          * <li>"frameFX"</li>
1388          * <li>"innerShadow"</li>
1389          * <li>"innerGlow"</li>
1390          * <li>"chromeFX"</li>
1391          * <li>"solidFill"</li>
1392          * <li>"gradientFill"</li>
1393          * <li>"patternFill"</li>
1394          * <li>"outerGlow"</li>
1395          * <li>"dropShadow"</li>
1396          * </ul>
1397          * @param {Boolean} [show] Show (instead of hide) the layer effect; false by default<br>
1398          * @see jamStyles.showHideLayerEffects
1399          * @see jamStyles.showHideAllLayerEffects
1400          * @see jamStyles.showHideAllDocumentEffects
1401          * @example
1402          * jamStyles.<strong>showHideLayerEffect</strong> ("dropShadow");         // Hide
1403          * jamStyles.<strong>showHideLayerEffect</strong> ("bevelEmboss", true);  // Show
1404          */
1405         jamStyles.showHideLayerEffect = function (effect, show)
1406         {
1407             this.showHideLayerEffects ([ effect ], show);
1408         };
1409         //
1410         /**
1411          * @description Show or hide all layer effects.
1412          * @param {Boolean} [show] Show (instead of hide) all layer effects; false by default<br>
1413          * @see jamStyles.showHideLayerEffect
1414          * @see jamStyles.showHideLayerEffects
1415          * @see jamStyles.showHideAllDocumentEffects
1416          * @example
1417          * jamStyles.<strong>showHideAllLayerEffects</strong> ();      // Hide
1418          * jamStyles.<strong>showHideAllLayerEffects</strong> (true);  // Show
1419          */
1420         jamStyles.showHideAllLayerEffects = function (show)
1421         {
1422             this.showHideLayerEffects ([ "layerEffects" ], show);
1423         };
1424         //
1425         /**
1426          * @description Show or hide all document effects.
1427          * @param {Boolean} [show] Show (instead of hide) all document effects; false by default<br>
1428          * @see jamStyles.showHideLayerEffect
1429          * @see jamStyles.showHideLayerEffects
1430          * @see jamStyles.showHideAllLayerEffects
1431          * @example
1432          * jamStyles.<strong>showHideAllDocumentEffects</strong> ();      // Hide
1433          * jamStyles.<strong>showHideAllDocumentEffects</strong> (true);  // Show
1434          */
1435         jamStyles.showHideAllDocumentEffects = function (show)
1436         {
1437             jamEngine.jsonPlay
1438             (
1439                 "set",
1440                 {
1441                     "target":
1442                     [
1443                         "<reference>",
1444                         [
1445                             [ "property", [ "<property>", "layerFXVisible" ] ],
1446                             [ "document", [ "<enumerated>", [ "ordinal", "targetEnum" ] ] ]
1447                         ]
1448                     ],
1449                     "to": [ "<object>", [ "layerFXVisible", { "layerFXVisible": [ "<boolean>", show || false ] } ] ]
1450                 }
1451             );
1452         };
1453         //
1454         function setGlobalAngle (target, globalLightingAngle, globalAltitude)
1455         {
1456             var globalAngle =
1457             {
1458                 "globalLightingAngle": [ "<unitDouble>", [ "angleUnit", globalLightingAngle ] ]
1459             };
1460             if (typeof globalAltitude !== 'undefined')
1461             {
1462                 globalAngle["globalAltitude"] = [ "<unitDouble>", [ "angleUnit", globalAltitude ] ];
1463             }
1464             jamEngine.jsonPlay
1465             (
1466                 "set",
1467                 {
1468                     "target":
1469                     [
1470                         "<reference>",
1471                         [
1472                             [ "property", [ "<property>", "globalAngle" ] ],
1473                             [ target, [ "<enumerated>", [ "ordinal", "targetEnum" ] ] ]
1474                         ]
1475                     ],
1476                     "to": [ "<object>", [ "globalAngle", globalAngle ] ]
1477                 }
1478             );
1479         };
1480         //
1481         /**
1482          * @description Set application's layer effects global angle (global light: lighting angle and altitude).
1483          * @param {Number} globalLightingAngle Global lighting angle (-180° to 180°)
1484          * @param {Number} [globalAltitude] Global altitude (0° to 90°)
1485          * @see jamStyles.setDocumentGlobalAngle
1486          * @example
1487          * jamStyles.<strong>setApplicationGlobalAngle</strong> (30, 45);
1488          */
1489         jamStyles.setApplicationGlobalAngle = function (globalLightingAngle, globalAltitude)
1490         {
1491             setGlobalAngle ("application", globalLightingAngle, globalAltitude);
1492         };
1493         //
1494         /**
1495          * @description Set document's layer effects global angle (global light: lighting angle and altitude).
1496          * @param {Number} globalLightingAngle Global lighting angle (-180° to 180°)
1497          * @param {Number} [globalAltitude] Global altitude (0° to 90°)
1498          * @see jamStyles.setApplicationGlobalAngle
1499          * @example
1500          * jamStyles.<strong>setDocumentGlobalAngle</strong> (120, 90);
1501          */
1502         jamStyles.setDocumentGlobalAngle = function (globalLightingAngle, globalAltitude)
1503         {
1504             setGlobalAngle ("document", globalLightingAngle, globalAltitude);
1505         };
1506         //
1507         function readBEInt (file, byteCount)
1508         {
1509             var bytes = file.read (byteCount);
1510             var intValue = 0;
1511             for (var index = 0; index < byteCount; index++)
1512             {
1513                 intValue = (intValue << 8) + bytes.charCodeAt (index);
1514             }
1515             return intValue;
1516         }
1517         //
1518         function readUnicodeString (file)
1519         {
1520             var unicodeString = "";
1521             var unicodeLength = readBEInt (file, 4);    // Includes terminating null
1522             for (var index = 0; index < unicodeLength; index++)
1523             {
1524                 var unicodeChar = readBEInt (file, 2);
1525                 if (unicodeChar !== 0)
1526                 {
1527                     unicodeString += String.fromCharCode (unicodeChar);
1528                 }
1529             }
1530             return unicodeString;
1531         }
1532         //
1533         function readBytes (file, byteCount)
1534         {
1535             return file.read (byteCount);
1536         }
1537         //
1538         function readPascalString (file)
1539         {
1540             var stringLength = readBEInt (file, 1);
1541             return readBytes (file, stringLength);
1542         }
1543         //
1544         /**
1545          * @description Convert a styles file (*.asl or Styles.psp) into a data structure in JSON format [available in CS2 or later].
1546          * @param {String|Object} stylesFile Styles file path string or File object
1547          * @param {Boolean} includePatternsInfo Include embedded patterns basic info
1548          * @returns {Object|String} Converted styles file data structure in JSON format, or error message string
1549          * @example
1550          * function stylesFileFilter (f)
1551          * {
1552          *     return (f instanceof Folder) || jamStyles.isStylesFile (f);
1553          * }
1554          * var select = (File.fs === "Macintosh") ? stylesFileFilter : "Styles Files:*.asl,All Files:*";
1555          * var stylesFile = File.openDialog ("Select a styles file:", select);
1556          * if (stylesFile !== null)
1557          * {
1558          *     var fileData = jamStyles.<strong>dataFromStylesFile</strong> (stylesFile, true);
1559          *     if (typeof fileData === 'string')
1560          *     {
1561          *         alert (fileData + "\n" + "Styles file: “" + File.decode (stylesFile.name) + "”");
1562          *     }
1563          *     else
1564          *     {
1565          *         alert ("Number of patterns: " + fileData["patterns"].length);
1566          *         alert ("Number of styles: " + fileData["styles"].length);
1567          *     }
1568          * }
1569          */
1570         jamStyles.dataFromStylesFile = function (stylesFile, includePatternsInfo)
1571         {
1572             var imageModes = [ "Bitmap", "Grayscale", "Indexed", "RGB", "CMYK", null, null, "Multichannel", "Duotone", "Lab" ];
1573             //
1574             var file;
1575             if (typeof stylesFile === 'string')
1576             {
1577                 file = new File (stylesFile);
1578             }
1579             else if (stylesFile instanceof File)
1580             {
1581                 file = stylesFile;
1582             }
1583             //
1584             var fileData;
1585             if (file.open ("r"))
1586             {
1587                 try
1588                 {
1589                     file.encoding = 'BINARY';
1590                     var formatVersion;
1591                     if (this.isStylesPalette (file))
1592                     {
1593                         formatVersion = 2;
1594                     }
1595                     else if (this.isStylesFile (file))
1596                     {
1597                         formatVersion = readBEInt (file, 2);
1598                     }
1599                     if (formatVersion === 2)
1600                     {
1601                         var magicNumber = file.read (4);
1602                         if (magicNumber === '8BSL')
1603                         {
1604                             var subVersion = readBEInt (file, 2);
1605                             if (subVersion === 3)
1606                             {
1607                                 var patternsLength = readBEInt (file, 4);
1608                                 var patternsEnd = file.tell () + patternsLength;
1609                                 if (includePatternsInfo)
1610                                 {
1611                                     var patterns = [ ];
1612                                     while (file.tell () < patternsEnd)
1613                                     {
1614                                         var pattern = { };
1615                                         var patternLength = readBEInt (file, 4);
1616                                         var patternEnd = file.tell () + patternLength;
1617                                         var patternVersion = readBEInt (file, 4);
1618                                         pattern["version"] = patternVersion;
1619                                         if (patternVersion === 1)
1620                                         {
1621                                             pattern["imageMode"] = imageModes[readBEInt (file, 4)];
1622                                             pattern["height"] = readBEInt (file, 2);
1623                                             pattern["width"] = readBEInt (file, 2);
1624                                             pattern["name"] = readUnicodeString (file);
1625                                             pattern["ID"] = readPascalString (file);
1626                                         }
1627                                         else
1628                                         {
1629                                             pattern["error"] = "Unsupported version";
1630                                         }
1631                                         patterns.push (pattern);
1632                                         file.seek (patternEnd + ((4 - (patternLength % 4)) % 4), 0);
1633                                     }
1634                                 }
1635                                 file.seek (patternsEnd, 0);
1636                                 var saveMeaningfulIds = jamEngine.meaningfulIds;
1637                                 var saveParseFriendly = jamEngine.parseFriendly;
1638                                 jamEngine.meaningfulIds = true;
1639                                 jamEngine.parseFriendly = true;
1640                                 var actionDescriptor;
1641                                 var jsonDesc;
1642                                 var styles = [ ];
1643                                 var styleCount = readBEInt (file, 4);
1644                                 for (var i = 0; i < styleCount; i++)
1645                                 {
1646                                     var style = { };
1647                                     var styleLength = readBEInt (file, 4);
1648                                     var styleEnd = file.tell () + styleLength;
1649                                     actionDescriptor = jamActions.readActionDescriptor (file);
1650                                     jsonDesc = jamEngine.classIdAndActionDescriptorToJson (0, actionDescriptor)["<descriptor>"];
1651                                     style["name"] = jsonDesc["name"][1];
1652                                     style["ID"] = jsonDesc["ID"][1];
1653                                     actionDescriptor = jamActions.readActionDescriptor (file);
1654                                     jsonDesc = jamEngine.classIdAndActionDescriptorToJson (0, actionDescriptor)["<descriptor>"];
1655                                     if ("documentMode" in jsonDesc)
1656                                     {
1657                                         style["documentMode"] = this.fromDocumentModeObject (jsonDesc["documentMode"]);
1658                                     }
1659                                     if ("blendOptions" in jsonDesc)
1660                                     {
1661                                         style["blendOptions"] = this.fromBlendOptionsObject (jsonDesc["blendOptions"]);
1662                                     }
1663                                     if ("layerEffects" in jsonDesc)
1664                                     {
1665                                         style["layerEffects"] = this.fromLayerEffectsObject (jsonDesc["layerEffects"]);
1666                                     }
1667                                     styles.push (style);
1668                                     file.seek (styleEnd, 0);
1669                                 }
1670                                 jamEngine.meaningfulIds = saveMeaningfulIds;
1671                                 jamEngine.parseFriendly = saveParseFriendly;
1672                                 fileData = { };
1673                                 if (includePatternsInfo)
1674                                 {
1675                                     fileData["patterns"] = patterns;
1676                                 }
1677                                 fileData["styles"] = styles;
1678                             }
1679                             else
1680                             {
1681                                 throw new Error ("[jamStyles.dataFromStylesFile] Unrecognized sub-version: " + subVersion);
1682                             }
1683                         }
1684                         else
1685                         {
1686                             throw new Error ("[jamStyles.dataFromStylesFile] Unrecognized magic number: " + magicNumber);
1687                         }
1688                     }
1689                     else
1690                     {
1691                         throw new Error ("[jamStyles.dataFromStylesFile] Unrecognized format version: " + formatVersion);
1692                     }
1693                 }
1694                 catch (e)
1695                 {
1696                     fileData = e.message;
1697                 }
1698                 finally
1699                 {
1700                     file.close ();
1701                 }
1702             }
1703             else
1704             {
1705                 fileData = "[jamStyles.dataFromStylesFile] Cannot open file";
1706             }
1707             return fileData;
1708         };
1709         //
1710         /**
1711          * @description Extract patterns data from a styles file (*.asl or Styles.psp).
1712          * @param {String|Object} stylesFile Styles file path string or File object
1713          * @returns {Array|String} JSON array of patterns data, or error message string
1714          * @example
1715          * function stylesFileFilter (f)
1716          * {
1717          *     return (f instanceof Folder) || jamStyles.isStylesFile (f);
1718          * }
1719          * var select = (File.fs === "Macintosh") ? stylesFileFilter : "Styles Files:*.asl,All Files:*";
1720          * var stylesFile = File.openDialog ("Select a styles file:", select);
1721          * if (stylesFile !== null)
1722          * {
1723          *     var patternsData = jamStyles.<strong>patternsFromStylesFile</strong> (stylesFile);
1724          *     if (typeof patternsData === 'string')
1725          *     {
1726          *         alert (patternsData + "\n" + "Styles file: “" + File.decode (stylesFile.name) + "”");
1727          *     }
1728          *     else
1729          *     {
1730          *         alert ("Number of patterns: " + patternsData.length);
1731          *     }
1732          * }
1733          */
1734         jamStyles.patternsFromStylesFile = function (stylesFile)
1735         {
1736             var file;
1737             if (typeof stylesFile === 'string')
1738             {
1739                 file = new File (stylesFile);
1740             }
1741             else if (stylesFile instanceof File)
1742             {
1743                 file = stylesFile;
1744             }
1745             //
1746             var patternsData;
1747             if (file.open ("r"))
1748             {
1749                 try
1750                 {
1751                     file.encoding = 'BINARY';
1752                     var formatVersion;
1753                     if (this.isStylesPalette (file))
1754                     {
1755                         formatVersion = 2;
1756                     }
1757                     else if (this.isStylesFile (file))
1758                     {
1759                         formatVersion = readBEInt (file, 2);
1760                     }
1761                     if (formatVersion === 2)
1762                     {
1763                         var magicNumber = file.read (4);
1764                         if (magicNumber === '8BSL')
1765                         {
1766                             var subVersion = readBEInt (file, 2);
1767                             if (subVersion === 3)
1768                             {
1769                                 var patternsLength = readBEInt (file, 4);
1770                                 var patternsEnd = file.tell () + patternsLength;
1771                                 var patternsData = [ ];
1772                                 while (file.tell () < patternsEnd)
1773                                 {
1774                                     var patternLength = readBEInt (file, 4);
1775                                     patternsData.push (readBytes (file, patternLength));
1776                                     file.seek ((4 - (patternLength % 4)) % 4, 1);
1777                                 }
1778                             }
1779                             else
1780                             {
1781                                 throw new Error ("[jamStyles.patternsFromStylesFile] Unrecognized sub-version: " + subVersion);
1782                             }
1783                         }
1784                         else
1785                         {
1786                             throw new Error ("[jamStyles.patternsFromStylesFile] Unrecognized magic number: " + magicNumber);
1787                         }
1788                     }
1789                     else
1790                     {
1791                         throw new Error ("[jamStyles.patternsFromStylesFile] Unrecognized format version: " + formatVersion);
1792                     }
1793                 }
1794                 catch (e)
1795                 {
1796                     patternsData = e.message;
1797                 }
1798                 finally
1799                 {
1800                     file.close ();
1801                 }
1802             }
1803             else
1804             {
1805                 patternsData = "[jamStyles.patternsFromStylesFile] Cannot open file";
1806             }
1807             return patternsData;
1808         };
1809         //
1810         /**
1811          * @description Generate a patterns file from patterns data extracted from a styles file (*.asl or Styles.psp).
1812          * @param {String|Object} patternsFile Patterns file path string or File object
1813          * @param {Array} patternsData JSON array of patterns data
1814          * @example
1815          * function stylesFileFilter (f)
1816          * {
1817          *     return (f instanceof Folder) || jamStyles.isStylesFile (f);
1818          * }
1819          * var select = (File.fs === "Macintosh") ? stylesFileFilter : "Styles Files:*.asl,All Files:*";
1820          * var stylesFile = File.openDialog ("Select a styles file:", select);
1821          * if (stylesFile !== null)
1822          * {
1823          *     var patternsData = jamStyles.patternsFromStylesFile (stylesFile);
1824          *     if (typeof patternsData === 'string')
1825          *     {
1826          *         alert (patternsData + "\n" + "Styles file: “" + File.decode (stylesFile.name) + "”");
1827          *     }
1828          *     else
1829          *     {
1830          *         var patternsFile = new File ("~/Desktop/myPatterns.pat");
1831          *         jamStyles.<strong>patternsFileFromPatterns</strong> (patternsFile, patternsData);
1832          *         patternsFile.parent.execute ();
1833          *     }
1834          * }
1835          */
1836         jamStyles.patternsFileFromPatterns = function (patternsFile, patternsData)
1837         {
1838             var file;
1839             if (typeof patternsFile === 'string')
1840             {
1841                 file = new File (patternsFile);
1842             }
1843             else if (patternsFile instanceof File)
1844             {
1845                 file = patternsFile;
1846             }
1847             //
1848             if (file.open ('w', '8BPT', '8BIM'))
1849             {
1850                 file.encoding = "BINARY";
1851                 file.write ('8BPT');
1852                 file.write ('\x00\x01');
1853                 var count = patternsData.length;
1854                 file.write (String.fromCharCode ((count >> 24) & 0xFF, (count >> 16) & 0xFF, (count >> 8) & 0xFF, count & 0xFF));
1855                 for (var index = 0; index < count; index++)
1856                 {
1857                     file.write (patternsData[index]);
1858                 }
1859                 file.close ();
1860             }
1861         };
1862     } ());
1863 }
1864 
1865 //------------------------------------------------------------------------------
1866 
1867