1 //------------------------------------------------------------------------------
  2 // File: jamHelpers.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 //  - Incremented version number to keep in sync with other modules.
 24 //  4.4:
 25 //  - Normalized error messages.
 26 //  4.3:
 27 //  - Moved extra color-related functions to new jamColors module.
 28 //  4.2:
 29 //  - Cleaned up code.
 30 //  4.0:
 31 //  - Removed reference to 'this' for main global object.
 32 //  3.6:
 33 //  - Added support for explicit format as an alternative to existing minimal format
 34 //    for most important simplified formats.
 35 //  - Removed rounding of color component numerical values and transparency opacity
 36 //    in minimal format, for consistency with explicit format.
 37 //  - Removed jamHelpers.toChannelReference ().
 38 //  - Removed jamHelpers.hexToColor () and jamHelpers.colorToHex ().
 39 //  - Added jamHelpers.hexToColorObject () and jamHelpers.hexFromColorObject ().
 40 //  - Removed jamHelpers.namedColor ().
 41 //  - Added jamHelpers.nameToColorObject ().
 42 //  - Added jamHelpers.fromBlendRangeList ().
 43 //  - Added jamHelpers.fromCurvePointList ().
 44 //  3.5:
 45 //  - Fixed simplified format for jamHelpers.toCurvePointList ().
 46 //  - Improved book color simplified format for jamHelpers.toColorObject () and
 47 //    jamHelpers.fromColorObject.
 48 //  3.4:
 49 //  - Added jamHelpers.defineNamedColorsSet (), jamHelpers.enumerateNamedColors (),
 50 //    jamHelpers.namedColor (), jamHelpers.hexToColor () and jamHelpers.colorToHex ().
 51 //  3.3:
 52 //  - Added links to respective simplified formats specification pages.
 53 //  3.2:
 54 //  - Incremented version number to keep in sync with other modules.
 55 //  3.1:
 56 //  - Incremented version number to keep in sync with other modules.
 57 //  3.0:
 58 //  - Applied the redefined JSON AM Reference format.
 59 //  2.0:
 60 //  - Renamed jamHelpers.js to jamHelpers.jsxinc.
 61 //  - Replaced all single color mode functions:
 62 //    jamHelpers.toBookColorObject (), etc. with jamHelpers.toColorObject ().
 63 //  - Added jamHelpers.fromColorObject ().
 64 //  - Added jamHelpers.toGradientObject () and jamHelpers.fromGradientObject ().
 65 //  - Renamed jamHelpers.toEnumChannelReference () to
 66 //    jamHelpers.toChannelReference ().
 67 //  - Revamped simplified format for jamHelpers.toCurvesAdjustmentList ().
 68 //  - Renamed jamHelpers.toHueSaturationAdjustmentList () to
 69 //    jamHelpers.toHueSatAdjustmentV2List ().
 70 //  - Removed jamHelpers.toHueSatMasterAdjustmentList ();
 71 //    jamHelpers.toHueSatAdjustmentV2List () should be used instead.
 72 //  - Revamped simplified format for jamHelpers.toBlendRangeList ().
 73 //  - Added jamHelpers.fromIntegerList ().
 74 //  - Revamped simplified format for jamHelpers.toPointObject ().
 75 //  - Added jamHelpers.toPointList ().
 76 //  - Revamped simplified format for jamHelpers.toOffsetObject ().
 77 //  - Revamped simplified format for jamHelpers.toRectangleObject ().
 78 //  - Removed jamHelpers.toRoundedRectangleObject (), merged into
 79 //    jamHelpers.toRectangleObject ().
 80 //  - Revamped simplified format for jamHelpers.toEllipseObject ().
 81 //  - Revamped simplified format for jamHelpers.toCustomShapeObject ().
 82 //  - Removed jamHelpers.toPointsPolygonObject (); jamHelpers.toPointList ()
 83 //    should be used instead (cf. example).
 84 //  - Revamped simplified format for jamHelpers.toCurvePointList ();
 85 //    removed unused unit parameter too.
 86 //  - Revamped simplified format for jamHelpers.toRationalPointList ().
 87 //  - Revamped simplified format for jamHelpers.toPathComponentList ().
 88 //  - Added jamHelpers.fromPathComponentList ().
 89 //  - Moved jamHelpers.toDistanceUnit () and jamHelpers.fromDistanceUnit () to
 90 //    jamUtils.toDistanceUnit () and jamUtils.fromDistanceUnit ().
 91 //  - Removed jamHelpers.toHexaString () and jamHelpers.fromHexaString ();
 92 //    jamEngine.dataToHexaString () and jamEngine.hexaToDataString () should
 93 //    be used instead.
 94 //  1.0:
 95 //  - Initial release.
 96 //------------------------------------------------------------------------------
 97 
 98 /**
 99  * @fileOverview
100  * @name jamHelpers.jsxinc
101  * @author Michel MARIANI
102  */
103 
104 //------------------------------------------------------------------------------
105 
106 if (typeof jamHelpers !== 'object')
107 {
108     /**
109      * Global object (used to simulate a namespace in JavaScript) containing
110      * a set of helper functions for scripts written with the
111      * <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-action-manager/">JSON Action Manager</a> engine.
112      * @author Michel MARIANI
113      * @version 4.5
114      * @namespace
115      */
116     var jamHelpers = { };
117     //
118     (function ()
119     {
120         /**
121          * @description Get a color object in JSON AM Data Format from a simplified color JSON object or JSON array.
122          * @param {Object|Array} color Simplified color JSON object or JSON array
123          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/color-object-simplified-format/">Color Object Simplified Format</a>)
124          * @returns {Object|Array} Color object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
125          * @see jamHelpers.fromColorObject
126          * @example
127          * jamEngine.jsonPlay
128          * (
129          *     "set",
130          *     {
131          *         "target": { "<reference>": [ { "color": { "<property>": "foregroundColor" } } ] },
132          *         "to": jamHelpers.<strong>toColorObject</strong> ({ "luminance": 77, "a": -43, "b": 68 })
133          *     }
134          * );
135          * jamEngine.jsonPlay
136          * (
137          *     "set",
138          *     {
139          *         "target": { "<reference>": [ { "color": { "<property>": "backgroundColor" } } ] },
140          *         "to": jamHelpers.<strong>toColorObject</strong> ([ "RGBColor", [ 184, 0, 184 ] ])
141          *     }
142          * );
143          */
144         jamHelpers.toColorObject = function (color)
145         {
146             var colorObject;
147             if (color.constructor === Object)
148             {
149                 function restoreDesc (desc)
150                 {
151                     var restoredDesc = { };
152                     for (var key in desc)
153                     {
154                         if (desc.hasOwnProperty (key))
155                         {
156                             var value = desc[key];
157                             var typedValue = null;
158                             switch (key)
159                             {
160                                 case "book":
161                                 case "name":
162                                     typedValue = [ "<string>", localize (value) ];
163                                     break;
164                                 case "bookKey":
165                                     typedValue = [ "<data>", value ];
166                                     break;
167                                 case "bookID":
168                                     typedValue = [ "<integer>", value ];
169                                     break;
170                                 case "a":
171                                 case "b":
172                                 case "black":
173                                 case "blue":
174                                 case "brightness":
175                                 case "cyan":
176                                 case "gray":
177                                 case "green":
178                                 case "luminance":
179                                 case "magenta":
180                                 case "red":
181                                 case "saturation":
182                                 case "yellowColor":
183                                     typedValue = [ "<double>", value ];
184                                     break;
185                                 case "hue":
186                                     typedValue = [ "<unitDouble>", [ "angleUnit", value ] ];
187                                     break;
188                                 case "color":
189                                     var colorClass;
190                                     if ((("book" in value) && ("name" in value)) || (("bookID" in value) && ("bookKey" in value)))
191                                     {
192                                         colorClass = "bookColor";
193                                     }
194                                     else if (("cyan" in value) && ("magenta" in value) && ("yellowColor" in value) && ("black" in value))
195                                     {
196                                         colorClass = "CMYKColorClass";
197                                     }
198                                     else if ("gray" in value)
199                                     {
200                                         colorClass = "grayscale";
201                                     }
202                                     else if (("hue" in value) && ("saturation" in value) && ("brightness" in value))
203                                     {
204                                         colorClass = "HSBColorClass";
205                                     }
206                                     else if (("luminance" in value) && ("a" in value) && ("b" in value))
207                                     {
208                                         colorClass = "labColor";
209                                     }
210                                     else if (("red" in value) && ("green" in value) && ("blue" in value))
211                                     {
212                                         colorClass = "RGBColor";
213                                     }
214                                     typedValue = [ "<object>", [ colorClass, restoreDesc (value) ] ];
215                                     break;
216                             }
217                             if (typedValue)
218                             {
219                                 restoredDesc[key] = typedValue;
220                             }
221                         }
222                     }
223                     return restoredDesc;
224                 }
225                 colorObject = restoreDesc ({ "color": color })["color"];
226             }
227             else if (color.constructor === Array)
228             {
229                 var colorClass = color[0];
230                 switch (jamEngine.uniIdStrToId (colorClass))
231                 {
232                     case jamEngine.uniIdStrToId ("bookColor"):
233                         switch (color[1].length)
234                         {
235                             case 2:
236                                 if (typeof color[1][0] === 'string')
237                                 {
238                                     colorObject =
239                                     [
240                                         "<object>",
241                                         [
242                                             "bookColor",
243                                             {
244                                                 "book": [ "<string>", color[1][0] ],
245                                                 "name": [ "<string>", color[1][1] ]
246                                             }
247                                         ]
248                                     ];
249                                 }
250                                 else if (typeof color[1][0] === 'number')
251                                 {
252                                     colorObject =
253                                     [
254                                         "<object>",
255                                         [
256                                             "bookColor",
257                                             {
258                                                 "bookID": [ "<integer>", color[1][0] ],
259                                                 "bookKey": [ "<data>", color[1][1] ]
260                                             }
261                                         ]
262                                     ];
263                                 }
264                                 break;
265                             case 4:
266                                 colorObject =
267                                 [
268                                     "<object>",
269                                     [
270                                         "bookColor",
271                                         {
272                                             "book": [ "<string>", color[1][0] ],
273                                             "name": [ "<string>", color[1][1] ],
274                                             "bookID": [ "<integer>", color[1][2] ],
275                                             "bookKey": [ "<data>", color[1][3] ]
276                                         }
277                                     ]
278                                 ];
279                                 break;
280                         }
281                         break;
282                     case jamEngine.uniIdStrToId ("CMYKColorClass"):
283                         colorObject =
284                         [
285                             "<object>",
286                             [
287                                 "CMYKColorClass",
288                                 {
289                                     "cyan": [ "<double>", color[1][0] ],
290                                     "magenta": [ "<double>", color[1][1] ],
291                                     "yellowColor": [ "<double>", color[1][2] ],
292                                     "black": [ "<double>", color[1][3] ]
293                                 }
294                             ]
295                         ];
296                         break;
297                     case jamEngine.uniIdStrToId ("grayscale"):
298                         colorObject =
299                         [
300                             "<object>",
301                             [
302                                 "grayscale",
303                                 {
304                                     "gray": [ "<double>", (color[1].constructor === Array) ? color[1][0] : color[1] ]
305                                 }
306                             ]
307                         ];
308                         break;
309                     case jamEngine.uniIdStrToId ("HSBColorClass"):
310                         colorObject =
311                         [
312                             "<object>",
313                             [
314                                 "HSBColorClass",
315                                 {
316                                     "hue": [ "<unitDouble>", [ "angleUnit", color[1][0] ] ],
317                                     "saturation": [ "<double>", color[1][1] ],
318                                     "brightness": [ "<double>", color[1][2] ]
319                                 }
320                             ]
321                         ];
322                         break;
323                     case jamEngine.uniIdStrToId ("labColor"):
324                         colorObject =
325                         [
326                             "<object>",
327                             [
328                                 "labColor",
329                                 {
330                                     "luminance": [ "<double>", color[1][0] ],
331                                     "a": [ "<double>", color[1][1] ],
332                                     "b": [ "<double>", color[1][2] ]
333                                 }
334                             ]
335                         ];
336                         break;
337                     case jamEngine.uniIdStrToId ("RGBColor"):
338                         colorObject =
339                         [
340                             "<object>",
341                             [
342                                 "RGBColor",
343                                 {
344                                     "red": [ "<double>", color[1][0] ],
345                                     "green": [ "<double>", color[1][1] ],
346                                     "blue": [ "<double>", color[1][2] ]
347                                 }
348                             ]
349                         ];
350                         break;
351                     default:
352                         throw new Error ("[jamHelpers.toColorObject] Unrecognized color class: " + colorClass);
353                         break;
354                 }
355             }
356             return colorObject;
357         };
358         //
359         /**
360          * @description Get a simplified color JSON object or JSON array from a color object in JSON AM Data Format.
361          * @param {Object|Array} colorObject Color object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
362          * @param {Boolean} [explicit] Explicit flag: if true, return an explicit JSON object instead of a minimal JSON array
363          * @returns {Object|Array} Simplified color JSON object or JSON array
364          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/color-object-simplified-format/">Color Object Simplified Format</a>)
365          * @see jamHelpers.toColorObject
366          * @example
367          * jamEngine.meaningfulIds = true;
368          * var resultObj = jamEngine.jsonGet ([ { "application": { "<enumerated>": { "ordinal": "targetEnum" } } } ]);
369          * var fgColorArr = jamHelpers.<strong>fromColorObject</strong> (resultObj["foregroundColor"], true);
370          * alert (jamJSON.stringify (fgColorArr));
371          * // -> { "hue": 260, "saturation": 48, "brightness": 92 } // Lavender
372          * var bgColorArr = jamHelpers.<strong>fromColorObject</strong> (resultObj["backgroundColor"]);
373          * alert (jamJSON.stringify (bgColorArr));
374          * // -> [ "bookColor", [ "PANTONE® solid coated", "PANTONE 2395 C", 3002, " 2395C" ] ] // Vivid Magenta
375          */
376         jamHelpers.fromColorObject = function (colorObject, explicit)
377         {
378             var color;
379             if (explicit)
380             {
381                 color = jamEngine.simplifyObject (colorObject);
382             }
383             else
384             {
385                 var normalizedColorObject = jamEngine.normalizeJsonItem (colorObject, { meaningfulIds: true, parseFriendly: true });
386                 var colorClass = normalizedColorObject[1][0];
387                 var colorDesc = normalizedColorObject[1][1];
388                 switch (colorClass)
389                 {
390                     case "bookColor":
391                         var book = colorDesc["book"][1];
392                         var name = colorDesc["name"][1];
393                         if (("bookID" in colorDesc) && ("bookKey" in colorDesc))
394                         {
395                             var bookID = colorDesc["bookID"][1];
396                             var bookKey = colorDesc["bookKey"][1];
397                             color = [ colorClass, [ book, name, bookID, bookKey ] ];
398                         }
399                         else
400                         {
401                             color = [ colorClass, [ book, name ] ];
402                         }
403                         break;
404                     case "CMYKColorClass":
405                         var cyan = colorDesc["cyan"][1];
406                         var magenta =  colorDesc["magenta"][1];
407                         var yellowColor = colorDesc["yellowColor"][1];
408                         var black = colorDesc["black"][1];
409                         color = [ colorClass, [ cyan, magenta, yellowColor, black ] ];
410                         break;
411                     case "grayscale":
412                         var gray = colorDesc["gray"][1];
413                         color = [ colorClass, [ gray ] ];
414                         break;
415                     case "HSBColorClass":
416                         var hue = colorDesc["hue"][1][1];
417                         var saturation = colorDesc["saturation"][1];
418                         var brightness = colorDesc["brightness"][1];
419                         color = [ colorClass, [ hue, saturation, brightness ] ];
420                         break;
421                     case "labColor":
422                         var luminance = colorDesc["luminance"][1];
423                         var a = colorDesc["a"][1];
424                         var b = colorDesc["b"][1];
425                         color = [ colorClass, [ luminance, a, b ] ];
426                         break;
427                     case "RGBColor":
428                         var red =  colorDesc["red"][1];
429                         var green = colorDesc["green"][1];
430                         var blue = colorDesc["blue"][1];
431                         color = [ colorClass, [ red, green, blue ] ];
432                         break;
433                     default:
434                         throw new Error ("[jamHelpers.fromColorObject] Unrecognized color class: " + colorClass);
435                         break;
436                 }
437             }
438             return color;
439         };
440         //
441         /**
442          * @description Get a color object in JSON AM Data Format from a color name belonging to a named colors set.
443          * @param {String} setName Named colors set name (case-insensitive, whitespace ignored):<br />
444          * <ul>
445          * <li>
446          * "CSS" or "SVG" or "W3C":
447          * <a href="http://www.w3.org/TR/css3-color/#svg-color">CSS Color Module Level 3 - Extended color keywords</a><br />
448          * or <a href="http://www.w3.org/TR/SVG/types.html#ColorKeywords">SVG 1.1 (Second Edition) - Recognized color keyword names</a>
449          * </li>
450          * <li>"X11": <a href="http://www.thomas-guettler.de/rgb.txt.html">Colors of /usr/lib/X11/rgb.txt</a></li>
451          * <li>or name of a custom named colors set defined by jamColors.defineNamedColorsSet ()</li>
452          * </ul>
453          * @param {String} colorName Color name (case-insensitive, whitespace ignored)
454          * @returns {Object|Array} Color object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
455          * @example
456          * jamEngine.jsonPlay
457          * (
458          *     "set",
459          *     {
460          *         "target": { "<reference>": [ { "color": { "<property>": "backgroundColor" } } ] },
461          *         "to": jamHelpers.<strong>nameToColorObject</strong> ("W3C", "Slate Grey")
462          *     }
463          * );
464          * jamEngine.jsonPlay
465          * (
466          *     "set",
467          *     {
468          *         "target": { "<reference>": [ { "color": { "<property>": "foregroundColor" } } ] },
469          *         "to": jamHelpers.<strong>nameToColorObject</strong> ("X11", "Deep Pink 4")
470          *     }
471          * );
472          */
473         jamHelpers.nameToColorObject = function (setName, colorName)
474         {
475             return this.toColorObject (jamColors.nameToColor (setName, colorName));
476         };
477         //
478         /**
479          * @description Get a color object in JSON AM Data Format from a RGB color string in HTML/CSS hexadecimal notation.
480          * @param {String} hexColorString RGB color string in HTML/CSS hexadecimal notation (3 or 6 digits, starting with an optional # sign)
481          * @returns {Object|Array} Color object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
482          * @see jamHelpers.hexFromColorObject
483          * @example
484          * jamEngine.jsonPlay
485          * (
486          *     "set",
487          *     {
488          *         "target": { "<reference>": [ { "color": { "<property>": "backgroundColor" } } ] },
489          *         "to": jamHelpers.<strong>hexToColorObject</strong> ("#F0F")
490          *     }
491          * );
492          * jamEngine.jsonPlay
493          * (
494          *     "set",
495          *     {
496          *         "target": { "<reference>": [ { "color": { "<property>": "foregroundColor" } } ] },
497          *         "to": jamHelpers.<strong>hexToColorObject</strong> ("#00FF00")
498          *     }
499          * );
500          */
501         jamHelpers.hexToColorObject = function (hexColorString)
502         {
503             return this.toColorObject (["RGBColor", jamColors.hexToRgb (hexColorString)]);
504         };
505         //
506         /**
507          * @description Get a RGB color string in HTML/CSS hexadecimal notation from a color object in JSON AM Data Format.
508          * @param {Object|Array} colorObject Color object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
509          * @param {Boolean} [noSign] Do not add a leading # sign (false by default)
510          * @param {Boolean} [lowercase] Use lowercase hexadecimal digits (false by default)
511          * @returns {String|Null} RGB color string in HTML/CSS hexadecimal notation (6 digits, starting with a # sign),
512          * or null if color object in JSON AM Data Format is not RGB
513          * @see jamHelpers.hexToColorObject
514          * @example
515          * jamEngine.meaningfulIds = true;
516          * var resultObj = jamEngine.jsonGet ([ { "application": { "<enumerated>": { "ordinal": "targetEnum" } } } ]);
517          * var fgHexColor = jamHelpers.<strong>hexFromColorObject</strong> (resultObj["foregroundColor"]);
518          * alert (jamJSON.stringify (fgHexColor));  // -> "#FFDEAD"
519          * var bgHexColor = jamHelpers.<strong>hexFromColorObject</strong> (resultObj["backgroundColor"], true, true);
520          * alert (jamJSON.stringify (bgHexColor));  // -> "4b1f5e"
521          */
522         jamHelpers.hexFromColorObject = function (colorObject, noSign, lowercase)
523         {
524             var color = this.fromColorObject (colorObject);
525             return (color[0] === "RGBColor") ? jamColors.rgbToHex (color[1], noSign, lowercase) : null;
526         };
527         //
528         /**
529          * @description Get a gradient object in JSON AM Data Format from a simplified gradient JSON object or JSON array.
530          * @param {Object|Array} gradient Simplified gradient JSON object or JSON array
531          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/gradient-object-simplified-format/">Gradient Object Simplified Format</a>)
532          * @returns {Object|Array} Gradient object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
533          * @see jamHelpers.fromGradientObject
534          * @example
535          * var customStopsGradient =
536          * {
537          *     "gradientForm": "customStops",
538          *     "interpolation": 4096,
539          *     "colors":
540          *     [
541          *         {
542          *             "location": 0, "midpoint": 50, "type": "userStop",
543          *             "color": { "red": 255, "green": 0, "blue": 0 }
544          *         },
545          *         {
546          *             "location": 4096, "midpoint": 50, "type": "userStop",
547          *             "color": { "red": 95, "green": 95, "blue": 0 }
548          *         }
549          *     ],
550          *     "transparency":
551          *     [
552          *         { "location": 0, "midpoint": 50, "opacity": 75 },
553          *         { "location": 4096, "midpoint": 50, "opacity": 100 }
554          *     ]
555          * };
556          * jamEngine.jsonPlay
557          * (
558          *     "gradientClassEvent",
559          *     {
560          *         "from": jamHelpers.toPointObject ([ [ 50.0, 12.5 ], "percentUnit" ]),
561          *         "to": jamHelpers.toPointObject ([ [ 50.0, 75.0 ], "percentUnit" ]),
562          *         "opacity": { "<unitDouble>": { "percentUnit": 50 } },
563          *         "mode": { "<enumerated>": { "blendMode": "hardLight" } },
564          *         "type": { "<enumerated>": { "gradientType": "linear" } },
565          *         "gradient": jamHelpers.<strong>toGradientObject</strong> (customStopsGradient)
566          *     }
567          * );
568          * @example
569          * var colorNoiseGradient =
570          * [
571          *     "Color Noise",
572          *     "colorNoise",
573          *     345807450,
574          *     false,
575          *     true,
576          *     1024,
577          *     "RGBColor",
578          *     [ 0, 18, 25, 0 ],
579          *     [ 100, 60, 73, 100 ]
580          * ];
581          * jamEngine.jsonPlay
582          * (
583          *     "gradientClassEvent",
584          *     {
585          *         "from": jamHelpers.toPointObject ([ [ 12.5, 50.0 ], "percentUnit" ]),
586          *         "to": jamHelpers.toPointObject ([ [ 75.0, 50.0 ], "percentUnit" ]),
587          *         "opacity": { "<unitDouble>": { "percentUnit": 100 } },
588          *         "mode": { "<enumerated>": { "blendMode": "normal" } },
589          *         "type": { "<enumerated>": { "gradientType": "linear" } },
590          *         "gradient": jamHelpers.<strong>toGradientObject</strong> (colorNoiseGradient)
591          *     }
592          * );
593          */
594         jamHelpers.toGradientObject = function (gradient)
595         {
596             var gradientObject;
597             if (gradient.constructor === Object)
598             {
599                 var that = this;
600                 function restoreDesc (desc)
601                 {
602                     var restoredDesc = { };
603                     for (var key in desc)
604                     {
605                         if (desc.hasOwnProperty (key))
606                         {
607                             var value = desc[key];
608                             var typedValue = null;
609                             var restoredList;
610                             switch (key)
611                             {
612                                 case "showTransparency":
613                                 case "vectorColor":
614                                     typedValue = [ "<boolean>", value ];
615                                     break;
616                                 case "name":
617                                     typedValue = [ "<string>", localize (value) ];
618                                     break;
619                                 case "gradientForm":
620                                     typedValue = [ "<enumerated>", [ "gradientForm", value ] ];
621                                     break;
622                                 case "type":
623                                     typedValue = [ "<enumerated>", [ "colorStopType", value ] ];
624                                     break;
625                                 case "colorSpace":
626                                     typedValue = [ "<enumerated>", [ "colorSpace", value ] ];
627                                     break;
628                                 case "location":
629                                 case "midpoint":
630                                 case "randomSeed":
631                                 case "smoothness":
632                                     typedValue = [ "<integer>", value ];
633                                     break;
634                                 case "interpolation":
635                                     typedValue = [ "<double>", value ];
636                                     break;
637                                 case "opacity":
638                                     typedValue = [ "<unitDouble>", [ "percentUnit", value ] ];
639                                     break;
640                                 case "colors":
641                                     restoredList = [ ];
642                                     for (var i = 0; i < value.length; i++)
643                                     {
644                                         restoredList.push ([ "<object>", [ "colorStop", restoreDesc (value[i]) ] ]);
645                                     }
646                                     typedValue = [ "<list>", restoredList ];
647                                     break;
648                                 case "transparency":
649                                     restoredList = [ ];
650                                     for (var i = 0; i < value.length; i++)
651                                     {
652                                         restoredList.push ([ "<object>", [ "transparencyStop", restoreDesc (value[i]) ] ]);
653                                     }
654                                     typedValue = [ "<list>", restoredList ];
655                                     break;
656                                 case "minimum":
657                                 case "maximum":
658                                     restoredList = [ ];
659                                     for (var i = 0; i < value.length; i++)
660                                     {
661                                         restoredList.push ([ "<integer>", value[i] ]);
662                                     }
663                                     typedValue = [ "<list>", restoredList ];
664                                     break;
665                                 case "color":
666                                     typedValue = that.toColorObject (value);
667                                     break;
668                                 case "gradient":
669                                     typedValue = [ "<object>", [ "gradientClassEvent", restoreDesc (value) ] ];
670                                     break;
671                             }
672                             if (typedValue)
673                             {
674                                 restoredDesc[key] = typedValue;
675                             }
676                         }
677                     }
678                     return restoredDesc;
679                 }
680                 gradientObject = restoreDesc ({ "gradient": gradient })["gradient"];
681             }
682             else if (gradient.constructor === Array)
683             {
684                 var gradientObj = { };
685                 var gradientName = gradient[0];  // Name
686                 if (gradientName)   // Can be null (or empty string)
687                 {
688                     gradientObj["name"] = [ "<string>", gradientName ];
689                 }
690                 var gradientForm = gradient[1];  // Gradient Type
691                 gradientObj["gradientForm"] = [ "<enumerated>", [ "gradientForm", gradientForm ] ];
692                 switch (jamEngine.uniIdStrToId (gradientForm))
693                 {
694                     case jamEngine.uniIdStrToId ("customStops"):    // Solid
695                         gradientObj["interpolation"] = [ "<double>", gradient[2] ];  // Smoothness
696                         var colorStops = gradient[3];
697                         var colorsArr = [ ];
698                         for (var i = 0; i < colorStops.length; i++)
699                         {
700                             var colorStopObj = { };
701                             colorStopObj["location"] = [ "<integer>", colorStops[i][0] ];
702                             colorStopObj["midpoint"] = [ "<integer>", colorStops[i][1] ];
703                             var type = colorStops[i][2];
704                             colorStopObj["type"] = [ "<enumerated>", [ "colorStopType", type ] ];
705                             switch (jamEngine.uniIdStrToId (type))
706                             {
707                                 case jamEngine.uniIdStrToId ("userStop"):
708                                     colorStopObj["color"] = this.toColorObject (colorStops[i][3]);
709                                     break;
710                                 case jamEngine.uniIdStrToId ("backgroundColor"):
711                                 case jamEngine.uniIdStrToId ("foregroundColor"):
712                                     break;
713                                 default:
714                                     throw new Error ("[jamHelpers.toGradientObject] Unrecognized color stop type: " + type);
715                                     break;
716                             }
717                             colorsArr.push ([ "<object>", [ "colorStop", colorStopObj ] ]);
718                         }
719                         gradientObj["colors"] = [ "<list>", colorsArr ];
720                         var transparencyStops = gradient[4];
721                         if (typeof transparencyStops !== 'undefined')
722                         {
723                             var transparencyArr = [ ];
724                             for (var j = 0; j < transparencyStops.length; j++)
725                             {
726                                 var transparencyStopObj = { };
727                                 transparencyStopObj["location"] = [ "<integer>", transparencyStops[j][0] ];
728                                 transparencyStopObj["midpoint"] = [ "<integer>", transparencyStops[j][1] ];
729                                 transparencyStopObj["opacity"] = [ "<unitDouble>", [ "percentUnit", transparencyStops[j][2] ] ];
730                                 transparencyArr.push ([ "<object>", [ "transparencyStop", transparencyStopObj ] ]);
731                             }
732                             gradientObj["transparency"] = [ "<list>", transparencyArr ];
733                         }
734                         break;
735                     case jamEngine.uniIdStrToId ("colorNoise"): // Noise
736                         gradientObj["randomSeed"] = [ "<integer>", gradient[2] ];    // Randomize
737                         gradientObj["showTransparency"] = [ "<boolean>", gradient[3] ];  // Add Transparency
738                         gradientObj["vectorColor"] = [ "<boolean>", gradient[4] ];   // Restrict Colors
739                         gradientObj["smoothness"] = [ "<integer>", gradient[5] ];    // Roughness
740                         var colorSpace = gradient[6];
741                         gradientObj["colorSpace"] = [ "<enumerated>", [ "colorSpace", colorSpace ] ];   // Color Model
742                         switch (jamEngine.uniIdStrToId (colorSpace))
743                         {
744                             case jamEngine.uniIdStrToId ("RGBColor"):
745                             case jamEngine.uniIdStrToId ("HSBColorEnum"):
746                             case jamEngine.uniIdStrToId ("labColor"):
747                                 break;
748                             default:
749                                 throw new Error ("[jamHelpers.toGradientObject] Unrecognized color space: " + colorSpace);
750                                 break;
751                         }
752                         gradientObj["minimum"] = this.toIntegerList (gradient[7]); // Three color components (0 to 100) + transparency (0)
753                         gradientObj["maximum"] = this.toIntegerList (gradient[8]); // Three color components (0 to 100) + transparency (100)
754                         break;
755                     default:
756                         throw new Error ("[jamHelpers.toGradientObject] Unrecognized gradient form: " + gradientForm);
757                         break;
758                 }
759                 gradientObject = [ "<object>", [ "gradientClassEvent", gradientObj ] ];
760             }
761             return gradientObject;
762         };
763         //
764         /**
765          * @description Get a simplified gradient JSON object or JSON array from a gradient object in JSON AM Data Format.
766          * @param {Object|Array} gradientObject Gradient object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
767          * @param {Boolean} [explicit] Explicit flag: if true, return an explicit JSON object instead of a minimal JSON array
768          * @returns {Object|Array} Simplified gradient JSON object or JSON array
769          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/gradient-object-simplified-format/">Gradient Object Simplified Format</a>)
770          * @see jamHelpers.toGradientObject
771          * @example
772          * var gradientObject =
773          * {
774          *     "<object>":
775          *     {
776          *         "gradientClassEvent":
777          *         {
778          *             "name": { "<string>": "N-86" },
779          *             "gradientForm": { "<enumerated>": { "gradientForm": "colorNoise" } },
780          *             "showTransparency": { "<boolean>": false },
781          *             "vectorColor": { "<boolean>": true },
782          *             "colorSpace": { "<enumerated>": { "colorSpace": "labColor" } },
783          *             "randomSeed": { "<integer>": 17 },
784          *             "smoothness": { "<integer>": 3523 },   // 3523/4096 => 86 %
785          *             "minimum":
786          *             {
787          *                 "<list>":
788          *                 [
789          *                     { "<integer>": 25 },
790          *                     { "<integer>": 0 },
791          *                     { "<integer>": 33 },
792          *                     { "<integer>": 0 }
793          *                 ]
794          *             },
795          *             "maximum":
796          *             {
797          *                 "<list>":
798          *                 [
799          *                     { "<integer>": 75 },
800          *                     { "<integer>": 67 },
801          *                     { "<integer>": 100 },
802          *                     { "<integer>": 100 }
803          *                 ]
804          *             }
805          *         }
806          *     }
807          * };
808          * alert (jamJSON.stringify (jamHelpers.<strong>fromGradientObject</strong> (gradientObject, true), '\t'));
809          * // ->
810          * // {
811          * //     "name": "N-86",
812          * //     "gradientForm": "colorNoise",
813          * //     "randomSeed": 17,
814          * //     "showTransparency": false,
815          * //     "vectorColor": true,
816          * //     "smoothness": 3523,
817          * //     "colorSpace": "labColor",
818          * //     "minimum": [ 25, 0, 33, 0 ],
819          * //     "maximum": [ 75, 67, 100, 100 ]
820          * // }
821          * alert (jamJSON.stringify (jamHelpers.<strong>fromGradientObject</strong> (gradientObject)));
822          * // -> [ "N-86", "colorNoise", 17, false, true, 3523, "labColor", [ 25, 0, 33, 0 ], [ 75, 67, 100, 100 ] ]
823          */
824         jamHelpers.fromGradientObject = function (gradientObject, explicit)
825         {
826             var gradient;
827             if (explicit)
828             {
829                 gradient = jamEngine.simplifyObject (gradientObject);
830             }
831             else
832             {
833                 gradient = [ ];
834                 var normalizedGradientObject = jamEngine.normalizeJsonItem (gradientObject, { meaningfulIds: true, parseFriendly: true });
835                 var gradientDesc = normalizedGradientObject[1][1];
836                 var name = gradientDesc["name"];
837                 gradient.push ((name) ? name[1] : null);
838                 var gradientForm = gradientDesc["gradientForm"][1][1];
839                 gradient.push (gradientForm);
840                 switch (gradientForm)
841                 {
842                     case "customStops":
843                         gradient.push (gradientDesc["interpolation"][1]);
844                         var colors = gradientDesc["colors"][1];
845                         var colorStops = [ ];
846                         for (var i = 0; i < colors.length; i++)
847                         {
848                             var colorStop = colors[i][1][1];
849                             var colorStopArr = [ ];
850                             colorStopArr.push (colorStop["location"][1]);
851                             colorStopArr.push (colorStop["midpoint"][1]);
852                             var type = colorStop["type"][1][1];
853                             colorStopArr.push (type);
854                             switch (type)
855                             {
856                                 case "userStop":
857                                     colorStopArr.push (this.fromColorObject (colorStop["color"]));
858                                     break;
859                                 case "backgroundColor":
860                                 case "foregroundColor":
861                                     break;
862                                 default:
863                                     throw new Error ("[jamHelpers.fromGradientObject] Unrecognized color stop type: " + type);
864                                     break;
865                             }
866                             colorStops.push (colorStopArr);
867                         }
868                         gradient.push (colorStops);
869                         var transparency = gradientDesc["transparency"][1];
870                         var transparencyStops = [ ];
871                         for (var j = 0; j < transparency.length; j++)
872                         {
873                             var transparencyStop = transparency[j][1][1];
874                             var transparencyStopArr = [ ];
875                             transparencyStopArr.push (transparencyStop["location"][1]);
876                             transparencyStopArr.push (transparencyStop["midpoint"][1]);
877                             transparencyStopArr.push (transparencyStop["opacity"][1][1]);
878                             transparencyStops.push (transparencyStopArr);
879                         }
880                         gradient.push (transparencyStops);
881                         break;
882                     case "colorNoise":
883                         gradient.push (gradientDesc["randomSeed"][1]);
884                         gradient.push (gradientDesc["showTransparency"][1]);
885                         gradient.push (gradientDesc["vectorColor"][1]);
886                         gradient.push (gradientDesc["smoothness"][1]);
887                         var colorSpace = gradientDesc["colorSpace"][1][1]
888                         gradient.push (colorSpace);
889                         switch (colorSpace)
890                         {
891                             case "RGBColor":
892                             case "HSBColorEnum":
893                             case "labColor":
894                                 break;
895                             default:
896                                 throw new Error ("[jamHelpers.fromGradientObject] Unrecognized color space: " + colorSpace);
897                                 break;
898                         }
899                         gradient.push (this.fromIntegerList (gradientDesc["minimum"]));
900                         gradient.push (this.fromIntegerList (gradientDesc["maximum"]));
901                         break;
902                     default:
903                         throw new Error ("[jamHelpers.fromGradientObject] Unrecognized gradient form: " + gradientForm);
904                         break;
905                 }
906             }
907             return gradient;
908         };
909         //
910         /**
911          * @description Get a curves adjustment list in JSON AM Data Format from a JSON array of channel and individual channel curves values.
912          * @param {Array} curvesAdjustmentsArr JSON array of channel and individual channel curves values
913          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/curves-adjustment-list-simplified-format/">Curves Adjustment List Simplified Format</a>)
914          * @returns {Object|Array} Curves adjustment list in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
915          * @example
916          * var curvesAdjustments =
917          * [
918          *     [ "red", [ "curve", [ [ 0, 0 ], [ 77, 51 ], [ 178, 204 ], [ 255, 255 ] ] ] ],
919          *     [ "green", [ "curve", [ [ 0, 255 ], [ 127, 0 ], [ 255, 255 ] ] ] ],
920          *     [ "blue", [ "curve", [ [ 0, 255 ], [ 255, 0 ] ] ] ]
921          * ];
922          * jamEngine.jsonPlay
923          * (
924          *     "make",
925          *     {
926          *         "target": { "<reference>": [ { "adjustmentLayer": { "<class>": null } } ] },
927          *         "using":
928          *         {
929          *             "<object>":
930          *             {
931          *                 "adjustmentLayer":
932          *                 {
933          *                     "name": { "<string>": "Curves" },
934          *                     "opacity": { "<unitDouble>": { "percentUnit": 75 } },
935          *                     "type":
936          *                     {
937          *                         "<object>":
938          *                         {
939          *                             "curves":
940          *                             {
941          *                                 "adjustment": jamHelpers.<strong>toCurvesAdjustmentList</strong> (curvesAdjustments)
942          *                             }
943          *                         }
944          *                     }
945          *                 }
946          *             }
947          *         }
948          *     }
949          * );
950          */
951         jamHelpers.toCurvesAdjustmentList = function (curvesAdjustmentsArr)
952         {
953             var curvesAdjustmentListArr = [ ];
954             for (var i = 0; i < curvesAdjustmentsArr.length; i++)
955             {
956                 var curvesAdjustment = curvesAdjustmentsArr[i];
957                 var channel = [ "<reference>", [ [ "channel", [ "<enumerated>", [ "channel", curvesAdjustment[0] ] ] ] ] ];
958                 var info = curvesAdjustment[1];
959                 var type = info[0];
960                 var points = info[1];
961                 var pointArr = [ ];
962                 switch (jamEngine.uniIdStrToId (type))
963                 {
964                     case jamEngine.uniIdStrToId ("mapping"):
965                         for (var j = 0; j < points.length; j++)
966                         {
967                             pointArr.push ([ "<integer>", points[j] ]);
968                         }
969                         var mapping = [ "<list>", pointArr ];
970                         curvesAdjustmentListArr.push ([ "<object>", [ "curvesAdjustment", { "channel": channel, "mapping": mapping } ] ]);
971                         break;
972                     case jamEngine.uniIdStrToId ("curve"):
973                         for (var j = 0; j < points.length; j++)
974                         {
975                             var point =
976                             [
977                                 "<object>",
978                                 [
979                                     "point",
980                                     {
981                                         "horizontal": [ "<double>", points[j][0] ],
982                                         "vertical": [ "<double>", points[j][1] ]
983                                     }
984                                 ]
985                             ];
986                             pointArr.push (point);
987                         }
988                         var curve = [ "<list>", pointArr ];
989                         curvesAdjustmentListArr.push ([ "<object>", [ "curvesAdjustment", { "channel": channel, "curve": curve } ] ]);
990                         break;
991                     default:
992                         throw new Error ("[jamHelpers.toCurvesAdjustmentList] Unrecognized curve type");
993                         break;
994                 }
995             }
996             return [ "<list>", curvesAdjustmentListArr ];
997         };
998         //
999         /**
1000          * @description Get a hue/saturation adjustment list in JSON AM Data Format from a JSON array of master and local color ranges.
1001          * @param {Array} hueSatAdjustmentsArr JSON array of master and local color ranges
1002          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/huesaturation-adjustment-list-simplified-format/">Hue/Saturation Adjustment List Simplified Format</a>)
1003          * @returns {Object|Array} Hue/saturation adjustment list in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
1004          * @example
1005          * jamEngine.jsonPlay
1006          * (
1007          *     "hueSaturation",
1008          *     {
1009          *         "colorize": { "<boolean>": true },
1010          *         "adjustment": jamHelpers.<strong>toHueSatAdjustmentV2List</strong> ([ [ 210, 30, 10 ] ])
1011          *     }
1012          * );
1013          * @example
1014          * var hueSatAdjustments =
1015          * [
1016          *     [ 20, 0, 0 ],    // Master
1017          *     [ 1, 315, 345, 15, 45, 0, 50, 0 ],  // Reds
1018          *     [ 3, 75, 105, 135, 165, 0, 60, 0 ]  // Greens
1019          * ];
1020          * jamEngine.jsonPlay
1021          * (
1022          *     "hueSaturation",
1023          *     {
1024          *         "adjustment": jamHelpers.<strong>toHueSatAdjustmentV2List</strong> (hueSatAdjustments)
1025          *     }
1026          * );
1027          */
1028         jamHelpers.toHueSatAdjustmentV2List = function (hueSatAdjustmentsArr)
1029         {
1030             var hueSatAdjustmentListArr = [ ];
1031             for (var i = 0; i < hueSatAdjustmentsArr.length; i++)
1032             {
1033                 var hueSatAdjustmentArr = hueSatAdjustmentsArr[i];
1034                 var hueSatAdjustmentObj;
1035                 if ((hueSatAdjustmentArr.length === 3) && (i === 0)) // Master
1036                 {
1037                     hueSatAdjustmentObj =
1038                     {
1039                         "hue": [ "<integer>", hueSatAdjustmentArr[0] ],
1040                         "saturation": [ "<integer>", hueSatAdjustmentArr[1] ],
1041                         "lightness": [ "<integer>", hueSatAdjustmentArr[2] ]
1042                     };
1043                 }
1044                 else if (hueSatAdjustmentArr.length === (1 + 4 + 3)) // Local
1045                 {
1046                     hueSatAdjustmentObj =
1047                     {
1048                         "localRange": [ "<integer>", hueSatAdjustmentArr[0] ],
1049                         "beginRamp": [ "<integer>", hueSatAdjustmentArr[1] ],
1050                         "beginSustain": [ "<integer>", hueSatAdjustmentArr[2] ],
1051                         "endSustain": [ "<integer>", hueSatAdjustmentArr[3] ],
1052                         "endRamp": [ "<integer>", hueSatAdjustmentArr[4] ],
1053                         "hue": [ "<integer>", hueSatAdjustmentArr[5] ],
1054                         "saturation": [ "<integer>", hueSatAdjustmentArr[6] ],
1055                         "lightness": [ "<integer>", hueSatAdjustmentArr[7] ]
1056                     };
1057                 }
1058                 hueSatAdjustmentListArr.push ([ "<object>", [ "hueSatAdjustmentV2", hueSatAdjustmentObj ] ]);
1059             }
1060             return [ "<list>", hueSatAdjustmentListArr ];
1061         };
1062         //
1063         /**
1064          * @description Get a blend range list in JSON AM Data Format from a JSON array of individual channel blend ranges.
1065          * @param {Array} blendRanges JSON array of individual channel blend ranges, each one being either a JSON object or a JSON array
1066          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/blend-range-list-simplified-format/">Blend Range List Simplified Format</a>)
1067          * @returns {Object|Array} Blend range list in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
1068          * @see jamHelpers.fromBlendRangeList
1069          * @example
1070          * var blendRanges =
1071          * [
1072          *     {
1073          *         "channel": "gray",
1074          *         "srcBlackMin": 0,
1075          *         "srcBlackMax": 80,
1076          *         "srcWhiteMin": 255,
1077          *         "srcWhiteMax": 255,
1078          *         "destBlackMin": 0,
1079          *         "destBlackMax": 0,
1080          *         "destWhiteMin": 255,
1081          *         "destWhiteMax": 255
1082          *     },
1083          *     {
1084          *         "channel": "red",
1085          *         "srcBlackMin": 0,
1086          *         "srcBlackMax": 0,
1087          *         "srcWhiteMin": 255,
1088          *         "srcWhiteMax": 255,
1089          *         "destBlackMin": 0,
1090          *         "destBlackMax": 0,
1091          *         "destWhiteMin": 145,
1092          *         "destWhiteMax": 255
1093          *     }
1094          * ];
1095          * jamEngine.jsonPlay
1096          * (
1097          *     "set",
1098          *     {
1099          *         "target": { "<reference>": [ { "layer": { "<enumerated>": { "ordinal": "targetEnum" } } } ] },
1100          *         "to":
1101          *         {
1102          *             "<object>":
1103          *             {
1104          *                 "layer":
1105          *                 {
1106          *                     "blendRange": jamHelpers.<strong>toBlendRangeList</strong> (blendRanges)
1107          *                 }
1108          *             }
1109          *         }
1110          *     }
1111          * );
1112          */
1113         jamHelpers.toBlendRangeList = function (blendRanges)
1114         {
1115             var blendRangeListArr = [ ];
1116             var blendRangeObject;
1117             for (var i = 0; i < blendRanges.length; i++)
1118             {
1119                 var blendRange = blendRanges[i];
1120                 if (blendRange.constructor === Object)
1121                 {
1122                     function restoreDesc (desc)
1123                     {
1124                         var restoredDesc = { };
1125                         for (var key in desc)
1126                         {
1127                             if (desc.hasOwnProperty (key))
1128                             {
1129                                 var value = desc[key];
1130                                 var typedValue = null;
1131                                 switch (key)
1132                                 {
1133                                     case "channel":
1134                                         typedValue = [ "<reference>", [ [ "channel", [ "<enumerated>", [ "channel", value ] ] ] ] ]; // Special handling
1135                                         break;
1136                                     case "srcBlackMin":
1137                                     case "srcBlackMax":
1138                                     case "srcWhiteMin":
1139                                     case "srcWhiteMax":
1140                                     case "destBlackMin":
1141                                     case "destBlackMax":
1142                                     case "destWhiteMin":
1143                                     case "destWhiteMax":
1144                                         typedValue = [ "<integer>", value ];
1145                                         break;
1146                                     case "blendRange":
1147                                         typedValue = [ "<object>", [ "blendRange", restoreDesc (value) ] ];
1148                                         break;
1149                                 }
1150                                 if (typedValue)
1151                                 {
1152                                     restoredDesc[key] = typedValue;
1153                                 }
1154                             }
1155                         }
1156                         return restoredDesc;
1157                     }
1158                     blendRangeObject = restoreDesc ({ "blendRange": blendRange })["blendRange"];
1159                 }
1160                 else if (blendRange.constructor === Array)
1161                 {
1162                     blendRangeObject =
1163                     [
1164                         "<object>",
1165                         [
1166                             "blendRange",
1167                             {
1168                                 "channel": [ "<reference>", [ [ "channel", [ "<enumerated>", [ "channel", blendRange[0] ] ] ] ] ],
1169                                 "srcBlackMin": [ "<integer>", blendRange[1] ],
1170                                 "srcBlackMax": [ "<integer>", blendRange[2] ],
1171                                 "srcWhiteMin": [ "<integer>", blendRange[3] ],
1172                                 "srcWhiteMax": [ "<integer>", blendRange[4] ],
1173                                 "destBlackMin": [ "<integer>", blendRange[5] ],
1174                                 "destBlackMax": [ "<integer>", blendRange[6] ],
1175                                 "destWhiteMin": [ "<integer>", blendRange[7] ],
1176                                 "destWhiteMax": [ "<integer>", blendRange[8] ]
1177                             }
1178                         ]
1179                     ];
1180                 }
1181                 blendRangeListArr.push (blendRangeObject);
1182             }
1183             return [ "<list>", blendRangeListArr ];
1184         };
1185         //
1186         /**
1187          * @description Get a simplified blend range JSON array from a blend range list in JSON AM Data Format.
1188          * @param {Object|Array} blendRangeList Blend range list in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
1189          * @param {Boolean} [explicit] Explicit flag: if true, return a JSON array made of explicit JSON objects instead of minimal JSON arrays
1190          * @returns {Array} Simplified blend range JSON array
1191          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/blend-range-list-simplified-format/">Blend Range List Simplified Format</a>)
1192          * @see jamHelpers.toBlendRangeList
1193          * @example
1194          * var blendRangeList =
1195          * {
1196          *     "<list>":
1197          *     [
1198          *         {
1199          *             "<object>":
1200          *             {
1201          *                 "blendRange":
1202          *                 {
1203          *                     "channel":
1204          *                     {
1205          *                         "<reference>": [ { "channel": { "<enumerated>": { "channel": "gray" } } } ]
1206          *                     },
1207          *                     "srcBlackMin": { "<integer>": 0 },
1208          *                     "srcBlackMax": { "<integer>": 80 },
1209          *                     "srcWhiteMin": { "<integer>": 255 },
1210          *                     "srcWhiteMax": { "<integer>": 255 },
1211          *                     "destBlackMin": { "<integer>": 0 },
1212          *                     "destBlackMax": { "<integer>": 0 },
1213          *                     "destWhiteMin": { "<integer>": 145 },
1214          *                     "destWhiteMax": { "<integer>": 255 }
1215          *                 }
1216          *             }
1217          *         }
1218          *     ]
1219          * };
1220          * alert (jamJSON.stringify (jamHelpers.<strong>fromBlendRangeList</strong> (blendRangeList, true), '\t'));
1221          * // ->
1222          * // [
1223          * //     {
1224          * //         "channel": "gray",
1225          * //         "srcBlackMin": 0,
1226          * //         "srcBlackMax": 80,
1227          * //         "srcWhiteMin": 255,
1228          * //         "srcWhiteMax": 255,
1229          * //         "destBlackMin": 0,
1230          * //         "destBlackMax": 0,
1231          * //         "destWhiteMin": 145,
1232          * //         "destWhiteMax": 255
1233          * //     }
1234          * // ]
1235          * alert (jamJSON.stringify (jamHelpers.<strong>fromBlendRangeList</strong> (blendRangeList)));
1236          * // ->
1237          * // [ [ "gray", 0, 80, 255, 255, 0, 0, 145, 255 ] ]
1238          */
1239         jamHelpers.fromBlendRangeList = function (blendRangeList, explicit)
1240         {
1241             var blendRanges;
1242             if (explicit)
1243             {
1244                 var replaceChannelHook = function (desc, key, getDefaultValue)
1245                 {
1246                     var replacedValue = undefined;
1247                     if (key === "channel")  // Special handling
1248                     {
1249                         var value = getDefaultValue (desc, key);
1250                         replacedValue = value[0]["channel"];
1251                     }
1252                     return replacedValue;
1253                 };
1254                 blendRanges = jamEngine.simplifyList (blendRangeList, replaceChannelHook);
1255             }
1256             else
1257             {
1258                 blendRanges = [ ];
1259                 var normalizedBlendRangeList = jamEngine.normalizeJsonItem (blendRangeList, { meaningfulIds: true, parseFriendly: true });
1260                 for (index = 0; index < normalizedBlendRangeList[1].length; index++)
1261                 {
1262                     var blendRange = normalizedBlendRangeList[1][index][1][1];
1263                     var blendRangeArr =
1264                     [
1265                         blendRange["channel"][1][0][1][1][1],
1266                         blendRange["srcBlackMin"][1],
1267                         blendRange["srcBlackMax"][1],
1268                         blendRange["srcWhiteMin"][1],
1269                         blendRange["srcWhiteMax"][1],
1270                         blendRange["destBlackMin"][1],
1271                         blendRange["destBlackMax"][1],
1272                         blendRange["destWhiteMin"][1],
1273                         blendRange["destWhiteMax"][1]
1274                     ];
1275                     blendRanges.push (blendRangeArr);
1276                 }
1277             }
1278             return blendRanges;
1279         };
1280         //
1281         /**
1282          * @description Get an integer list in JSON AM Data Format from a JSON array of integer values.
1283          * @param {Array} integersArr JSON array of integer values
1284          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/integer-list-simplified-format/">Integer List Simplified Format</a>)
1285          * @returns {Object|Array} Integer list in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
1286          * @see jamHelpers.fromIntegerList
1287          * @example
1288          * var customMatrix =
1289          * [
1290          *     0,  0,  0,  0,  0,
1291          *     0,  1,  1,  1,  0,
1292          *     0,  1, -8,  1,  0,
1293          *     0,  1,  1,  1,  0,
1294          *     0,  0,  0,  0,  0
1295          * ];
1296          * jamEngine.jsonPlay
1297          * (
1298          *     "custom",
1299          *     {
1300          *         "scale": { "<integer>": 1 },
1301          *         "offset": { "<integer>": 0 },
1302          *         "matrix": jamHelpers.<strong>toIntegerList</strong> (customMatrix)
1303          *     }
1304          * );
1305          */
1306         jamHelpers.toIntegerList = function (integersArr)
1307         {
1308             var integerListArr = [ ];
1309             for (var i = 0; i < integersArr.length; i++)
1310             {
1311                 integerListArr.push ([ "<integer>", integersArr[i] ]);
1312             }
1313             return [ "<list>", integerListArr ];
1314         };
1315         //
1316         /**
1317          * @description Get a JSON array of integer values from an integer list in JSON AM Data Format.
1318          * @param {Object|Array} integerList Integer list in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
1319          * @returns {Array} JSON array of integer values
1320          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/integer-list-simplified-format/">Integer Simplified List Format</a>)
1321          * @see jamHelpers.toIntegerList
1322          * @example
1323          * var integerList =
1324          * {
1325          *     "<list>":
1326          *     [
1327          *         { "<integer>": 0 },
1328          *         { "<integer>": 25 },
1329          *         { "<integer>": 50 },
1330          *         { "<integer>": 75 },
1331          *         { "<integer>": 100 }
1332          *     ]
1333          * };
1334          * alert (jamJSON.stringify (jamHelpers.<strong>fromIntegerList</strong> (integerList)));
1335          * // -> [ 0, 25, 50, 75, 100 ]
1336          */
1337         jamHelpers.fromIntegerList = function (integerList)
1338         {
1339             var normalizedIntegerList = jamEngine.normalizeJsonItem (integerList, { meaningfulIds: true, parseFriendly: true });
1340             var integersArr = [ ];
1341             var integers = normalizedIntegerList[1];
1342             for (var i = 0; i < integers.length; i++)
1343             {
1344                 integersArr.push (integers[i][1]);
1345             }
1346             return integersArr;
1347         };
1348         //
1349         function toUnitDouble (value, unit)
1350         {
1351             return (typeof unit === 'undefined') ? [ "<double>", value ] : [ "<unitDouble>", [ unit, value ] ];
1352         }
1353         //
1354         /**
1355          * @description Get a point object in JSON AM Data Format from a JSON array of data (coordinate values) and optional unit ID string.
1356          * @param {Array} pointArr JSON array of data: [ horizontal, vertical ], and optional unit ID string ("distanceUnit", "percentUnit", "pixelsUnit", etc.)
1357          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/point-object-simplified-format/">Point Object Simplified Format</a>)
1358          * @returns {Object|Array} Point object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
1359          * @example
1360          * jamEngine.jsonPlay
1361          * (
1362          *     "lensFlare",
1363          *     {
1364          *         "brightness": { "<integer>": 120 },
1365          *         "flareCenter": jamHelpers.<strong>toPointObject</strong> ([ [ 0.75, 0.75 ] ]),
1366          *         "lens": { "<enumerated>": { "lens": "zoom" } }
1367          *     }
1368          * );
1369          */
1370         jamHelpers.toPointObject = function (pointArr)
1371         {
1372             var data = pointArr[0];
1373             var unit = pointArr[1];  // Optional, may be undefined
1374             var pointObject =
1375             [
1376                 "<object>",
1377                 [
1378                     "point",
1379                     {
1380                         "horizontal": toUnitDouble (data[0], unit),
1381                         "vertical": toUnitDouble (data[1], unit)
1382                     }
1383                 ]
1384             ];
1385             return pointObject;
1386         };
1387         //
1388         /**
1389          * @description Get a point list in JSON AM Data Format from a JSON array of data (list of points) and optional unit ID string.
1390          * @param {Array} pointsArr JSON array of data (list of points as JSON arrays of 2 numeric coordinates: [ horizontal, vertical ]) and an optional unit ID string ("distanceUnit", "percentUnit", "pixelsUnit", etc.)
1391          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/point-list-simplified-format/">Point List Simplified Format</a>)
1392          * @returns {Object|Array} Point list in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
1393          * @see jamHelpers.fromPointList
1394          * @example
1395          * var points = [ [ [ 467, 116 ], [ 482, 335 ], [ 313, 274 ], [ 366, 102 ] ], "pixelsUnit" ];
1396          * jamEngine.jsonPlay
1397          * (
1398          *     "set",
1399          *     {
1400          *         "target": { "<reference>": [ { "channel": { "<property>": "selection" } } ] },
1401          *         "to": { "<object>": { "polygon": { "points": jamHelpers.<strong>toPointList</strong> (points) } } },
1402          *         "feather": { "<unitDouble>": { "pixelsUnit": 5 } },
1403          *         "antiAlias": { "<boolean>": true }
1404          *     }
1405          * );
1406          */
1407         jamHelpers.toPointList = function (pointsArr)
1408         {
1409             var data = pointsArr[0];
1410             var unit = pointsArr[1];  // Optional, may be undefined
1411             var pointListArr = [ ];
1412             for (var i = 0; i < data.length; i++)
1413             {
1414                 pointListArr.push
1415                 (
1416                     [
1417                         "<object>",
1418                         [
1419                             "point",
1420                             {
1421                                 "horizontal": toUnitDouble (data[i][0], unit),
1422                                 "vertical": toUnitDouble (data[i][1], unit)
1423                             }
1424                         ]
1425                     ]
1426                 );
1427             }
1428             return [ "<list>", pointListArr ];
1429         };
1430         //
1431         //
1432         /**
1433          * @description Get a JSON array of data (list of points) and optional unit ID string from a point list in JSON AM Data Format.
1434          * @param {Object|Array} pointList Point list in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
1435          * @returns {Array} JSON array of data (list of points as JSON arrays of 2 numeric coordinates: [ horizontal, vertical ]) and optional unit ID string for coordinates
1436          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/point-list-simplified-format/">Point List Simplified Format</a>)
1437          * @see jamHelpers.toPointList
1438          * @example
1439          * var pointList =
1440          * {
1441          *     "<list>":
1442          *     [
1443          *         {
1444          *             "<object>":
1445          *             {
1446          *                 "point":
1447          *                 {
1448          *                     "horizontal": { "<unitDouble>": { "percentUnit": 80 } },
1449          *                     "vertical": { "<unitDouble>": { "percentUnit": 50 } }
1450          *                 }
1451          *             }
1452          *         },
1453          *         {
1454          *             "<object>":
1455          *             {
1456          *                 "point":
1457          *                 {
1458          *                     "horizontal": { "<unitDouble>": { "percentUnit": 20 } },
1459          *                     "vertical": { "<unitDouble>": { "percentUnit": 20 } }
1460          *                 }
1461          *             }
1462          *         },
1463          *         {
1464          *             "<object>":
1465          *             {
1466          *                 "point":
1467          *                 {
1468          *                     "horizontal": { "<unitDouble>": { "percentUnit": 20 } },
1469          *                     "vertical": { "<unitDouble>": { "percentUnit": 80 } }
1470          *                 }
1471          *             }
1472          *         }
1473          *     ]
1474          * };
1475          * alert (jamJSON.stringify (jamHelpers.<strong>fromPointList</strong> (pointList)));
1476          * // -> [ [ [ 80, 50 ], [ 20, 20 ], [ 20, 80 ] ], "percentUnit" ]
1477          */
1478         jamHelpers.fromPointList = function (pointList)
1479         {
1480             var pointsArr = [ ];
1481             var normalizedPointList = jamEngine.normalizeJsonItem (pointList, { meaningfulIds: true, parseFriendly: true });
1482             var data = [ ];
1483             var unit;
1484             function getValue (coordinate)
1485             {
1486                 var value;
1487                 switch (coordinate[0])
1488                 {
1489                     case "<unitDouble>":
1490                         unit = coordinate[1][0];
1491                         value = coordinate[1][1];
1492                         break;
1493                     case "<double>":
1494                         unit = undefined;
1495                         value = coordinate[1];
1496                         break;
1497                 }
1498                 return value;
1499             }
1500             var pointListArr = normalizedPointList[1];
1501             for (var i = 0; i < pointListArr.length; i++)
1502             {
1503                 data.push ([ getValue (pointListArr[i][1][1]["horizontal"]), getValue (pointListArr[i][1][1]["vertical"]) ]);
1504             }
1505             pointsArr.push (data);
1506             if (unit)
1507             {
1508                 pointsArr.push (unit);
1509             }
1510             return pointsArr;
1511         };
1512         //
1513         /**
1514          * @description Get an offset object in JSON AM Data Format from a JSON array of data (offset values) and optional unit ID string.
1515          * @param {Array} offsetArr JSON array of data: [ horizontal, vertical ], and optional unit ID string ("distanceUnit", "percentUnit", "pixelsUnit", etc.)
1516          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/offset-object-simplified-format/">Offset Object Simplified Format</a>)
1517          * @returns {Object|Array} Offset object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
1518          * @example
1519          * jamEngine.jsonPlay
1520          * (
1521          *     "move",
1522          *     {
1523          *         "target": { "<reference>": [ { "layer": { "<enumerated>": { "ordinal": "targetEnum" } } } ] },
1524          *         "to": jamHelpers.<strong>toOffsetObject</strong> ([ [ 50, 50 ], "percentUnit" ])
1525          *     }
1526          * );
1527          */
1528         jamHelpers.toOffsetObject = function (offsetArr)
1529         {
1530             var data = offsetArr[0];
1531             var unit = offsetArr[1];  // Optional, may be undefined
1532             var offsetObject =
1533             [
1534                 "<object>",
1535                 [
1536                     "offset",
1537                     {
1538                         "horizontal": toUnitDouble (data[0], unit),
1539                         "vertical": toUnitDouble (data[1], unit)
1540                     }
1541                 ]
1542             ];
1543             return offsetObject;
1544         };
1545         //
1546         /**
1547          * @description Get a rectangle object in JSON AM Data Format from a JSON array of data (bound values and optional radius) and optional unit ID string.
1548          * @param {Array} rectangleArr JSON array of data: [ left, top, right, bottom ] for plain rectangles or [ left, top, right, bottom, radius ] for rounded rectangles, and optional unit ID string ("distanceUnit", "percentUnit", "pixelsUnit", etc.)
1549          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/rectangle-object-simplified-format/">Rectangle Object Simplified Format</a>)
1550          * @returns {Object|Array} Rectangle object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
1551          * @example
1552          * // Plain rectangle
1553          * jamEngine.jsonPlay
1554          * (
1555          *     "set",
1556          *     {
1557          *         "target": { "<reference>": [ { "channel": { "<property>": "selection" } } ] },
1558          *         "to": jamHelpers.<strong>toRectangleObject</strong> ([ [ 10, 10, 90, 90 ], "percentUnit" ])
1559          *     }
1560          * );
1561          * @example
1562          * // Rounded rectangle
1563          * jamEngine.jsonPlay
1564          * (
1565          *     "set",
1566          *     {
1567          *         "target": { "<reference>": [ { "path": { "<property>": "workPath" } } ] },
1568          *         "to": jamHelpers.<strong>toRectangleObject</strong> ([ [ 10, 10, 90, 90, 10 ], "percentUnit" ])
1569          *     }
1570          * );
1571          */
1572         jamHelpers.toRectangleObject = function (rectangleArr)
1573         {
1574             var data = rectangleArr[0];
1575             var unit = rectangleArr[1];  // Optional, may be undefined
1576             var rectangleObj =
1577             {
1578                 "left": toUnitDouble (data[0], unit),
1579                 "top": toUnitDouble (data[1], unit),
1580                 "right": toUnitDouble (data[2], unit),
1581                 "bottom": toUnitDouble (data[3], unit)
1582             };
1583             if (data.length === 5)
1584             {
1585                 rectangleObj["radius"] = toUnitDouble (data[4], unit);
1586             }
1587             return [ "<object>", [ "rectangle", rectangleObj ] ];
1588         };
1589         //
1590         /**
1591          * @description Get an ellipse object in JSON AM Data Format from a JSON array of data (bound values) and optional unit ID string.
1592          * @param {Array} ellipseArr JSON array of data: [ left, top, right, bottom ], and optional unit ID string ("distanceUnit", "percentUnit", "pixelsUnit", etc.)
1593          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/ellipse-object-simplified-format/">Ellipse Object Simplified Format</a>)
1594          * @returns {Object|Array} Ellipse object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
1595          * @example
1596          * jamEngine.jsonPlay
1597          * (
1598          *     "set",
1599          *     {
1600          *         "target": { "<reference>": [ { "path": { "<property>": "workPath" } } ] },
1601          *         "to": jamHelpers.<strong>toEllipseObject</strong> ([ [ 10, 10, 90, 90 ], "percentUnit" ])
1602          *     }
1603          * );
1604          */
1605         jamHelpers.toEllipseObject = function (ellipseArr)
1606         {
1607             var data = ellipseArr[0];
1608             var unit = ellipseArr[1];  // Optional, may be undefined
1609             var ellipseObject =
1610             [
1611                 "<object>",
1612                 [
1613                     "ellipse",
1614                     {
1615                         "left": toUnitDouble (data[0], unit),
1616                         "top": toUnitDouble (data[1], unit),
1617                         "right": toUnitDouble (data[2], unit),
1618                         "bottom": toUnitDouble (data[3], unit)
1619                     }
1620                 ]
1621             ];
1622             return ellipseObject;
1623         };
1624         //
1625         /**
1626          * @description Get a custom shape object in JSON AM Data Format from a JSON array of data (name and bound values) and optional unit ID string.
1627          * @param {Array} customShapeArr JSON array of data: [ name, left, top, right, bottom ], and optional unit ID string ("distanceUnit", "percentUnit", "pixelsUnit", etc.)
1628          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/custom-shape-object-simplified-format/">Custom Shape Object Simplified Format</a>)
1629          * @returns {Object|Array} Custom shape object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
1630          * @example
1631          * jamEngine.jsonPlay
1632          * (
1633          *     "set",
1634          *     {
1635          *         "target": { "<reference>": [ { "path": { "<property>": "workPath" } } ] },
1636          *         "to": jamHelpers.<strong>toCustomShapeObject</strong> ([ [ "Yin Yang", 20, 20, 80, 80 ], "percentUnit" ])
1637          *     }
1638          * );
1639          */
1640         jamHelpers.toCustomShapeObject = function (customShapeArr)
1641         {
1642             var data = customShapeArr[0];
1643             var unit = customShapeArr[1];  // Optional, may be undefined
1644             var customShapeObject =
1645             [
1646                 "<object>",
1647                 [
1648                     "customShape",
1649                     {
1650                         "name": [ "<string>", data[0] ],
1651                         "left": toUnitDouble (data[1], unit),
1652                         "top": toUnitDouble (data[2], unit),
1653                         "right": toUnitDouble (data[3], unit),
1654                         "bottom": toUnitDouble (data[4], unit)
1655                     }
1656                 ]
1657             ];
1658             return customShapeObject;
1659         };
1660         //
1661         /**
1662          * @description Get a curve point list in JSON AM Data Format from a JSON array of curve points.
1663          * @param {Array} curvePoints JSON array of curve points, each one being either a JSON object or a JSON array
1664          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/curve-point-list-simplified-format/">Curve Point List Simplified Format</a>)
1665          * @returns {Object|Array} Curve point list in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
1666          * @see jamHelpers.fromCurvePointList
1667          * @example
1668          * var curvePoints =
1669          * [
1670          *     { "horizontal": 0, "vertical": 0 },
1671          *     { "horizontal": 75, "vertical": 150 },
1672          *     { "horizontal": 175, "vertical": 25, "continuity": false },
1673          *     { "horizontal": 255, "vertical": 255 }
1674          * ];
1675          * jamEngine.jsonPlay
1676          * (
1677          *     "set",
1678          *     {
1679          *         "target":
1680          *         {
1681          *             "<reference>":
1682          *             [
1683          *                 { "property": { "<property>": "layerEffects" } },
1684          *                 { "layer": { "<enumerated>": { "ordinal": "targetEnum" } } }
1685          *             ]
1686          *         },
1687          *         "to":
1688          *         {
1689          *             "<object>":
1690          *             {
1691          *                 "layerEffects":
1692          *                 {
1693          *                     "scale": { "<unitDouble>": { "percentUnit": 100 } },
1694          *                     "chromeFX":
1695          *                     {
1696          *                         "<object>":
1697          *                         {
1698          *                             "chromeFX":
1699          *                             {
1700          *                                 "enabled": { "<boolean>": true },
1701          *                                 "mode": { "<enumerated>": { "blendMode": "normal" } },
1702          *                                 "color": jamHelpers.toColorObject ([ "HSBColorClass", [ 190, 100, 100 ] ]),
1703          *                                 "antiAlias": { "<boolean>": true },
1704          *                                 "invert": { "<boolean>": false },
1705          *                                 "opacity": { "<unitDouble>": { "percentUnit": 90 } },
1706          *                                 "localLightingAngle": { "<unitDouble>": { "angleUnit": 45 } },
1707          *                                 "distance": { "<unitDouble>": { "pixelsUnit": 10 } },
1708          *                                 "blur": { "<unitDouble>": { "pixelsUnit": 15 } },
1709          *                                 "mappingShape":
1710          *                                 {
1711          *                                     "<object>":
1712          *                                     {
1713          *                                         "shapingCurve":
1714          *                                         {
1715          *                                             "curve": jamHelpers.<strong>toCurvePointList</strong> (curvePoints)
1716          *                                         }
1717          *                                     }
1718          *                                 }
1719          *                             }
1720          *                         }
1721          *                     }
1722          *                 }
1723          *             }
1724          *         }
1725          *     }
1726          * );
1727          */
1728         jamHelpers.toCurvePointList = function (curvePoints)
1729         {
1730             var curvePointListArr = [ ];
1731             var curvePointObject;
1732             for (var i = 0; i < curvePoints.length; i++)
1733             {
1734                 var curvePoint = curvePoints[i];
1735                 if (curvePoint.constructor === Object)
1736                 {
1737                     function restoreDesc (desc)
1738                     {
1739                         var restoredDesc = { };
1740                         for (var key in desc)
1741                         {
1742                             if (desc.hasOwnProperty (key))
1743                             {
1744                                 var value = desc[key];
1745                                 var typedValue = null;
1746                                 switch (key)
1747                                 {
1748                                     case "continuity":
1749                                         typedValue = [ "<boolean>", value ];
1750                                         break;
1751                                     case "horizontal":
1752                                     case "vertical":
1753                                         typedValue = [ "<double>", value ];
1754                                         break;
1755                                     case "curvePoint":
1756                                         typedValue = [ "<object>", [ "curvePoint", restoreDesc (value) ] ];
1757                                         break;
1758                                 }
1759                                 if (typedValue)
1760                                 {
1761                                     restoredDesc[key] = typedValue;
1762                                 }
1763                             }
1764                         }
1765                         return restoredDesc;
1766                     }
1767                     curvePointObject = restoreDesc ({ "curvePoint": curvePoint })["curvePoint"];
1768                 }
1769                 else if (curvePoint.constructor === Array)
1770                 {
1771                     switch (curvePoint.length)
1772                     {
1773                         case 2:
1774                             // smooth point by default
1775                             curvePointObject =
1776                             [
1777                                 "<object>",
1778                                 [
1779                                     "curvePoint",
1780                                     {
1781                                         "horizontal": [ "<double>", curvePoint[0] ],
1782                                         "vertical": [ "<double>", curvePoint[1] ]
1783                                     }
1784                                 ]
1785                             ];
1786                             break;
1787                         case 3:
1788                             // curvePoint[2] should be either true (smooth) or false (corner)
1789                             curvePointObject =
1790                             [
1791                                 "<object>",
1792                                 [
1793                                     "curvePoint",
1794                                     {
1795                                         "horizontal": [ "<double>", curvePoint[0] ],
1796                                         "vertical": [ "<double>", curvePoint[1] ],
1797                                         "continuity": [ "<boolean>", curvePoint[2] ]
1798                                     }
1799                                 ]
1800                             ];
1801                             break;
1802                     }
1803                 }
1804                 curvePointListArr.push (curvePointObject);
1805             }
1806             return [ "<list>", curvePointListArr ];
1807         };
1808         //
1809         /**
1810          * @description Get a simplified curve point JSON array from a curve point list in JSON AM Data Format.
1811          * @param {Object|Array} curvePointList Curve point list in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
1812          * @param {Boolean} [explicit] Explicit flag: if true, return a JSON array made of explicit JSON objects instead of minimal JSON arrays
1813          * @returns {Array} Simplified curve point JSON array
1814          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/curve-point-list-simplified-format/">Curve Point List Simplified Format</a>)
1815          * @see jamHelpers.toCurvePointList
1816          * @example
1817          * var curvePointList =
1818          * {
1819          *     "<list>":
1820          *     [
1821          *         {
1822          *             "<object>":
1823          *             {
1824          *                 "curvePoint":
1825          *                 {
1826          *                     "horizontal": { "<double>": 0 },
1827          *                     "vertical": { "<double>": 0 }
1828          *                 }
1829          *             }
1830          *         },
1831          *         {
1832          *             "<object>":
1833          *             {
1834          *                 "curvePoint":
1835          *                 {
1836          *                     "horizontal": { "<double>": 75 },
1837          *                     "vertical": { "<double>": 150 }
1838          *                 }
1839          *             }
1840          *         },
1841          *         {
1842          *             "<object>":
1843          *             {
1844          *                 "curvePoint":
1845          *                 {
1846          *                     "horizontal": { "<double>": 175 },
1847          *                     "vertical": { "<double>": 25 },
1848          *                     "continuity": { "<boolean>": false }
1849          *                 }
1850          *             }
1851          *         },
1852          *         {
1853          *             "<object>":
1854          *             {
1855          *                 "curvePoint":
1856          *                 {
1857          *                     "horizontal": { "<double>": 255 },
1858          *                     "vertical": { "<double>": 255 }
1859          *                 }
1860          *             }
1861          *         }
1862          *     ]
1863          * };
1864          * alert (jamJSON.stringify (jamHelpers.<strong>fromCurvePointList</strong> (curvePointList, true), '\t'));
1865          * // ->
1866          * // [
1867          * //     {
1868          * //         "horizontal": 0,
1869          * //         "vertical": 0
1870          * //     },
1871          * //     {
1872          * //         "horizontal": 75,
1873          * //         "vertical": 150
1874          * //     },
1875          * //     {
1876          * //         "horizontal": 175,
1877          * //         "vertical": 25,
1878          * //         "continuity": false
1879          * //     },
1880          * //     {
1881          * //         "horizontal": 255,
1882          * //         "vertical": 255
1883          * //     }
1884          * // ]
1885          * alert (jamJSON.stringify (jamHelpers.<strong>fromCurvePointList</strong> (curvePointList)));
1886          * // ->
1887          * // [ [ 0, 0 ], [ 75, 150 ], [ 175, 25, false ], [ 255, 255 ] ]
1888          */
1889         jamHelpers.fromCurvePointList = function (curvePointList, explicit)
1890         {
1891             var curvePoints;
1892             if (explicit)
1893             {
1894                 curvePoints = jamEngine.simplifyList (curvePointList);
1895             }
1896             else
1897             {
1898                 curvePoints = [ ];
1899                 var normalizedCurvePointList = jamEngine.normalizeJsonItem (curvePointList, { meaningfulIds: true, parseFriendly: true });
1900                 for (index = 0; index < normalizedCurvePointList[1].length; index++)
1901                 {
1902                     var curvePoint = normalizedCurvePointList[1][index][1][1];
1903                     var curvePointArr =
1904                     [
1905                         curvePoint["horizontal"][1],
1906                         curvePoint["vertical"][1]
1907                     ];
1908                     if ("continuity" in curvePoint)
1909                     {
1910                         curvePointArr.push (curvePoint["continuity"][1]);
1911                     }
1912                     curvePoints.push (curvePointArr);
1913                 }
1914             }
1915             return curvePoints;
1916         };
1917         //
1918         /**
1919          * @description Get a rational point list in JSON AM Data Format from a JSON array of a list of rational points and optional unit ID string.
1920          * @param {Array} rationalPointsArr JSON array of a list of rational points (as JSON arrays of 2 numeric coordinates: [ horizontal, vertical ]) and an optional unit ID string ("distanceUnit", "percentUnit", "pixelsUnit", etc.)
1921          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/rational-point-list-simplified-format/">Rational Point List Simplified Format</a>)
1922          * @returns {Object|Array} Rational point list in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
1923          * @example
1924          * var bounds = [ [ 0, 0, 100, 100 ], "percentUnit" ];
1925          * var meshPoints =
1926          * [
1927          *     [
1928          *         [ 0, 0 ], [ 50, 0 ], [ 50, 0 ], [ 100, 0 ],
1929          *         [ 0, 50 ], [ 100 / 3, 100 / 3 ], [ 200 / 3, 100 / 3 ], [ 100, 50 ],
1930          *         [ 0, 50 ], [ 100 / 3, 200 / 3 ], [ 200 / 3, 200 / 3 ], [ 100, 50 ],
1931          *         [ 0, 100 ], [ 50, 100 ], [ 50, 100 ], [ 100, 100 ]
1932          *     ],
1933          *     "percentUnit"
1934          * ];
1935          * jamEngine.jsonPlay
1936          * (
1937          *     "transform",
1938          *     {
1939          *         "target": { "<reference>": [ { "layer": { "<enumerated>": { "ordinal": "targetEnum" } } } ] },
1940          *         "freeTransformCenterState": { "<enumerated>": { "quadCenterState": "QCSAverage" } },
1941          *         "warp":
1942          *         {
1943          *             "<object>":
1944          *             {
1945          *                 "warp":
1946          *                 {
1947          *                     "warpStyle": { "<enumerated>": { "warpStyle": "warpCustom" } },
1948          *                     "bounds": jamHelpers.toRectangleObject (bounds),
1949          *                     "uOrder": { "<integer>": 4 },
1950          *                     "vOrder": { "<integer>": 4 },
1951          *                     "customEnvelopeWarp":
1952          *                     {
1953          *                         "<object>":
1954          *                         {
1955          *                             "customEnvelopeWarp":
1956          *                             {
1957          *                                 "meshPoints": jamHelpers.<strong>toRationalPointList</strong> (meshPoints)
1958          *                             }
1959          *                         }
1960          *                     }
1961          *                 }
1962          *             }
1963          *         }
1964          *     }
1965          * );
1966          */
1967         jamHelpers.toRationalPointList = function (rationalPointsArr)
1968         {
1969             var data = rationalPointsArr[0];
1970             var unit = rationalPointsArr[1];  // Optional, may be undefined
1971             var rationalPointListArr = [ ];
1972             for (var i = 0; i < data.length; i++)
1973             {
1974                 rationalPointListArr.push
1975                 (
1976                     [
1977                         "<object>",
1978                         [
1979                             "rationalPoint",
1980                             {
1981                                 "horizontal": toUnitDouble (data[i][0], unit),
1982                                 "vertical": toUnitDouble (data[i][1], unit)
1983                             }
1984                         ]
1985                     ]
1986                 );
1987             }
1988             return [ "<list>", rationalPointListArr ];
1989         };
1990         //
1991         /**
1992          * @description Get a path component list in JSON AM Data Format from either a JSON object or a JSON array (made of two elements: simplified path component values and unit ID string for coordinates).
1993          * @param {Object|Array} pathComponents JSON object or JSON array made of two elements: simplified path component values and unit ID string for coordinates ("distanceUnit", "percentUnit", "pixelsUnit")
1994          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/path-component-list-simplified-format/">Path Component List Simplified Format</a>)
1995          * @returns {Object|Array} Path component list in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
1996          * @see jamHelpers.fromPathComponentList
1997          * @example
1998          * // Self-intersecting five-pointed star path
1999          * var starPath =
2000          * {
2001          *     "pathComponents":
2002          *     [
2003          *         {
2004          *             "shapeOperation": "add",
2005          *             "windingFill": true,
2006          *             "subpathListKey":
2007          *             [
2008          *                 {
2009          *                     "closedSubpath": true,
2010          *                     "points":
2011          *                     [
2012          *                         { "anchor": { "horizontal": 12.5, "vertical": 41 } },
2013          *                         { "anchor": { "horizontal": 87.5, "vertical": 41 } },
2014          *                         { "anchor": { "horizontal": 23, "vertical": 85.5 } },
2015          *                         { "anchor": { "horizontal": 50, "vertical": 14.5 } },
2016          *                         { "anchor": { "horizontal": 77, "vertical": 85.5 } }
2017          *                     ]
2018          *                 }
2019          *             ]
2020          *         }
2021          *     ],
2022          *     "unit": "percentUnit"
2023          * };
2024          * jamEngine.jsonPlay
2025          * (
2026          *     "set",
2027          *     {
2028          *         "target": { "<reference>": [ { "path": { "<property>": "workPath" } } ] },
2029          *         "to": jamHelpers.<strong>toPathComponentList</strong> (starPath)
2030          *     }
2031          * );
2032          * jamEngine.jsonPlay
2033          * (
2034          *     "fill",
2035          *     {
2036          *         "target": { "<reference>": [ { "path": { "<property>": "workPath" } } ] },
2037          *         "wholePath": { "<boolean>": true },
2038          *         "using": { "<enumerated>": { "fillContents": "color" } },
2039          *         "color": jamHelpers.toColorObject ([ "RGBColor", [ 255, 0, 255 ] ]),  // Magenta
2040          *         "opacity": { "<unitDouble>": { "percentUnit": 100 } },
2041          *         "mode": { "<enumerated>": { "blendMode": "normal" } },
2042          *         "radius": { "<unitDouble>": { "pixelsUnit": 0.0 } },
2043          *         "antiAlias": { "<boolean>": true }
2044          *     }
2045          * );
2046          * jamEngine.jsonPlay
2047          * (
2048          *     "delete",
2049          *     {
2050          *         "target": { "<reference>": [ { "path": { "<property>": "workPath" } } ] }
2051          *     }
2052          * );
2053          */
2054         jamHelpers.toPathComponentList = function (pathComponents)
2055         {
2056             var pathComponentList;
2057             if (pathComponents.constructor === Object)
2058             {
2059                 var unit;
2060                 if ("unit" in pathComponents)
2061                 {
2062                     unit = pathComponents["unit"];
2063                 }
2064                 var data = pathComponents["pathComponents"];
2065                 function restoreDesc (desc)
2066                 {
2067                     var restoredDesc = { };
2068                     for (var key in desc)
2069                     {
2070                         if (desc.hasOwnProperty (key))
2071                         {
2072                             var value = desc[key];
2073                             var typedValue = null;
2074                             var restoredList;
2075                             switch (key)
2076                             {
2077                                 case "closedSubpath":
2078                                 case "smooth":
2079                                 case "windingFill":
2080                                     typedValue = [ "<boolean>", value ];
2081                                     break;
2082                                 case "shapeOperation":
2083                                     typedValue = [ "<enumerated>", [ "shapeOperation", value ] ];
2084                                     break;
2085                                 case "horizontal":
2086                                 case "vertical":
2087                                     typedValue = toUnitDouble (value, unit);
2088                                     break;
2089                                 case "anchor":
2090                                 case "backward":
2091                                 case "forward":
2092                                     typedValue = [ "<object>", [ "point", restoreDesc (value) ] ];
2093                                     break;
2094                                 case "subpathListKey":
2095                                     restoredList = [ ];
2096                                     for (var i = 0; i < value.length; i++)
2097                                     {
2098                                         restoredList.push ([ "<object>", [ "subpathsList", restoreDesc (value[i]) ] ]);
2099                                     }
2100                                     typedValue = [ "<list>", restoredList ];
2101                                     break;
2102                                 case "points":
2103                                     restoredList = [ ];
2104                                     for (var i = 0; i < value.length; i++)
2105                                     {
2106                                         restoredList.push ([ "<object>", [ "pathPoint", restoreDesc (value[i]) ] ]);
2107                                     }
2108                                     typedValue = [ "<list>", restoredList ];
2109                                     break;
2110                                 case "pathComponents":
2111                                     restoredList = [ ];
2112                                     for (var i = 0; i < value.length; i++)
2113                                     {
2114                                         restoredList.push ([ "<object>", [ "pathComponent", restoreDesc (value[i]) ] ]);
2115                                     }
2116                                     typedValue = [ "<list>", restoredList ];
2117                                     break;
2118                             }
2119                             if (typedValue)
2120                             {
2121                                 restoredDesc[key] = typedValue;
2122                             }
2123                         }
2124                     }
2125                     return restoredDesc;
2126                 }
2127                 pathComponentList = restoreDesc ({ "pathComponents": data })["pathComponents"];
2128             }
2129             else if (pathComponents.constructor === Array)
2130             {
2131                 var pathComponentListArr = [ ];
2132                 var data = pathComponents[0];
2133                 var unit = pathComponents[1];
2134                 for (var i = 0; i < data.length; i++)
2135                 {
2136                     var shapeOperation = data[i][0];
2137                     var subpaths = data[i][1];
2138                     var windingFill = data[i][2];
2139                     var subpathArr = [ ];
2140                     for (var j = 0; j < subpaths.length; j++)
2141                     {
2142                         var points = subpaths[j][0];
2143                         var closedSubpath = subpaths[j][1];
2144                         var pointArr = [ ];
2145                         for (var k = 0; k < points.length; k++)
2146                         {
2147                             var point = points[k];
2148                             switch (point.length)
2149                             {
2150                                 case 1:
2151                                     pointArr.push
2152                                     (
2153                                         [
2154                                             "<object>",
2155                                             [
2156                                                 "pathPoint",
2157                                                 {
2158                                                     "anchor":
2159                                                     [
2160                                                         "<object>",
2161                                                         [
2162                                                             "point",
2163                                                             {
2164                                                                 "horizontal": toUnitDouble (point[0][0], unit),
2165                                                                 "vertical": toUnitDouble (point[0][1], unit)
2166                                                             }
2167                                                         ]
2168                                                     ]
2169                                                 }
2170                                             ]
2171                                         ]
2172                                     );
2173                                     break;
2174                                 case 3:
2175                                 case 4:
2176                                     pointArr.push
2177                                     (
2178                                         [
2179                                             "<object>",
2180                                             [
2181                                                 "pathPoint",
2182                                                 {
2183                                                     "anchor":
2184                                                     [
2185                                                         "<object>",
2186                                                         [
2187                                                             "point",
2188                                                             {
2189                                                                 "horizontal": toUnitDouble (point[0][0], unit),
2190                                                                 "vertical": toUnitDouble (point[0][1], unit)
2191                                                             }
2192                                                         ]
2193                                                     ],
2194                                                     "forward":
2195                                                     [
2196                                                         "<object>",
2197                                                         [
2198                                                             "point",
2199                                                             {
2200                                                                 "horizontal": toUnitDouble (point[1][0], unit),
2201                                                                 "vertical": toUnitDouble (point[1][1], unit)
2202                                                             }
2203                                                         ]
2204                                                     ],
2205                                                     "backward":
2206                                                     [
2207                                                         "<object>",
2208                                                         [
2209                                                             "point",
2210                                                             {
2211                                                                 "horizontal": toUnitDouble (point[2][0], unit),
2212                                                                 "vertical": toUnitDouble (point[2][1], unit)
2213                                                             }
2214                                                         ]
2215                                                     ],
2216                                                     "smooth": [ "<boolean>", point[3] || false ]
2217                                                 }
2218                                             ]
2219                                         ]
2220                                     );
2221                                     break;
2222                             }
2223                         }
2224                         var subpath = { };
2225                         if (closedSubpath)
2226                         {
2227                             subpath["closedSubpath"] = [ "<boolean>", closedSubpath ];
2228                         }
2229                         subpath["points"] = [ "<list>", pointArr ];
2230                         subpathArr.push ([ "<object>", [ "subpathsList", subpath ] ]);
2231                     }
2232                     var pathComponent = { };
2233                     pathComponent["shapeOperation"] = [ "<enumerated>", [ "shapeOperation", shapeOperation ] ];
2234                     if (windingFill)
2235                     {
2236                         pathComponent["windingFill"] = [ "<boolean>", windingFill ];
2237                     }
2238                     pathComponent["subpathListKey"] = [ "<list>", subpathArr ];
2239                     pathComponentListArr.push ([ "<object>", [ "pathComponent", pathComponent ] ]);
2240                 }
2241                 pathComponentList = [ "<list>", pathComponentListArr ];
2242             }
2243             return pathComponentList;
2244         };
2245         //
2246         /**
2247          * @description Get either a JSON object or a JSON array (made of two elements: simplified path component values and unit ID string for coordinates) from a path component list in JSON AM Data Format.
2248          * @param {Object|Array} pathComponentList Path component list in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
2249          * @param {Boolean} [explicit] Explicit flag: if true, return an explicit JSON object instead of a minimal JSON array
2250          * @returns {Array|Object} JSON object or JSON array made of two elements: simplified path component values and unit ID string for coordinates ("distanceUnit", "percentUnit", "pixelsUnit")
2251          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/path-component-list-simplified-format/">Path Component List Simplified Format</a>)
2252          * @see jamHelpers.toPathComponentList
2253          * @example
2254          * jamEngine.meaningfulIds = true;
2255          * jamEngine.parseFriendly = true;
2256          * var resultObj = jamEngine.jsonGet ([ { "path": { "<property>": "workPath" } } ]);
2257          * var pathComponentArr =
2258          *     jamHelpers.<strong>fromPathComponentList</strong> (resultObj["pathContents"][1][1]["pathComponents"], true);
2259          * jamUtils.writeJsonFile (new File ("~/Desktop/work-path.json"), pathComponentArr, '\t');
2260          */
2261         jamHelpers.fromPathComponentList = function (pathComponentList, explicit)
2262         {
2263             var pathComponents;
2264             if (explicit)
2265             {
2266                 pathComponents = { };
2267                 var unit;
2268                 var done = false;
2269                 function getUnitHook (desc, key)
2270                 {
2271                     if (!done)
2272                     {
2273                         if (key === "horizontal")   // Unit used by the first encountered horizontal coordinate, assumed to be the same for all others
2274                         {
2275                             var value = desc[key];
2276                             if (value[0] === "<unitDouble>")
2277                             {
2278                                 unit = value[1][0];
2279                             }
2280                             done = true;
2281                         }
2282                     }
2283                     return undefined;
2284                 }
2285                 pathComponents["pathComponents"] = jamEngine.simplifyList (pathComponentList, getUnitHook);
2286                 if (unit)
2287                 {
2288                     pathComponents["unit"] = unit;
2289                 }
2290             }
2291             else
2292             {
2293                 pathComponents = [ ];
2294                 var normalizedPathComponentList = jamEngine.normalizeJsonItem (pathComponentList, { meaningfulIds: true, parseFriendly: true });
2295                 var data = [ ];
2296                 var unit;
2297                 function getValue (coordinate)
2298                 {
2299                     var value;
2300                     switch (coordinate[0])
2301                     {
2302                         case "<unitDouble>":
2303                             unit = coordinate[1][0];
2304                             value = coordinate[1][1];
2305                             break;
2306                         case "<double>":
2307                             unit = undefined;
2308                             value = coordinate[1];
2309                             break;
2310                     }
2311                     return value;
2312                 }
2313                 var pathComponentListArr = normalizedPathComponentList[1];
2314                 for (var i = 0; i < pathComponentListArr.length; i++)
2315                 {
2316                     var pathComponent = pathComponentListArr[i][1][1];
2317                     var shapeOperation = pathComponent["shapeOperation"][1][1];
2318                     var windingFill = ("windingFill" in pathComponent) ? pathComponent["windingFill"][1] : false;
2319                     var subpathsArr = [ ];
2320                     var subpathListArr = pathComponent["subpathListKey"][1];
2321                     for (var j = 0; j < subpathListArr.length; j++)
2322                     {
2323                         var subpathsList = subpathListArr[j][1][1];
2324                         var closedSubpath = ("closedSubpath" in subpathsList) ? subpathsList["closedSubpath"][1] : false;
2325                         var pathPointsArr = [ ];
2326                         var pointsArr = subpathsList["points"][1];
2327                         for (var k = 0; k < pointsArr.length; k++)
2328                         {
2329                             var pathPoint = pointsArr[k][1][1];
2330                             var pathPointArr = [ ];
2331                             var anchor = pathPoint["anchor"][1][1];
2332                             pathPointArr.push ([ getValue (anchor["horizontal"]), getValue (anchor["vertical"]) ]);
2333                             if ("forward" in pathPoint)
2334                             {
2335                                 var forward = pathPoint["forward"][1][1];
2336                                 pathPointArr.push ([ getValue (forward["horizontal"]), getValue (forward["vertical"]) ]);
2337                             }
2338                             if ("backward" in pathPoint)
2339                             {
2340                                 var backward = pathPoint["backward"][1][1];
2341                                 pathPointArr.push ([ getValue (backward["horizontal"]), getValue (backward["vertical"]) ]);
2342                             }
2343                             var smooth = ("smooth" in pathPoint) ? pathPoint["smooth"][1] : false;
2344                             if (smooth)
2345                             {
2346                                 pathPointArr.push (smooth);
2347                             }
2348                             pathPointsArr.push (pathPointArr);
2349                         }
2350                         var subpathArr = [ ];
2351                         subpathArr.push (pathPointsArr);
2352                         if (closedSubpath)
2353                         {
2354                             subpathArr.push (closedSubpath);
2355                         }
2356                         subpathsArr.push (subpathArr);
2357                     }
2358                     var pathComponentArr = [ ];
2359                     pathComponentArr.push (shapeOperation);
2360                     pathComponentArr.push (subpathsArr);
2361                     if (windingFill)
2362                     {
2363                         pathComponentArr.push (windingFill);
2364                     }
2365                     data.push (pathComponentArr);
2366                 }
2367                 pathComponents.push (data);
2368                 if (unit)
2369                 {
2370                     pathComponents.push (unit);
2371                 }
2372             }
2373             return pathComponents;
2374         };
2375     } ());
2376 }
2377 
2378 //------------------------------------------------------------------------------
2379 
2380