1 //------------------------------------------------------------------------------
  2 // File: jamLayers.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.3:
 25 //  - Fixed incorrect key in function toGradient (): "align" (not "alignment").
 26 //  4.4:
 27 //  - Normalized error messages.
 28 //  4.2:
 29 //  - Cleaned up code.
 30 //  4.0:
 31 //  - Removed reference to 'this' for main global object.
 32 //  - Improved handling of unsupported adjustment or fill layer type.
 33 //  3.6.7:
 34 //  - Fixed type of pattern "phase" key (double instead of unit double).
 35 //  3.6.6:
 36 //  - Improved handling of preset files: "using" key added to several adjustment 
 37 //    layers: "blackAndWhite", "channelMixer", "curves", "exposure", "levels",
 38 //    "selectiveColor".
 39 //  3.6.5:
 40 //  - Fixed bug in jamLayers.fromLayerObject (): "type" is now correctly handled
 41 //    in all cases.
 42 //  3.6.4:
 43 //  - Initial release.
 44 //------------------------------------------------------------------------------
 45 
 46 /**
 47  * @fileOverview
 48  * @name jamLayers.jsxinc
 49  * @author Michel MARIANI
 50  */
 51 
 52 //------------------------------------------------------------------------------
 53 
 54 if (typeof jamLayers !== 'object')
 55 {
 56     /**
 57      * Global object (used to simulate a namespace in JavaScript) containing
 58      * a set of layer-related functions for scripts written with the
 59      * <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-action-manager/">JSON Action Manager</a> engine.
 60      * @author Michel MARIANI
 61      * @version 4.5
 62      * @namespace
 63      */
 64     var jamLayers = { };
 65     //
 66     (function ()
 67     {
 68         function getObjectClass (object)
 69         {
 70             return (jamEngine.parseCompact (object))[0];
 71         }
 72         //
 73         function toBlackAndWhite (desc)
 74         {
 75             function restoreDesc (desc)
 76             {
 77                 var restoredDesc = { };
 78                 for (var key in desc)
 79                 {
 80                     if (desc.hasOwnProperty (key))
 81                     {
 82                         var value = desc[key];
 83                         var typedValue = null;
 84                         switch (key)
 85                         {
 86                             case "using":
 87                                 typedValue = [ "<path>", value ];
 88                                 break;
 89                             case "useTint":
 90                                 typedValue = [ "<boolean>", value ];
 91                                 break;
 92                             case "blue":
 93                             case "cyan":
 94                             case "green":
 95                             case "magenta":
 96                             case "red":
 97                             case "yellow":
 98                                 typedValue = [ "<integer>", value ];
 99                                 break;
100                             case "tintColor":
101                                 typedValue = jamHelpers.toColorObject (value);
102                                 break;
103                         }
104                         if (typedValue)
105                         {
106                             restoredDesc[key] = typedValue;
107                         }
108                     }
109                 }
110                 return restoredDesc;
111             }
112             //
113             return restoreDesc (desc);
114         }
115         //
116         function toBrightnessContrast (desc)
117         {
118             function restoreDesc (desc)
119             {
120                 var restoredDesc = { };
121                 for (var key in desc)
122                 {
123                     if (desc.hasOwnProperty (key))
124                     {
125                         var value = desc[key];
126                         var typedValue = null;
127                         switch (key)
128                         {
129                             case "useLegacy":
130                                 typedValue = [ "<boolean>", value ];
131                                 break;
132                             case "brightness":
133                             case "contrast":
134                                 typedValue = [ "<integer>", value ];
135                                 break;
136                         }
137                         if (typedValue)
138                         {
139                             restoredDesc[key] = typedValue;
140                         }
141                     }
142                 }
143                 return restoredDesc;
144             }
145             //
146             return restoreDesc (desc);
147         }
148         //
149         function toChannelMixer (desc)
150         {
151             function restoreDesc (desc, hintData)
152             {
153                 var restoredDesc = { };
154                 for (var key in desc)
155                 {
156                     if (desc.hasOwnProperty (key))
157                     {
158                         var value = desc[key];
159                         var typedValue = null;
160                         switch (key)
161                         {
162                             case "using":
163                                 typedValue = [ "<path>", value ];
164                                 break;
165                             case "monochromatic":
166                                 typedValue = [ "<boolean>", value ];
167                                 break;
168                             case "black":
169                             case "blue":
170                             case "cyan":
171                             case "green":
172                             case "magenta":
173                             case "red":
174                             case "yellowColor":
175                                 if (hintData)
176                                 {
177                                     typedValue = [ "<unitDouble>", [ hintData, value ] ];
178                                 }
179                                 else
180                                 {
181                                     typedValue = [ "<object>", [ "channelMatrix", restoreDesc (value, "percentUnit") ] ];
182                                 }
183                                 break;
184                             case "gray":
185                                 typedValue = [ "<object>", [ "channelMatrix", restoreDesc (value, "percentUnit") ] ];
186                                 break;
187                             case "constant":
188                                 typedValue = [ "<unitDouble>", [ hintData, value ] ];
189                                 break;
190                         }
191                         if (typedValue)
192                         {
193                             restoredDesc[key] = typedValue;
194                         }
195                     }
196                 }
197                 return restoredDesc;
198             }
199             //
200             return restoreDesc (desc);
201         }
202         //
203         function toColorBalance (desc)
204         {
205             function restoreDesc (desc)
206             {
207                 var restoredDesc = { };
208                 for (var key in desc)
209                 {
210                     if (desc.hasOwnProperty (key))
211                     {
212                         var value = desc[key];
213                         var typedValue = null;
214                         var restoredList;
215                         switch (key)
216                         {
217                             case "preserveLuminosity":
218                                 typedValue = [ "<boolean>", value ];
219                                 break;
220                             case "shadowLevels":
221                             case "midtoneLevels":
222                             case "highlightLevels":
223                                 restoredList = [ ];
224                                 for (var i = 0; i < value.length; i++)
225                                 {
226                                     restoredList.push ([ "<integer>", value[i] ]);
227                                 }
228                                 typedValue = [ "<list>", restoredList ];
229                                 break;
230                         }
231                         if (typedValue)
232                         {
233                             restoredDesc[key] = typedValue;
234                         }
235                     }
236                 }
237                 return restoredDesc;
238             }
239             //
240             return restoreDesc (desc);
241         }
242         //
243         function toCurves (desc)
244         {
245             function restoreDesc (desc)
246             {
247                 var restoredDesc = { };
248                 for (var key in desc)
249                 {
250                     if (desc.hasOwnProperty (key))
251                     {
252                         var value = desc[key];
253                         var typedValue = null;
254                         var restoredList;
255                         switch (key)
256                         {
257                             case "using":
258                                 typedValue = [ "<path>", value ];
259                                 break;
260                             case "horizontal":
261                             case "vertical":
262                                 typedValue = [ "<double>", value ];
263                                 break;
264                             case "adjustment":
265                                 restoredList = [ ];
266                                 for (var i = 0; i < value.length; i++)
267                                 {
268                                     restoredList.push ([ "<object>", [ "curvesAdjustment", restoreDesc (value[i]) ] ]);
269                                 }
270                                 typedValue = [ "<list>", restoredList ];
271                                 break;
272                             case "curve":
273                                 restoredList = [ ];
274                                 for (var i = 0; i < value.length; i++)
275                                 {
276                                     restoredList.push ([ "<object>", [ "point", restoreDesc (value[i]) ] ]);
277                                 }
278                                 typedValue = [ "<list>", restoredList ];
279                                 break;
280                             case "mapping":
281                                 restoredList = [ ];
282                                 for (var i = 0; i < value.length; i++)
283                                 {
284                                     restoredList.push ([ "<integer>", value[i] ]);
285                                 }
286                                 typedValue = [ "<list>", restoredList ];
287                                 break;
288                             case "channel":
289                                 typedValue = [ "<reference>", [ [ "channel", [ "<enumerated>", [ "channel", value ] ] ] ] ]; // Special handling
290                                 break;
291                         }
292                         if (typedValue)
293                         {
294                             restoredDesc[key] = typedValue;
295                         }
296                     }
297                 }
298                 return restoredDesc;
299             }
300             //
301             return restoreDesc (desc);
302         }
303         //
304         function toExposure (desc)
305         {
306             function restoreDesc (desc)
307             {
308                 var restoredDesc = { };
309                 for (var key in desc)
310                 {
311                     if (desc.hasOwnProperty (key))
312                     {
313                         var value = desc[key];
314                         var typedValue = null;
315                         switch (key)
316                         {
317                             case "using":
318                                 typedValue = [ "<path>", value ];
319                                 break;
320                             case "exposure":
321                             case "gammaCorrection":
322                             case "offset":
323                                 typedValue = [ "<double>", value ];
324                                 break;
325                         }
326                         if (typedValue)
327                         {
328                             restoredDesc[key] = typedValue;
329                         }
330                     }
331                 }
332                 return restoredDesc;
333             }
334             //
335             return restoreDesc (desc);
336         }
337         //
338         function toGradientMap (desc)
339         {
340             function restoreDesc (desc)
341             {
342                 var restoredDesc = { };
343                 for (var key in desc)
344                 {
345                     if (desc.hasOwnProperty (key))
346                     {
347                         var value = desc[key];
348                         var typedValue = null;
349                         switch (key)
350                         {
351                             case "dither":
352                             case "reverse":
353                                 typedValue = [ "<boolean>", value ];
354                                 break;
355                             case "gradient":
356                                 typedValue = jamHelpers.toGradientObject (value);
357                                 break;
358                         }
359                         if (typedValue)
360                         {
361                             restoredDesc[key] = typedValue;
362                         }
363                     }
364                 }
365                 return restoredDesc;
366             }
367             //
368             return restoreDesc (desc);
369         }
370         //
371         function toHueSaturation (desc)
372         {
373             function restoreDesc (desc)
374             {
375                 var restoredDesc = { };
376                 for (var key in desc)
377                 {
378                     if (desc.hasOwnProperty (key))
379                     {
380                         var value = desc[key];
381                         var typedValue = null;
382                         var restoredList;
383                         switch (key)
384                         {
385                             case "using":
386                                 typedValue = [ "<path>", value ];
387                                 break;
388                             case "colorize":
389                                 typedValue = [ "<boolean>", value ];
390                                 break;
391                             case "beginRamp":
392                             case "beginSustain":
393                             case "endRamp":
394                             case "endSustain":
395                             case "hue":
396                             case "lightness":
397                             case "localRange":
398                             case "saturation":
399                                 typedValue = [ "<integer>", value ];
400                                 break;
401                             case "adjustment":
402                                 restoredList = [ ];
403                                 for (var i = 0; i < value.length; i++)
404                                 {
405                                     restoredList.push ([ "<object>", [ "hueSatAdjustmentV2", restoreDesc (value[i]) ] ]);
406                                 }
407                                 typedValue = [ "<list>", restoredList ];
408                                 break;
409                             case "channel":
410                                 typedValue = [ "<reference>", [ [ "channel", [ "<enumerated>", [ "channel", value ] ] ] ] ]; // Special handling
411                                 break;
412                         }
413                         if (typedValue)
414                         {
415                             restoredDesc[key] = typedValue;
416                         }
417                     }
418                 }
419                 return restoredDesc;
420             }
421             //
422             return restoreDesc (desc);
423         }
424         //
425         function toLevels (desc)
426         {
427             function restoreDesc (desc)
428             {
429                 var restoredDesc = { };
430                 for (var key in desc)
431                 {
432                     if (desc.hasOwnProperty (key))
433                     {
434                         var value = desc[key];
435                         var typedValue = null;
436                         var restoredList;
437                         switch (key)
438                         {
439                             case "using":
440                                 typedValue = [ "<path>", value ];
441                                 break;
442                             case "auto":
443                             case "autoBlackWhite":
444                             case "autoContrast":
445                             case "autoNeutrals":
446                                 typedValue = [ "<boolean>", value ];
447                                 break;
448                             case "blackClip":
449                             case "gamma":
450                             case "whiteClip":
451                                 typedValue = [ "<double>", value ];
452                                 break;
453                             case "input":
454                             case "output":
455                                 restoredList = [ ];
456                                 for (var i = 0; i < value.length; i++)
457                                 {
458                                     restoredList.push ([ "<integer>", value[i] ]);
459                                 }
460                                 typedValue = [ "<list>", restoredList ];
461                                 break;
462                             case "adjustment":
463                                 restoredList = [ ];
464                                 for (var i = 0; i < value.length; i++)
465                                 {
466                                     restoredList.push ([ "<object>", [ "levelsAdjustment", restoreDesc (value[i]) ] ]);
467                                 }
468                                 typedValue = [ "<list>", restoredList ];
469                                 break;
470                             case "channel":
471                                 typedValue = [ "<reference>", [ [ "channel", [ "<enumerated>", [ "channel", value ] ] ] ] ]; // Special handling
472                                 break;
473                         }
474                         if (typedValue)
475                         {
476                             restoredDesc[key] = typedValue;
477                         }
478                     }
479                 }
480                 return restoredDesc;
481             }
482             //
483             return restoreDesc (desc);
484         }
485         //
486         function toPhotoFilter (desc)
487         {
488             function restoreDesc (desc)
489             {
490                 var restoredDesc = { };
491                 for (var key in desc)
492                 {
493                     if (desc.hasOwnProperty (key))
494                     {
495                         var value = desc[key];
496                         var typedValue = null;
497                         switch (key)
498                         {
499                             case "preserveLuminosity":
500                                 typedValue = [ "<boolean>", value ];
501                                 break;
502                             case "density":
503                                 typedValue = [ "<integer>", value ];
504                                 break;
505                             case "color":
506                                 typedValue = jamHelpers.toColorObject (value);
507                                 break;
508                         }
509                         if (typedValue)
510                         {
511                             restoredDesc[key] = typedValue;
512                         }
513                     }
514                 }
515                 return restoredDesc;
516             }
517             //
518             return restoreDesc (desc);
519         }
520         //
521         function toPosterize (desc)
522         {
523             function restoreDesc (desc)
524             {
525                 var restoredDesc = { };
526                 for (var key in desc)
527                 {
528                     if (desc.hasOwnProperty (key))
529                     {
530                         var value = desc[key];
531                         var typedValue = null;
532                         switch (key)
533                         {
534                             case "levels":
535                                 typedValue = [ "<integer>", value ];
536                                 break;
537                         }
538                         if (typedValue)
539                         {
540                             restoredDesc[key] = typedValue;
541                         }
542                     }
543                 }
544                 return restoredDesc;
545             }
546             //
547             return restoreDesc (desc);
548         }
549         //
550         function toSelectiveColor (desc)
551         {
552             function restoreDesc (desc)
553             {
554                 var restoredDesc = { };
555                 for (var key in desc)
556                 {
557                     if (desc.hasOwnProperty (key))
558                     {
559                         var value = desc[key];
560                         var typedValue = null;
561                         var restoredList;
562                         switch (key)
563                         {
564                             case "using":
565                                 typedValue = [ "<path>", value ];
566                                 break;
567                             case "colors":
568                                 typedValue = [ "<enumerated>", [ "colors", value ] ];
569                                 break;
570                             case "method":
571                                 typedValue = [ "<enumerated>", [ "correctionMethod", value ] ];
572                                 break;
573                             case "black":
574                             case "cyan":
575                             case "magenta":
576                             case "yellowColor":
577                                 typedValue = [ "<unitDouble>", [ "percentUnit", value ] ];
578                                 break;
579                             case "colorCorrection":
580                                 restoredList = [ ];
581                                 for (var i = 0; i < value.length; i++)
582                                 {
583                                     restoredList.push ([ "<object>", [ "colorCorrection", restoreDesc (value[i]) ] ]);
584                                 }
585                                 typedValue = [ "<list>", restoredList ];
586                                 break;
587                         }
588                         if (typedValue)
589                         {
590                             restoredDesc[key] = typedValue;
591                         }
592                     }
593                 }
594                 return restoredDesc;
595             }
596             //
597             return restoreDesc (desc);
598         }
599         //
600         function toThreshold (desc)
601         {
602             function restoreDesc (desc)
603             {
604                 var restoredDesc = { };
605                 for (var key in desc)
606                 {
607                     if (desc.hasOwnProperty (key))
608                     {
609                         var value = desc[key];
610                         var typedValue = null;
611                         switch (key)
612                         {
613                             case "level":
614                                 typedValue = [ "<integer>", value ];
615                                 break;
616                         }
617                         if (typedValue)
618                         {
619                             restoredDesc[key] = typedValue;
620                         }
621                     }
622                 }
623                 return restoredDesc;
624             }
625             //
626             return restoreDesc (desc);
627         }
628         //
629         function toVibrance (desc)
630         {
631             function restoreDesc (desc)
632             {
633                 var restoredDesc = { };
634                 for (var key in desc)
635                 {
636                     if (desc.hasOwnProperty (key))
637                     {
638                         var value = desc[key];
639                         var typedValue = null;
640                         switch (key)
641                         {
642                             case "vibrance":
643                             case "saturation":
644                                 typedValue = [ "<integer>", value ];
645                                 break;
646                         }
647                         if (typedValue)
648                         {
649                             restoredDesc[key] = typedValue;
650                         }
651                     }
652                 }
653                 return restoredDesc;
654             }
655             //
656             return restoreDesc (desc);
657         }
658         //
659         function toGradient (desc)
660         {
661             function restoreDesc (desc, hintData)
662             {
663                 var restoredDesc = { };
664                 for (var key in desc)
665                 {
666                     if (desc.hasOwnProperty (key))
667                     {
668                         var value = desc[key];
669                         var typedValue = null;
670                         switch (key)
671                         {
672                             case "align":
673                             case "dither":
674                             case "reverse":
675                                 typedValue = [ "<boolean>", value ];
676                                 break;
677                             case "angle":
678                                 typedValue = [ "<unitDouble>", [ "angleUnit", value ] ];
679                                 break;
680                             case "scale":
681                                 typedValue = [ "<unitDouble>", [ "percentUnit", value ] ];
682                                 break;
683                             case "horizontal":
684                             case "vertical":
685                                 typedValue = [ "<unitDouble>", [ hintData, value ] ];
686                                 break;
687                             case "offset":
688                                 typedValue = [ "<object>", [ "point", restoreDesc (value, "percentUnit") ] ];
689                                 break;
690                             case "type":
691                                 typedValue = [ "<enumerated>", [ "gradientType", value ] ];
692                                 break;
693                             case "gradient":
694                                 typedValue = jamHelpers.toGradientObject (value);
695                                 break;
696                         }
697                         if (typedValue)
698                         {
699                             restoredDesc[key] = typedValue;
700                         }
701                     }
702                 }
703                 return restoredDesc;
704             }
705             //
706             return restoreDesc (desc);
707         }
708         //
709         function toPattern (desc)
710         {
711             function restoreDesc (desc, hintData)
712             {
713                 var restoredDesc = { };
714                 for (var key in desc)
715                 {
716                     if (desc.hasOwnProperty (key))
717                     {
718                         var value = desc[key];
719                         var typedValue = null;
720                         switch (key)
721                         {
722                             case "align":
723                                 typedValue = [ "<boolean>", value ];
724                                 break;
725                             case "ID":
726                             case "name":
727                                 typedValue = [ "<string>", value ];
728                                 break;
729                             case "scale":
730                                 typedValue = [ "<unitDouble>", [ "percentUnit", value ] ];
731                                 break;
732                             case "horizontal":
733                             case "vertical":
734                                 typedValue = [ "<double>", value ];
735                                 break;
736                             case "phase":
737                                 typedValue = [ "<object>", [ "point", restoreDesc (value) ] ];
738                                 break;
739                             case "pattern":
740                                 typedValue = [ "<object>", [ "pattern", restoreDesc (value) ] ];
741                                 break;
742                         }
743                         if (typedValue)
744                         {
745                             restoredDesc[key] = typedValue;
746                         }
747                     }
748                 }
749                 return restoredDesc;
750             }
751             //
752             return restoreDesc (desc);
753         }
754         //
755         function toSolidColor (desc)
756         {
757             function restoreDesc (desc)
758             {
759                 var restoredDesc = { };
760                 for (var key in desc)
761                 {
762                     if (desc.hasOwnProperty (key))
763                     {
764                         var value = desc[key];
765                         var typedValue = null;
766                         switch (key)
767                         {
768                             case "color":
769                                 typedValue = jamHelpers.toColorObject (value);
770                                 break;
771                         }
772                         if (typedValue)
773                         {
774                             restoredDesc[key] = typedValue;
775                         }
776                     }
777                 }
778                 return restoredDesc;
779             }
780             //
781             return restoreDesc (desc);
782         }
783         //
784         /**
785          * @description Get the current layer type object in JSON AM format from a simplified layer type JSON object.
786          * @param {Object} layerType Simplified layer type JSON object
787          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/layer-object-simplified-format/">Layer Object Simplified Format</a>)
788          * @returns {Object|Array} Current layer type object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
789          * @example
790          * var brightness10 =
791          * {
792          *     "brightnessContrast":
793          *     {
794          *         "brightness": 10
795          *     }
796          * };
797          * jamEngine.jsonPlay
798          * (
799          *     "set",
800          *     {
801          *         "target": jamLayers.toLayerTypeReference (brightness10),
802          *         "to": jamLayers.<strong>toLayerTypeObject</strong> (brightness10)
803          *     }
804          * );
805          */
806         jamLayers.toLayerTypeObject = function (layerType)
807         {
808             var result = null;
809             var typeArr = jamEngine.parseCompact (layerType);
810             var kind = typeArr[0];
811             var desc = typeArr[1];
812             var toAdjustmentFunctions =
813             {
814                 // Adjustment layers
815                 "blackAndWhite": toBlackAndWhite,
816                 "brightnessContrast": toBrightnessContrast,
817                 "channelMixer": toChannelMixer,
818                 "colorBalance": toColorBalance,
819                 "curves": toCurves,
820                 "exposure": toExposure,
821                 "gradientMapClass": toGradientMap,
822                 "hueSaturation": toHueSaturation,
823                 "invert": null,
824                 "levels": toLevels,
825                 "photoFilter": toPhotoFilter,
826                 "posterize": toPosterize,
827                 "selectiveColor": toSelectiveColor,
828                 "thresholdClassEvent": toThreshold,
829                 "vibrance": toVibrance,
830                 // Fill layers
831                 "gradientLayer": toGradient,
832                 "patternLayer": toPattern,
833                 "solidColorLayer": toSolidColor
834             };
835             if (kind in toAdjustmentFunctions)
836             {
837                 result = (desc) ? [ "<object>", [ kind, (toAdjustmentFunctions[kind]) (desc) ] ] : [ "<class>", kind ];
838             }
839             return result;
840         };
841         //
842         /**
843          * @description Get a layer object in JSON AM Data Format from a simplified layer JSON object.
844          * @param {Object} layer Simplified layer JSON object
845          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/layer-object-simplified-format/">Layer Object Simplified Format</a>)
846          * @returns {Object|Array} Layer object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
847          * @see jamLayers.fromLayerObject
848          * @example
849          * var redFilter =
850          * {
851          *     "adjustmentLayer":
852          *     {
853          *         "name": "Red Filter",
854          *         "opacity": 75,
855          *         "type":
856          *         {
857          *             "photoFilter":
858          *             {
859          *                 "color": { "red": 255, "green": 0, "blue": 0 },
860          *                 "density": 50,
861          *                 "preserveLuminosity": true
862          *             }
863          *         }
864          *     }
865          * };
866          * jamEngine.jsonPlay
867          * (
868          *     "make",
869          *     {
870          *         "target": jamLayers.toLayerClassReference (redFilter),
871          *         "using": jamLayers.<strong>toLayerObject</strong> (redFilter)
872          *     }
873          * );
874          */
875         jamLayers.toLayerObject = function (layer)
876         {
877             var that = this;
878             function restoreDesc (desc)
879             {
880                 var restoredDesc = { };
881                 for (var key in desc)
882                 {
883                     if (desc.hasOwnProperty (key))
884                     {
885                         var value = desc[key];
886                         var typedValue = null;
887                         var restoredList;
888                         switch (key)
889                         {
890                             case "group":
891                             case "blendClipped":
892                             case "blendInterior":
893                             case "fillNeutral":
894                             case "layerMaskAsGlobalMask":
895                             case "protectAll":
896                             case "protectComposite":
897                             case "protectPosition":
898                             case "protectTransparency":
899                             case "transparencyShapesLayer":
900                             case "vectorMaskAsGlobalMask":
901                                 typedValue = [ "<boolean>", value ];
902                                 break;
903                             case "name":
904                                 typedValue = [ "<string>", value ];
905                                 break;
906                             case "srcBlackMin":
907                             case "srcBlackMax":
908                             case "srcWhiteMin":
909                             case "srcWhiteMax":
910                             case "destBlackMin":
911                             case "destBlackMax":
912                             case "destWhiteMin":
913                             case "destWhiteMax":
914                                 typedValue = [ "<integer>", value ];
915                                 break;
916                             case "fillOpacity":
917                             case "opacity":
918                             case "userMaskDensity":
919                             case "vectorMaskDensity":
920                                 typedValue = [ "<unitDouble>", [ "percentUnit", value ] ];
921                                 break;
922                             case "userMaskFeather":
923                             case "vectorMaskFeather":
924                                 typedValue = [ "<unitDouble>", [ "pixelsUnit", value ] ];
925                                 break;
926                             case "mode":
927                                 typedValue = [ "<enumerated>", [ "blendMode", value ] ];
928                                 break;
929                             case "color":
930                                 typedValue = [ "<enumerated>", [ "color", value ] ];
931                                 break;
932                             case "knockout":
933                                 typedValue = [ "<enumerated>", [ "knockout", value ] ];
934                                 break;
935                             case "channel":
936                                 typedValue = [ "<reference>", [ [ "channel", [ "<enumerated>", [ "channel", value ] ] ] ] ]; // Special handling
937                                 break;
938                             case "layerLocking":
939                                 typedValue = [ "<object>", [ "layerLocking", restoreDesc (value) ] ];
940                                 break;
941                             case "blendRange":
942                                 restoredList = [ ];
943                                 for (var i = 0; i < value.length; i++)
944                                 {
945                                     restoredList.push ([ "<object>", [ "blendRange", restoreDesc (value[i]) ] ]);
946                                 }
947                                 typedValue = [ "<list>", restoredList ];
948                                 break;
949                             case "channelRestrictions":
950                                 restoredList = [ ];
951                                 for (var i = 0; i < value.length; i++)
952                                 {
953                                     restoredList.push ([ "<enumerated>", [ "channel", value[i] ] ]);
954                                 }
955                                 typedValue = [ "<list>", restoredList ];
956                                 break;
957                             case "type":
958                                 typedValue = that.toLayerTypeObject (value);
959                                 break;
960                         }
961                         if (typedValue)
962                         {
963                             restoredDesc[key] = typedValue;
964                         }
965                     }
966                 }
967                 return restoredDesc;
968             }
969             //
970             var layerClass = getObjectClass (layer);
971             return [ "<object>", [ layerClass, restoreDesc (layer[layerClass]) ] ];
972          };
973         //
974         /**
975          * @description Get a simplified layer JSON object from a layer object in JSON AM Data Format.
976          * @param {Object|Array} layerObject Layer object in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
977          * @returns {Object} Simplified layer JSON object
978          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/layer-object-simplified-format/">Layer Object Simplified Format</a>)
979          * @see jamLayers.toLayerObject
980          * @example
981          * var layerObject =
982          * {
983          *     "<object>":
984          *     {
985          *         "adjustmentLayer":
986          *         {
987          *             "name":
988          *             {
989          *                 "<string>": "Contrast"
990          *             },
991          *             "type":
992          *             {
993          *                 "<object>":
994          *                 {
995          *                     "brightnessContrast":
996          *                     {
997          *                         "brightness":
998          *                         {
999          *                             "<integer>": 0
1000          *                         },
1001          *                         "contrast":
1002          *                         {
1003          *                             "<integer>": 20
1004          *                         }
1005          *                     }
1006          *                 }
1007          *             }
1008          *         }
1009          *     }
1010          * };
1011          * var layerObj = jamLayers.<strong>fromLayerObject</strong> (layerObject);
1012          * alert (jamJSON.stringify (layerObj, '\t'));
1013          */
1014         jamLayers.fromLayerObject = function (layerObject)
1015         {
1016             var layer = { };
1017             function layerHook (desc, key, getDefaultValue)
1018             {
1019                 var replacedValue = undefined;
1020                 switch (key)
1021                 {
1022                     // Special handling
1023                     case "layerObject":
1024                     case "type":
1025                         if (desc[key][0] === "<object>")
1026                         {
1027                             replacedValue = { };
1028                             replacedValue [desc[key][1][0]] = getDefaultValue (desc, key);
1029                         }
1030                         else if (desc[key][0] === "<class>")
1031                         {
1032                             replacedValue = { };
1033                             replacedValue [desc[key][1]] = null;
1034                         }
1035                         break;
1036                     // Special handling
1037                     case "channel":
1038                         var value = getDefaultValue (desc, key);
1039                         replacedValue = value[0]["channel"];
1040                         break;
1041                 }
1042                 return replacedValue;
1043             }
1044             // Hack for the time being, until jamEngine.simplifyObject is modified to allow hooking the top-level object...
1045             return jamEngine.simplifyObject ([ "<object>", [ "layerObject", { "layerObject": layerObject } ] ], layerHook)["layerObject"];
1046         };
1047         //
1048         /**
1049          * @description Get a layer class reference in JSON AM format from a simplified layer JSON object.
1050          * @param {Object} layer Simplified layer JSON object
1051          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/layer-object-simplified-format/">Layer Object Simplified Format</a>)
1052          * @returns {Object|Array} Layer class reference in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
1053          * @example
1054          * var invertColor =
1055          * {
1056          *     "adjustmentLayer":
1057          *     {
1058          *         "mode": "color",
1059          *         "type":
1060          *         {
1061          *             "invert": null
1062          *         }
1063          *     }
1064          * };
1065          * jamEngine.jsonPlay
1066          * (
1067          *     "make",
1068          *     {
1069          *         "target": jamLayers.<strong>toLayerClassReference</strong> (invertColor),
1070          *         "using": jamLayers.toLayerObject (invertColor)
1071          *     }
1072          * );
1073          */
1074         jamLayers.toLayerClassReference = function (layer)
1075         {
1076             return [ "<reference>", [ [ getObjectClass (layer), [ "<class>", null ] ] ] ];
1077         };
1078         //
1079         /**
1080          * @description Create a new layer.
1081          * @param {Object} layer Simplified layer JSON object<br>
1082          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/layer-object-simplified-format/">Layer Object Simplified Format</a>)
1083          * @param {Boolean} [below] Create a new layer below the current one (false by default; ignored for adjustment and fill layers)
1084          * @example
1085          * var blueOverlay =
1086          * {
1087          *     "contentLayer":
1088          *     {
1089          *         "name": "Blue Color | Overlay 50%",
1090          *         "mode": "overlay",
1091          *         "opacity": 50,
1092          *         "type":
1093          *         {
1094          *             "solidColorLayer":
1095          *             {
1096          *                 "color": { "red": 0, "green": 0, "blue": 255 }
1097          *             }
1098          *         }
1099          *     }
1100          * };
1101          * jamLayers.<strong>makeLayer</strong> (blueOverlay);
1102          */
1103         jamLayers.makeLayer = function (layer, below)
1104         {
1105             var makeDesc =
1106             {
1107                 "target": this.toLayerClassReference (layer),
1108                 "using": this.toLayerObject (layer)
1109             };
1110             if (below)
1111             {
1112                 makeDesc["below"] = [ "<boolean>", below ];
1113             }
1114             jamEngine.jsonPlay ("make", makeDesc);
1115         };
1116         //
1117         /**
1118          * @description Get the current layer reference in JSON AM format from a simplified layer JSON object.
1119          * @param {Object} layer Simplified layer JSON object
1120          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/layer-object-simplified-format/">Layer Object Simplified Format</a>)
1121          * @returns {Object|Array} Current layer reference in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
1122          * @example
1123          * var overlay50 =
1124          * {
1125          *     "layer":
1126          *     {
1127          *         "mode": "overlay",
1128          *         "opacity": 50
1129          *     }
1130          * };
1131          * jamEngine.jsonPlay
1132          * (
1133          *     "set",
1134          *     {
1135          *         "target": jamLayers.<strong>toLayerReference</strong> (overlay50),
1136          *         "to": jamLayers.toLayerObject (overlay50)
1137          *     }
1138          * );
1139          */
1140         jamLayers.toLayerReference = function (layer)
1141         {
1142             return [ "<reference>", [ [ getObjectClass (layer), [ "<enumerated>", [ "ordinal", "targetEnum" ] ] ] ] ];
1143         };
1144         //
1145         /**
1146          * @description Set properties of the current layer.
1147          * @param {Object} layerProperties Simplified layer properties JSON object<br>
1148          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/layer-object-simplified-format/">Layer Object Simplified Format</a>)
1149          * @example
1150          * var layerProperties =
1151          * {
1152          *     "opacity": 80,
1153          *     "blendRange":
1154          *     [
1155          *         {
1156          *             "channel": "gray",
1157          *             "srcBlackMin": 10,
1158          *             "srcBlackMax": 20,
1159          *             "srcWhiteMin": 255,
1160          *             "srcWhiteMax": 255,
1161          *             "destBlackMin": 0,
1162          *             "destBlackMax": 0,
1163          *             "destWhiteMin": 235,
1164          *             "destWhiteMax": 245
1165          *         }
1166          *     ]
1167          * };
1168          * jamLayers.<strong>setLayerProperties</strong> (layerProperties);
1169          */
1170         jamLayers.setLayerProperties = function (layerProperties)
1171         {
1172             var layer = { "layer": layerProperties };
1173             var setDesc =
1174             {
1175                 "target": this.toLayerReference (layer),
1176                 "to": this.toLayerObject (layer)
1177             };
1178             jamEngine.jsonPlay ("set", setDesc);
1179         };
1180         //
1181         /**
1182          * @description Get the current layer type reference in JSON AM format from a simplified layer type JSON object.
1183          * @param {Object} layerType Simplified layer type JSON object
1184          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/layer-object-simplified-format/">Layer Object Simplified Format</a>)
1185          * @returns {Object|Array} Current layer type reference in <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-am-data-format/">JSON AM Data Format</a>
1186          * @example
1187          * var contrast50 =
1188          * {
1189          *     "brightnessContrast":
1190          *     {
1191          *         "contrast": 50
1192          *     }
1193          * };
1194          * jamEngine.jsonPlay
1195          * (
1196          *     "set",
1197          *     {
1198          *         "target": jamLayers.<strong>toLayerTypeReference</strong> (contrast50),
1199          *         "to": jamLayers.toLayerTypeObject (contrast50)
1200          *     }
1201          * );
1202          */
1203         jamLayers.toLayerTypeReference = function (layerType)
1204         {
1205             var layerTypeClasses =
1206             {
1207                 // Adjustment layers
1208                 "blackAndWhite": "adjustmentLayer",
1209                 "brightnessContrast": "adjustmentLayer",
1210                 "channelMixer": "adjustmentLayer",
1211                 "colorBalance": "adjustmentLayer",
1212                 "curves": "adjustmentLayer",
1213                 "exposure": "adjustmentLayer",
1214                 "gradientMapClass": "adjustmentLayer",
1215                 "hueSaturation": "adjustmentLayer",
1216                 "invert": "adjustmentLayer",
1217                 "levels": "adjustmentLayer",
1218                 "photoFilter": "adjustmentLayer",
1219                 "posterize": "adjustmentLayer",
1220                 "selectiveColor": "adjustmentLayer",
1221                 "thresholdClassEvent": "adjustmentLayer",
1222                 "vibrance": "adjustmentLayer",
1223                 // Fill layers
1224                 "gradientLayer": "contentLayer",
1225                 "patternLayer": "contentLayer",
1226                 "solidColorLayer": "contentLayer"
1227             };
1228             return [ "<reference>", [ [ layerTypeClasses[getObjectClass (layerType)], [ "<enumerated>", [ "ordinal", "targetEnum" ] ] ] ] ];
1229         };
1230         //
1231         /**
1232          * @description Set current layer type.
1233          * @param {Object} layerType Simplified layer type JSON object<br>
1234          * (cf. <a href="http://www.tonton-pixel.com/blog/json-photoshop-scripting/json-simplified-formats/layer-object-simplified-format/">Layer Object Simplified Format</a>)
1235          * @example
1236          * var layerType =
1237          * {
1238          *     "photoFilter":
1239          *     {
1240          *         "density": 50
1241          *     }
1242          * };
1243          * jamLayers.<strong>setLayerType</strong> (layerType);
1244          */
1245         jamLayers.setLayerType = function (layerType)
1246         {
1247             var setDesc =
1248             {
1249                 "target": this.toLayerTypeReference (layerType),
1250                 "to": this.toLayerTypeObject (layerType)
1251             };
1252             jamEngine.jsonPlay ("set", setDesc);
1253         };
1254         //
1255         /**
1256          * @description Group the current layer (create clipping mask).
1257          * @see jamLayers.ungroupLayer
1258          * @example
1259          * jamLayers.<strong>groupLayer</strong> ();
1260          */
1261         jamLayers.groupLayer = function ()
1262         {
1263             // In CS4, could use a descriptor with the current layer reference as "target", but this wouldn"t work in CS then...
1264             jamEngine.jsonPlay ("groupEvent");
1265         };
1266         //
1267         /**
1268          * @description Ungroup the current layer (release clipping mask).
1269          * @see jamLayers.groupLayer
1270          * @example
1271          * jamLayers.<strong>ungroupLayer</strong> ();
1272          */
1273         jamLayers.ungroupLayer = function ()
1274         {
1275            // In CS4, could use a descriptor with the current layer reference as "target", but this wouldn"t work in CS then...
1276             jamEngine.jsonPlay ("ungroup");
1277         };
1278     } ());
1279 }
1280 
1281 //------------------------------------------------------------------------------
1282 
1283