PageSourceSearch

https://www.colorgraphicswa.com/bundles/theme5-product-configure-b…5nUr0TB8fafYVEeCwG-gd1UzRLpl7rE_8_vzq4hylI1

js colorgraphicswa.com collected 2026-09-30 13:26:00 UTC 385,652 bytes, 8,846 lines download raw bytes

1/* Minification failed. Returning unminified contents.
2(27,20-21): run-time error JS1195: Expected expression: )
3(27,23-24): run-time error JS1195: Expected expression: >
4(28,27-28): run-time error JS1195: Expected expression: >
5(34,30-31): run-time error JS1195: Expected expression: )
6(34,33-34): run-time error JS1195: Expected expression: >
7(39,1-2): run-time error JS1002: Syntax error: }
8(40,23-24): run-time error JS1195: Expected expression: >
9(42,1-2): run-time error JS1002: Syntax error: }
10(43,56-57): run-time error JS1195: Expected expression: >
11(48,27-37): run-time error JS1004: Expected ';': readAsText
12(54,65-66): run-time error JS1003: Expected ':': }
13(54,69-70): run-time error JS1195: Expected expression: >
14(55,53-54): run-time error JS1195: Expected expression: >
15(55,76-77): run-time error JS1004: Expected ';': )
16(58,1-2): run-time error JS1002: Syntax error: }
17(86,11-12): run-time error JS1195: Expected expression: )
18(86,13-14): run-time error JS1004: Expected ';': {
19(1810,2-3): run-time error JS1195: Expected expression: )
20(1825,22-23): run-time error JS1004: Expected ';': {
21(5078,3-4): run-time error JS1004: Expected ';': )
22(5080,2-8): run-time error JS1197: Too many errors. The file might not be a JavaScript file: jQuery
23(57,5-77): run-time error JS1018: 'return' statement outside of function: return disallowedSvgElements.length === 0 && !hasScriptAttr ? svg : null
24(47,9-19): run-time error JS1018: 'return' statement outside of function: return svg
25(41,5-34): run-time error JS1018: 'return' statement outside of function: return obj.size !== undefined
26 */
27const svgDisallowed = [
28    'a',
29    'animate',
30    'color-profile',
31    'cursor',
32    'discard',
33    'fedropshadow',
34    'font-face',
35    'font-face-format',
36    'font-face-name',
37    'font-face-src',
38    'font-face-uri',
39    'foreignobject',
40    'hatch',
41    'hatchpath',
42    'mesh',
43    'meshgradient',
44    'meshpatch',
45    'meshrow',
46    'missing-glyph',
47    'script',
48    'set',
49    'solidcolor',
50    'unknown',
51    'use'
52];
53const getWindow = () => (typeof window === 'undefined' ? null : window);
54const readAsText = (svg) => new Promise((resolve) => {
55    if (!isFile(svg)) {
56        resolve(svg.toString('utf-8'));
57    }
58    else {
59        const fileReader = new FileReader();
60        fileReader.onload = () => {
61            resolve(fileReader.result);
62        };
63        fileReader.readAsText(svg);
64    }
65});
66const isFile = (obj) => {
67    return obj.size !== undefined;
68};
69const sanitizeSVG = async (svg, window = getWindow()) => {
70    if (!window)
71        throw new Error('DOM window required');
72    if (isFile(svg) && svg.type !== 'image/svg+xml')
73        return svg;
74    const svgText = await readAsText(svg);
75    if (!svgText)
76        throw new Error('Image corrupt');
77    const playground = window.document.createElement('template');
78    playground.innerHTML = svgText;
79    const svgEl = playground.content.firstElementChild;
80    const attributes = Array.from(svgEl.attributes).map(({ name }) => name);
81    const hasScriptAttr = !!attributes.find((attr) => attr.startsWith('on'));
82    const disallowedSvgElements = svgEl.querySelectorAll(svgDisallowed.join(','));
83    return disallowedSvgElements.length === 0 && !hasScriptAttr ? svg : null;
84};;
85
86/*
87 *
88 * More info at [www.dropzonejs.com](http://www.dropzonejs.com)
89 *
90 * Copyright (c) 2012, Matias Meno
91 *
92 * Permission is hereby granted, free of charge, to any person obtaining a copy
93 * of this software and associated documentation files (the "Software"), to deal
94 * in the Software without restriction, including without limitation the rights
95 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
96 * copies of the Software, and to permit persons to whom the Software is
97 * furnished to do so, subject to the following conditions:
98 *
99 * The above copyright notice and this permission notice shall be included in
100 * all copies or substantial portions of the Software.
101 *
102 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
103 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
104 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
105 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
106 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
107 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
108 * THE SOFTWARE.
109 *
110 */
111
112(function() {
113  var Dropzone, Emitter, camelize, contentLoaded, detectVerticalSquash, drawImageIOSFix, noop, without,
114    __slice = [].slice,
115    __hasProp = {}.hasOwnProperty,
116    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
117
118  noop = function() {};
119
120  Emitter = (function() {
121    function Emitter() {}
122
123    Emitter.prototype.addEventListener = Emitter.prototype.on;
124
125    Emitter.prototype.on = function(event, fn) {
126      this._callbacks = this._callbacks || {};
127      if (!this._callbacks[event]) {
128        this._callbacks[event] = [];
129      }
130      this._callbacks[event].push(fn);
131      return this;
132    };
133
134    Emitter.prototype.emit = function() {
135      var args, callback, callbacks, event, _i, _len;
136      event = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
137      this._callbacks = this._callbacks || {};
138      callbacks = this._callbacks[event];
139      if (callbacks) {
140        for (_i = 0, _len = callbacks.length; _i < _len; _i++) {
141          callback = callbacks[_i];
142          callback.apply(this, args);
143        }
144      }
145      return this;
146    };
147
148    Emitter.prototype.removeListener = Emitter.prototype.off;
149
150    Emitter.prototype.removeAllListeners = Emitter.prototype.off;
151
152    Emitter.prototype.removeEventListener = Emitter.prototype.off;
153
154    Emitter.prototype.off = function(event, fn) {
155      var callback, callbacks, i, _i, _len;
156      if (!this._callbacks || arguments.length === 0) {
157        this._callbacks = {};
158        return this;
159      }
160      callbacks = this._callbacks[event];
161      if (!callbacks) {
162        return this;
163      }
164      if (arguments.length === 1) {
165        delete this._callbacks[event];
166        return this;
167      }
168      for (i = _i = 0, _len = callbacks.length; _i < _len; i = ++_i) {
169        callback = callbacks[i];
170        if (callback === fn) {
171          callbacks.splice(i, 1);
172          break;
173        }
174      }
175      return this;
176    };
177
178    return Emitter;
179
180  })();
181
182  Dropzone = (function(_super) {
183    var extend, resolveOption;
184
185    __extends(Dropzone, _super);
186
187    Dropzone.prototype.Emitter = Emitter;
188
189
190    /*
191    This is a list of all available events you can register on a dropzone object.
192    
193    You can register an event handler like this:
194    
195        dropzone.on("dragEnter", function() { });
196     */
197
198    Dropzone.prototype.events = ["drop", "dragstart", "dragend", "dragenter", "dragover", "dragleave", "addedfile", "addedfiles", "removedfile", "thumbnail", "error", "errormultiple", "processing", "processingmultiple", "uploadprogress", "totaluploadprogress", "sending", "sendingmultiple", "success", "successmultiple", "canceled", "canceledmultiple", "complete", "completemultiple", "reset", "maxfilesexceeded", "maxfilesreached", "queuecomplete"];
199
200    Dropzone.prototype.defaultOptions = {
201      url: null,
202      method: "post",
203      withCredentials: false,
204      parallelUploads: 2,
205      uploadMultiple: false,
206      maxFilesize: 256,
207      paramName: "file",
208      createImageThumbnails: true,
209      maxThumbnailFilesize: 10,
210      thumbnailWidth: 120,
211      thumbnailHeight: 120,
212      filesizeBase: 1000,
213      maxFiles: null,
214      params: {},
215      clickable: true,
216      ignoreHiddenFiles: true,
217      acceptedFiles: null,
218      acceptedMimeTypes: null,
219      autoProcessQueue: true,
220      autoQueue: true,
221      addRemoveLinks: false,
222      previewsContainer: null,
223      hiddenInputContainer: "body",
224      capture: null,
225      dictDefaultMessage: "Drop files here to upload",
226      dictFallbackMessage: "Your browser does not support drag'n'drop file uploads.",
227      dictFallbackText: "Please use the fallback form below to upload your files like in the olden days.",
228      dictFileTooBig: "File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB.",
229      dictInvalidFileType: "You can't upload files of this type.",
230      dictResponseError: "Server responded with {{statusCode}} code.",
231      dictCancelUpload: "Cancel upload",
232      dictCancelUploadConfirmation: "Are you sure you want to cancel this upload?",
233      dictRemoveFile: "Remove file",
234      dictRemoveFileConfirmation: null,
235      dictMaxFilesExceeded: "You can not upload any more files.",
236      accept: function(file, done) {
237        return done();
238      },
239      init: function() {
240        return noop;
241      },
242      forceFallback: false,
243      fallback: function() {
244        var child, messageElement, span, _i, _len, _ref;
245        this.element.className = "" + this.element.className + " dz-browser-not-supported";
246        _ref = this.element.getElementsByTagName("div");
247        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
248          child = _ref[_i];
249          if (/(^| )dz-message($| )/.test(child.className)) {
250            messageElement = child;
251            child.className = "dz-message";
252            continue;
253          }
254        }
255        if (!messageElement) {
256          messageElement = Dropzone.createElement("<div class=\"dz-message\"><span></span></div>");
257          this.element.appendChild(messageElement);
258        }
259        span = messageElement.getElementsByTagName("span")[0];
260        if (span) {
261          if (span.textContent != null) {
262            span.textContent = this.options.dictFallbackMessage;
263          } else if (span.innerText != null) {
264            span.innerText = this.options.dictFallbackMessage;
265          }
266        }
267        return this.element.appendChild(this.getFallbackForm());
268      },
269      resize: function(file) {
270        var info, srcRatio, trgRatio;
271        info = {
272          srcX: 0,
273          srcY: 0,
274          srcWidth: file.width,
275          srcHeight: file.height
276        };
277        srcRatio = file.width / file.height;
278        info.optWidth = this.options.thumbnailWidth;
279        info.optHeight = this.options.thumbnailHeight;
280        if ((info.optWidth == null) && (info.optHeight == null)) {
281          info.optWidth = info.srcWidth;
282          info.optHeight = info.srcHeight;
283        } else if (info.optWidth == null) {
284          info.optWidth = srcRatio * info.optHeight;
285        } else if (info.optHeight == null) {
286          info.optHeight = (1 / srcRatio) * info.optWidth;
287        }
288        trgRatio = info.optWidth / info.optHeight;
289        if (file.height < info.optHeight || file.width < info.optWidth) {
290          info.trgHeight = info.srcHeight;
291          info.trgWidth = info.srcWidth;
292        } else {
293          if (srcRatio > trgRatio) {
294            info.srcHeight = file.height;
295            info.srcWidth = info.srcHeight * trgRatio;
296          } else {
297            info.srcWidth = file.width;
298            info.srcHeight = info.srcWidth / trgRatio;
299          }
300        }
301        info.srcX = (file.width - info.srcWidth) / 2;
302        info.srcY = (file.height - info.srcHeight) / 2;
303        return info;
304      },
305
306      /*
307      Those functions register themselves to the events on init and handle all
308      the user interface specific stuff. Overwriting them won't break the upload
309      but can break the way it's displayed.
310      You can overwrite them if you don't like the default behavior. If you just
311      want to add an additional event handler, register it on the dropzone object
312      and don't overwrite those options.
313       */
314      drop: function(e) {
315        return this.element.classList.remove("dz-drag-hover");
316      },
317      dragstart: noop,
318      dragend: function(e) {
319        return this.element.classList.remove("dz-drag-hover");
320      },
321      dragenter: function(e) {
322        return this.element.classList.add("dz-drag-hover");
323      },
324      dragover: function(e) {
325        return this.element.classList.add("dz-drag-hover");
326      },
327      dragleave: function(e) {
328        return this.element.classList.remove("dz-drag-hover");
329      },
330      paste: noop,
331      reset: function() {
332        return this.element.classList.remove("dz-started");
333      },
334      addedfile: function(file) {
335        var node, removeFileEvent, removeLink, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _results;
336        if (this.element === this.previewsContainer) {
337          this.element.classList.add("dz-started");
338        }
339        if (this.previewsContainer) {
340          file.previewElement = Dropzone.createElement(this.options.previewTemplate.trim());
341          file.previewTemplate = file.previewElement;
342          this.previewsContainer.appendChild(file.previewElement);
343          _ref = file.previewElement.querySelectorAll("[data-dz-name]");
344          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
345            node = _ref[_i];
346            node.textContent = file.name;
347          }
348          _ref1 = file.previewElement.querySelectorAll("[data-dz-size]");
349          for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
350            node = _ref1[_j];
351            node.innerHTML = this.filesize(file.size);
352          }
353          if (this.options.addRemoveLinks) {
354            file._removeLink = Dropzone.createElement("<a class=\"dz-remove\" href=\"javascript:undefined;\" data-dz-remove>" + this.options.dictRemoveFile + "</a>");
355            file.previewElement.appendChild(file._removeLink);
356          }
357          removeFileEvent = (function(_this) {
358            return function(e) {
359              e.preventDefault();
360              e.stopPropagation();
361              if (file.status === Dropzone.UPLOADING) {
362                return Dropzone.confirm(_this.options.dictCancelUploadConfirmation, function() {
363                  return _this.removeFile(file);
364                });
365              } else {
366                if (_this.options.dictRemoveFileConfirmation) {
367                  return Dropzone.confirm(_this.options.dictRemoveFileConfirmation, function() {
368                    return _this.removeFile(file);
369                  });
370                } else {
371                  return _this.removeFile(file);
372                }
373              }
374            };
375          })(this);
376          _ref2 = file.previewElement.querySelectorAll("[data-dz-remove]");
377          _results = [];
378          for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
379            removeLink = _ref2[_k];
380            _results.push(removeLink.addEventListener("click", removeFileEvent));
381          }
382          return _results;
383        }
384      },
385      removedfile: function(file) {
386        var _ref;
387        if (file.previewElement) {
388          if ((_ref = file.previewElement) != null) {
389            _ref.parentNode.removeChild(file.previewElement);
390          }
391        }
392        return this._updateMaxFilesReachedClass();
393      },
394      thumbnail: function(file, dataUrl) {
395        var thumbnailElement, _i, _len, _ref;
396        if (file.previewElement) {
397          file.previewElement.classList.remove("dz-file-preview");
398          _ref = file.previewElement.querySelectorAll("[data-dz-thumbnail]");
399          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
400            thumbnailElement = _ref[_i];
401            thumbnailElement.alt = file.name;
402            thumbnailElement.src = dataUrl;
403          }
404          return setTimeout(((function(_this) {
405            return function() {
406              return file.previewElement.classList.add("dz-image-preview");
407            };
408          })(this)), 1);
409        }
410      },
411      error: function(file, message) {
412        var node, _i, _len, _ref, _results;
413        if (file.previewElement) {
414          file.previewElement.classList.add("dz-error");
415          if (typeof message !== "String" && message.error) {
416            message = message.error;
417          }
418          _ref = file.previewElement.querySelectorAll("[data-dz-errormessage]");
419          _results = [];
420          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
421            node = _ref[_i];
422            _results.push(node.textContent = message);
423          }
424          return _results;
425        }
426      },
427      errormultiple: noop,
428      processing: function(file) {
429        if (file.previewElement) {
430          file.previewElement.classList.add("dz-processing");
431          if (file._removeLink) {
432            return file._removeLink.textContent = this.options.dictCancelUpload;
433          }
434        }
435      },
436      processingmultiple: noop,
437      uploadprogress: function(file, progress, bytesSent) {
438        var node, _i, _len, _ref, _results;
439        if (file.previewElement) {
440          _ref = file.previewElement.querySelectorAll("[data-dz-uploadprogress]");
441          _results = [];
442          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
443            node = _ref[_i];
444            if (node.nodeName === 'PROGRESS') {
445              _results.push(node.value = progress);
446            } else {
447              _results.push(node.style.width = "" + progress + "%");
448            }
449          }
450          return _results;
451        }
452      },
453      totaluploadprogress: noop,
454      sending: noop,
455      sendingmultiple: noop,
456      success: function(file) {
457        if (file.previewElement) {
458          return file.previewElement.classList.add("dz-success");
459        }
460      },
461      successmultiple: noop,
462      canceled: function(file) {
463        return this.emit("error", file, "Upload canceled.");
464      },
465      canceledmultiple: noop,
466      complete: function(file) {
467        if (file._removeLink) {
468          file._removeLink.textContent = this.options.dictRemoveFile;
469        }
470        if (file.previewElement) {
471          return file.previewElement.classList.add("dz-complete");
472        }
473      },
474      completemultiple: noop,
475      maxfilesexceeded: noop,
476      maxfilesreached: noop,
477      queuecomplete: noop,
478      addedfiles: noop,
479      previewTemplate: "<div class=\"dz-preview dz-file-preview\">\n  <div class=\"dz-image\"><img data-dz-thumbnail /></div>\n  <div class=\"dz-details\">\n    <div class=\"dz-size\"><span data-dz-size></span></div>\n    <div class=\"dz-filename\"><span data-dz-name></span></div>\n  </div>\n  <div class=\"dz-progress\"><span class=\"dz-upload\" data-dz-uploadprogress></span></div>\n  <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n  <div class=\"dz-success-mark\">\n    <svg width=\"54px\" height=\"54px\" viewBox=\"0 0 54 54\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:sketch=\"http://www.bohemiancoding.com/sketch/ns\">\n      <title>Check</title>\n      <defs></defs>\n      <g id=\"Page-1\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" sketch:type=\"MSPage\">\n        <path d=\"M23.5,31.8431458 L17.5852419,25.9283877 C16.0248253,24.3679711 13.4910294,24.366835 11.9289322,25.9289322 C10.3700136,27.4878508 10.3665912,30.0234455 11.9283877,31.5852419 L20.4147581,40.0716123 C20.5133999,40.1702541 20.6159315,40.2626649 20.7218615,40.3488435 C22.2835669,41.8725651 24.794234,41.8626202 26.3461564,40.3106978 L43.3106978,23.3461564 C44.8771021,21.7797521 44.8758057,19.2483887 43.3137085,17.6862915 C41.7547899,16.1273729 39.2176035,16.1255422 37.6538436,17.6893022 L23.5,31.8431458 Z M27,53 C41.3594035,53 53,41.3594035 53,27 C53,12.6405965 41.3594035,1 27,1 C12.6405965,1 1,12.6405965 1,27 C1,41.3594035 12.6405965,53 27,53 Z\" id=\"Oval-2\" stroke-opacity=\"0.198794158\" stroke=\"#747474\" fill-opacity=\"0.816519475\" fill=\"#FFFFFF\" sketch:type=\"MSShapeGroup\"></path>\n      </g>\n    </svg>\n  </div>\n  <div class=\"dz-error-mark\">\n    <svg width=\"54px\" height=\"54px\" viewBox=\"0 0 54 54\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:sketch=\"http://www.bohemiancoding.com/sketch/ns\">\n      <title>Error</title>\n      <defs></defs>\n      <g id=\"Page-1\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" sketch:type=\"MSPage\">\n        <g id=\"Check-+-Oval-2\" sketch:type=\"MSLayerGroup\" stroke=\"#747474\" stroke-opacity=\"0.198794158\" fill=\"#FFFFFF\" fill-opacity=\"0.816519475\">\n          <path d=\"M32.6568542,29 L38.3106978,23.3461564 C39.8771021,21.7797521 39.8758057,19.2483887 38.3137085,17.6862915 C36.7547899,16.1273729 34.2176035,16.1255422 32.6538436,17.6893022 L27,23.3431458 L21.3461564,17.6893022 C19.7823965,16.1255422 17.2452101,16.1273729 15.6862915,17.6862915 C14.1241943,19.2483887 14.1228979,21.7797521 15.6893022,23.3461564 L21.3431458,29 L15.6893022,34.6538436 C14.1228979,36.2202479 14.1241943,38.7516113 15.6862915,40.3137085 C17.2452101,41.8726271 19.7823965,41.8744578 21.3461564,40.3106978 L27,34.6568542 L32.6538436,40.3106978 C34.2176035,41.8744578 36.7547899,41.8726271 38.3137085,40.3137085 C39.8758057,38.7516113 39.8771021,36.2202479 38.3106978,34.6538436 L32.6568542,29 Z M27,53 C41.3594035,53 53,41.3594035 53,27 C53,12.6405965 41.3594035,1 27,1 C12.6405965,1 1,12.6405965 1,27 C1,41.3594035 12.6405965,53 27,53 Z\" id=\"Oval-2\" sketch:type=\"MSShapeGroup\"></path>\n        </g>\n      </g>\n    </svg>\n  </div>\n</div>"
480    };
481
482    extend = function() {
483      var key, object, objects, target, val, _i, _len;
484      target = arguments[0], objects = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
485      for (_i = 0, _len = objects.length; _i < _len; _i++) {
486        object = objects[_i];
487        for (key in object) {
488          val = object[key];
489          target[key] = val;
490        }
491      }
492      return target;
493    };
494
495    function Dropzone(element, options) {
496      var elementOptions, fallback, _ref;
497      this.element = element;
498      this.version = Dropzone.version;
499      this.defaultOptions.previewTemplate = this.defaultOptions.previewTemplate.replace(/\n*/g, "");
500      this.clickableElements = [];
501      this.listeners = [];
502      this.files = [];
503      if (typeof this.element === "string") {
504        this.element = document.querySelector(this.element);
505      }
506      if (!(this.element && (this.element.nodeType != null))) {
507        throw new Error("Invalid dropzone element.");
508      }
509      if (this.element.dropzone) {
510        throw new Error("Dropzone already attached.");
511      }
512      Dropzone.instances.push(this);
513      this.element.dropzone = this;
514      elementOptions = (_ref = Dropzone.optionsForElement(this.element)) != null ? _ref : {};
515      this.options = extend({}, this.defaultOptions, elementOptions, options != null ? options : {});
516      if (this.options.forceFallback || !Dropzone.isBrowserSupported()) {
517        return this.options.fallback.call(this);
518      }
519      if (this.options.url == null) {
520        this.options.url = this.element.getAttribute("action");
521      }
522      if (!this.options.url) {
523        throw new Error("No URL provided.");
524      }
525      if (this.options.acceptedFiles && this.options.acceptedMimeTypes) {
526        throw new Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated.");
527      }
528      if (this.options.acceptedMimeTypes) {
529        this.options.acceptedFiles = this.options.acceptedMimeTypes;
530        delete this.options.acceptedMimeTypes;
531      }
532      this.options.method = this.options.method.toUpperCase();
533      if ((fallback = this.getExistingFallback()) && fallback.parentNode) {
534        fallback.parentNode.removeChild(fallback);
535      }
536      if (this.options.previewsContainer !== false) {
537        if (this.options.previewsContainer) {
538          this.previewsContainer = Dropzone.getElement(this.options.previewsContainer, "previewsContainer");
539        } else {
540          this.previewsContainer = this.element;
541        }
542      }
543      if (this.options.clickable) {
544        if (this.options.clickable === true) {
545          this.clickableElements = [this.element];
546        } else {
547          this.clickableElements = Dropzone.getElements(this.options.clickable, "clickable");
548        }
549      }
550      this.init();
551    }
552
553    Dropzone.prototype.getAcceptedFiles = function() {
554      var file, _i, _len, _ref, _results;
555      _ref = this.files;
556      _results = [];
557      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
558        file = _ref[_i];
559        if (file.accepted) {
560          _results.push(file);
561        }
562      }
563      return _results;
564    };
565
566    Dropzone.prototype.getRejectedFiles = function() {
567      var file, _i, _len, _ref, _results;
568      _ref = this.files;
569      _results = [];
570      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
571        file = _ref[_i];
572        if (!file.accepted) {
573          _results.push(file);
574        }
575      }
576      return _results;
577    };
578
579    Dropzone.prototype.getFilesWithStatus = function(status) {
580      var file, _i, _len, _ref, _results;
581      _ref = this.files;
582      _results = [];
583      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
584        file = _ref[_i];
585        if (file.status === status) {
586          _results.push(file);
587        }
588      }
589      return _results;
590    };
591
592    Dropzone.prototype.getQueuedFiles = function() {
593      return this.getFilesWithStatus(Dropzone.QUEUED);
594    };
595
596    Dropzone.prototype.getUploadingFiles = function() {
597      return this.getFilesWithStatus(Dropzone.UPLOADING);
598    };
599
600    Dropzone.prototype.getAddedFiles = function() {
601      return this.getFilesWithStatus(Dropzone.ADDED);
602    };
603
604    Dropzone.prototype.getActiveFiles = function() {
605      var file, _i, _len, _ref, _results;
606      _ref = this.files;
607      _results = [];
608      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
609        file = _ref[_i];
610        if (file.status === Dropzone.UPLOADING || file.status === Dropzone.QUEUED) {
611          _results.push(file);
612        }
613      }
614      return _results;
615    };
616
617    Dropzone.prototype.init = function() {
618      var eventName, noPropagation, setupHiddenFileInput, _i, _len, _ref, _ref1;
619      if (this.element.tagName === "form") {
620        this.element.setAttribute("enctype", "multipart/form-data");
621      }
622      if (this.element.classList.contains("dropzone") && !this.element.querySelector(".dz-message")) {
623        this.element.appendChild(Dropzone.createElement("<div class=\"dz-default dz-message\"><span>" + this.options.dictDefaultMessage + "</span></div>"));
624      }
625      if (this.clickableElements.length) {
626        setupHiddenFileInput = (function(_this) {
627          return function() {
628            if (_this.hiddenFileInput) {
629              _this.hiddenFileInput.parentNode.removeChild(_this.hiddenFileInput);
630            }
631            _this.hiddenFileInput = document.createElement("input");
632            _this.hiddenFileInput.setAttribute("type", "file");
633            if ((_this.options.maxFiles == null) || _this.options.maxFiles > 1) {
634              _this.hiddenFileInput.setAttribute("multiple", "multiple");
635            }
636            _this.hiddenFileInput.className = "dz-hidden-input";
637            if (_this.options.acceptedFiles != null) {
638              _this.hiddenFileInput.setAttribute("accept", _this.options.acceptedFiles);
639            }
640            if (_this.options.capture != null) {
641              _this.hiddenFileInput.setAttribute("capture", _this.options.capture);
642            }
643            _this.hiddenFileInput.style.visibility = "hidden";
644            _this.hiddenFileInput.style.position = "absolute";
645            _this.hiddenFileInput.style.top = "0";
646            _this.hiddenFileInput.style.left = "0";
647            _this.hiddenFileInput.style.height = "0";
648            _this.hiddenFileInput.style.width = "0";
649            document.querySelector(_this.options.hiddenInputContainer).appendChild(_this.hiddenFileInput);
650            return _this.hiddenFileInput.addEventListener("change", function() {
651              var file, files, _i, _len;
652              files = _this.hiddenFileInput.files;
653              if (files.length) {
654                for (_i = 0, _len = files.length; _i < _len; _i++) {
655                  file = files[_i];
656                  _this.addFile(file);
657                }
658              }
659              _this.emit("addedfiles", files);
660              return setupHiddenFileInput();
661            });
662          };
663        })(this);
664        setupHiddenFileInput();
665      }
666      this.URL = (_ref = window.URL) != null ? _ref : window.webkitURL;
667      _ref1 = this.events;
668      for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
669        eventName = _ref1[_i];
670        this.on(eventName, this.options[eventName]);
671      }
672      this.on("uploadprogress", (function(_this) {
673        return function() {
674          return _this.updateTotalUploadProgress();
675        };
676      })(this));
677      this.on("removedfile", (function(_this) {
678        return function() {
679          return _this.updateTotalUploadProgress();
680        };
681      })(this));
682      this.on("canceled", (function(_this) {
683        return function(file) {
684          return _this.emit("complete", file);
685        };
686      })(this));
687      this.on("complete", (function(_this) {
688        return function(file) {
689          if (_this.getAddedFiles().length === 0 && _this.getUploadingFiles().length === 0 && _this.getQueuedFiles().length === 0) {
690            return setTimeout((function() {
691              return _this.emit("queuecomplete");
692            }), 0);
693          }
694        };
695      })(this));
696      noPropagation = function(e) {
697        e.stopPropagation();
698        if (e.preventDefault) {
699          return e.preventDefault();
700        } else {
701          return e.returnValue = false;
702        }
703      };
704      this.listeners = [
705        {
706          element: this.element,
707          events: {
708            "dragstart": (function(_this) {
709              return function(e) {
710                return _this.emit("dragstart", e);
711              };
712            })(this),
713            "dragenter": (function(_this) {
714              return function(e) {
715                noPropagation(e);
716                return _this.emit("dragenter", e);
717              };
718            })(this),
719            "dragover": (function(_this) {
720              return function(e) {
721                var efct;
722                try {
723                  efct = e.dataTransfer.effectAllowed;
724                } catch (_error) {}
725                e.dataTransfer.dropEffect = 'move' === efct || 'linkMove' === efct ? 'move' : 'copy';
726                noPropagation(e);
727                return _this.emit("dragover", e);
728              };
729            })(this),
730            "dragleave": (function(_this) {
731              return function(e) {
732                return _this.emit("dragleave", e);
733              };
734            })(this),
735            "drop": (function(_this) {
736              return function(e) {
737                noPropagation(e);
738                return _this.drop(e);
739              };
740            })(this),
741            "dragend": (function(_this) {
742              return function(e) {
743                return _this.emit("dragend", e);
744              };
745            })(this)
746          }
747        }
748      ];
749      this.clickableElements.forEach((function(_this) {
750        return function(clickableElement) {
751          return _this.listeners.push({
752            element: clickableElement,
753            events: {
754              "click": function(evt) {
755                if ((clickableElement !== _this.element) || (evt.target === _this.element || Dropzone.elementInside(evt.target, _this.element.querySelector(".dz-message")))) {
756                  _this.hiddenFileInput.click();
757                }
758                return true;
759              }
760            }
761          });
762        };
763      })(this));
764      this.enable();
765      return this.options.init.call(this);
766    };
767
768    Dropzone.prototype.destroy = function() {
769      var _ref;
770      this.disable();
771      this.removeAllFiles(true);
772      if ((_ref = this.hiddenFileInput) != null ? _ref.parentNode : void 0) {
773        this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput);
774        this.hiddenFileInput = null;
775      }
776      delete this.element.dropzone;
777      return Dropzone.instances.splice(Dropzone.instances.indexOf(this), 1);
778    };
779
780    Dropzone.prototype.updateTotalUploadProgress = function() {
781      var activeFiles, file, totalBytes, totalBytesSent, totalUploadProgress, _i, _len, _ref;
782      totalBytesSent = 0;
783      totalBytes = 0;
784      activeFiles = this.getActiveFiles();
785      if (activeFiles.length) {
786        _ref = this.getActiveFiles();
787        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
788          file = _ref[_i];
789          totalBytesSent += file.upload.bytesSent;
790          totalBytes += file.upload.total;
791        }
792        totalUploadProgress = 100 * totalBytesSent / totalBytes;
793      } else {
794        totalUploadProgress = 100;
795      }
796      return this.emit("totaluploadprogress", totalUploadProgress, totalBytes, totalBytesSent);
797    };
798
799    Dropzone.prototype._getParamName = function(n) {
800      if (typeof this.options.paramName === "function") {
801        return this.options.paramName(n);
802      } else {
803        return "" + this.options.paramName + (this.options.uploadMultiple ? "[" + n + "]" : "");
804      }
805    };
806
807    Dropzone.prototype.getFallbackForm = function() {
808      var existingFallback, fields, fieldsString, form;
809      if (existingFallback = this.getExistingFallback()) {
810        return existingFallback;
811      }
812      fieldsString = "<div class=\"dz-fallback\">";
813      if (this.options.dictFallbackText) {
814        fieldsString += "<p>" + this.options.dictFallbackText + "</p>";
815      }
816      fieldsString += "<input type=\"file\" name=\"" + (this._getParamName(0)) + "\" " + (this.options.uploadMultiple ? 'multiple="multiple"' : void 0) + " /><input type=\"submit\" value=\"Upload!\"></div>";
817      fields = Dropzone.createElement(fieldsString);
818      if (this.element.tagName !== "FORM") {
819        form = Dropzone.createElement("<form action=\"" + this.options.url + "\" enctype=\"multipart/form-data\" method=\"" + this.options.method + "\"></form>");
820        form.appendChild(fields);
821      } else {
822        this.element.setAttribute("enctype", "multipart/form-data");
823        this.element.setAttribute("method", this.options.method);
824      }
825      return form != null ? form : fields;
826    };
827
828    Dropzone.prototype.getExistingFallback = function() {
829      var fallback, getFallback, tagName, _i, _len, _ref;
830      getFallback = function(elements) {
831        var el, _i, _len;
832        for (_i = 0, _len = elements.length; _i < _len; _i++) {
833          el = elements[_i];
834          if (/(^| )fallback($| )/.test(el.className)) {
835            return el;
836          }
837        }
838      };
839      _ref = ["div", "form"];
840      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
841        tagName = _ref[_i];
842        if (fallback = getFallback(this.element.getElementsByTagName(tagName))) {
843          return fallback;
844        }
845      }
846    };
847
848    Dropzone.prototype.setupEventListeners = function() {
849      var elementListeners, event, listener, _i, _len, _ref, _results;
850      _ref = this.listeners;
851      _results = [];
852      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
853        elementListeners = _ref[_i];
854        _results.push((function() {
855          var _ref1, _results1;
856          _ref1 = elementListeners.events;
857          _results1 = [];
858          for (event in _ref1) {
859            listener = _ref1[event];
860            _results1.push(elementListeners.element.addEventListener(event, listener, false));
861          }
862          return _results1;
863        })());
864      }
865      return _results;
866    };
867
868    Dropzone.prototype.removeEventListeners = function() {
869      var elementListeners, event, listener, _i, _len, _ref, _results;
870      _ref = this.listeners;
871      _results = [];
872      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
873        elementListeners = _ref[_i];
874        _results.push((function() {
875          var _ref1, _results1;
876          _ref1 = elementListeners.events;
877          _results1 = [];
878          for (event in _ref1) {
879            listener = _ref1[event];
880            _results1.push(elementListeners.element.removeEventListener(event, listener, false));
881          }
882          return _results1;
883        })());
884      }
885      return _results;
886    };
887
888    Dropzone.prototype.disable = function() {
889      var file, _i, _len, _ref, _results;
890      this.clickableElements.forEach(function(element) {
891        return element.classList.remove("dz-clickable");
892      });
893      this.removeEventListeners();
894      _ref = this.files;
895      _results = [];
896      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
897        file = _ref[_i];
898        _results.push(this.cancelUpload(file));
899      }
900      return _results;
901    };
902
903    Dropzone.prototype.enable = function() {
904      this.clickableElements.forEach(function(element) {
905        return element.classList.add("dz-clickable");
906      });
907      return this.setupEventListeners();
908    };
909
910    Dropzone.prototype.filesize = function(size) {
911      var cutoff, i, selectedSize, selectedUnit, unit, units, _i, _len;
912      selectedSize = 0;
913      selectedUnit = "b";
914      if (size > 0) {
915        units = ['TB', 'GB', 'MB', 'KB', 'b'];
916        for (i = _i = 0, _len = units.length; _i < _len; i = ++_i) {
917          unit = units[i];
918          cutoff = Math.pow(this.options.filesizeBase, 4 - i) / 10;
919          if (size >= cutoff) {
920            selectedSize = size / Math.pow(this.options.filesizeBase, 4 - i);
921            selectedUnit = unit;
922            break;
923          }
924        }
925        selectedSize = Math.round(10 * selectedSize) / 10;
926      }
927      return "<strong>" + selectedSize + "</strong> " + selectedUnit;
928    };
929
930    Dropzone.prototype._updateMaxFilesReachedClass = function() {
931      if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) {
932        if (this.getAcceptedFiles().length === this.options.maxFiles) {
933          this.emit('maxfilesreached', this.files);
934        }
935        return this.element.classList.add("dz-max-files-reached");
936      } else {
937        return this.element.classList.remove("dz-max-files-reached");
938      }
939    };
940
941    Dropzone.prototype.drop = function(e) {
942      var files, items;
943      if (!e.dataTransfer) {
944        return;
945      }
946      this.emit("drop", e);
947      files = e.dataTransfer.files;
948      this.emit("addedfiles", files);
949      if (files.length) {
950        items = e.dataTransfer.items;
951        if (items && items.length && (items[0].webkitGetAsEntry != null)) {
952          this._addFilesFromItems(items);
953        } else {
954          this.handleFiles(files);
955        }
956      }
957    };
958
959    Dropzone.prototype.paste = function(e) {
960      var items, _ref;
961      if ((e != null ? (_ref = e.clipboardData) != null ? _ref.items : void 0 : void 0) == null) {
962        return;
963      }
964      this.emit("paste", e);
965      items = e.clipboardData.items;
966      if (items.length) {
967        return this._addFilesFromItems(items);
968      }
969    };
970
971    Dropzone.prototype.handleFiles = function(files) {
972      var file, _i, _len, _results;
973      _results = [];
974      for (_i = 0, _len = files.length; _i < _len; _i++) {
975        file = files[_i];
976        _results.push(this.addFile(file));
977      }
978      return _results;
979    };
980
981    Dropzone.prototype._addFilesFromItems = function(items) {
982      var entry, item, _i, _len, _results;
983      _results = [];
984      for (_i = 0, _len = items.length; _i < _len; _i++) {
985        item = items[_i];
986        if ((item.webkitGetAsEntry != null) && (entry = item.webkitGetAsEntry())) {
987          if (entry.isFile) {
988            _results.push(this.addFile(item.getAsFile()));
989          } else if (entry.isDirectory) {
990            _results.push(this._addFilesFromDirectory(entry, entry.name));
991          } else {
992            _results.push(void 0);
993          }
994        } else if (item.getAsFile != null) {
995          if ((item.kind == null) || item.kind === "file") {
996            _results.push(this.addFile(item.getAsFile()));
997          } else {
998            _results.push(void 0);
999          }
1000        } else {
1001          _results.push(void 0);
1002        }
1003      }
1004      return _results;
1005    };
1006
1007    Dropzone.prototype._addFilesFromDirectory = function(directory, path) {
1008      var dirReader, entriesReader;
1009      dirReader = directory.createReader();
1010      entriesReader = (function(_this) {
1011        return function(entries) {
1012          var entry, _i, _len;
1013          for (_i = 0, _len = entries.length; _i < _len; _i++) {
1014            entry = entries[_i];
1015            if (entry.isFile) {
1016              entry.file(function(file) {
1017                if (_this.options.ignoreHiddenFiles && file.name.substring(0, 1) === '.') {
1018                  return;
1019                }
1020                file.fullPath = "" + path + "/" + file.name;
1021                return _this.addFile(file);
1022              });
1023            } else if (entry.isDirectory) {
1024              _this._addFilesFromDirectory(entry, "" + path + "/" + entry.name);
1025            }
1026          }
1027        };
1028      })(this);
1029      return dirReader.readEntries(entriesReader, function(error) {
1030        return typeof console !== "undefined" && console !== null ? typeof console.log === "function" ? console.log(error) : void 0 : void 0;
1031      });
1032    };
1033
1034    Dropzone.prototype.accept = function(file, done) {
1035      if (file.size > this.options.maxFilesize * 1024 * 1024) {
1036        return done(this.options.dictFileTooBig.replace("{{filesize}}", Math.round(file.size / 1024 / 10.24) / 100).replace("{{maxFilesize}}", this.options.maxFilesize));
1037      } else if (!Dropzone.isValidFile(file, this.options.acceptedFiles)) {
1038        return done(this.options.dictInvalidFileType);
1039      } else if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) {
1040        done(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}", this.options.maxFiles));
1041        return this.emit("maxfilesexceeded", file);
1042      } else {
1043        return this.options.accept.call(this, file, done);
1044      }
1045    };
1046
1047    Dropzone.prototype.addFile = function(file) {
1048      file.upload = {
1049        progress: 0,
1050        total: file.size,
1051        bytesSent: 0
1052      };
1053      this.files.push(file);
1054      file.status = Dropzone.ADDED;
1055      this.emit("addedfile", file);
1056      this._enqueueThumbnail(file);
1057      return this.accept(file, (function(_this) {
1058        return function(error) {
1059          if (error) {
1060            file.accepted = false;
1061            _this._errorProcessing([file], error);
1062          } else {
1063            file.accepted = true;
1064            if (_this.options.autoQueue) {
1065              _this.enqueueFile(file);
1066            }
1067          }
1068          return _this._updateMaxFilesReachedClass();
1069        };
1070      })(this));
1071    };
1072
1073    Dropzone.prototype.enqueueFiles = function(files) {
1074      var file, _i, _len;
1075      for (_i = 0, _len = files.length; _i < _len; _i++) {
1076        file = files[_i];
1077        this.enqueueFile(file);
1078      }
1079      return null;
1080    };
1081
1082    Dropzone.prototype.enqueueFile = function(file) {
1083      if (file.status === Dropzone.ADDED && file.accepted === true) {
1084        file.status = Dropzone.QUEUED;
1085        if (this.options.autoProcessQueue) {
1086          return setTimeout(((function(_this) {
1087            return function() {
1088              return _this.processQueue();
1089            };
1090          })(this)), 0);
1091        }
1092      } else {
1093        throw new Error("This file can't be queued because it has already been processed or was rejected.");
1094      }
1095    };
1096
1097    Dropzone.prototype._thumbnailQueue = [];
1098
1099    Dropzone.prototype._processingThumbnail = false;
1100
1101    Dropzone.prototype._enqueueThumbnail = function(file) {
1102      if (this.options.createImageThumbnails && file.type.match(/image.*/) && file.size <= this.options.maxThumbnailFilesize * 1024 * 1024) {
1103        this._thumbnailQueue.push(file);
1104        return setTimeout(((function(_this) {
1105          return function() {
1106            return _this._processThumbnailQueue();
1107          };
1108        })(this)), 0);
1109      }
1110    };
1111
1112    Dropzone.prototype._processThumbnailQueue = function() {
1113      if (this._processingThumbnail || this._thumbnailQueue.length === 0) {
1114        return;
1115      }
1116      this._processingThumbnail = true;
1117      return this.createThumbnail(this._thumbnailQueue.shift(), (function(_this) {
1118        return function() {
1119          _this._processingThumbnail = false;
1120          return _this._processThumbnailQueue();
1121        };
1122      })(this));
1123    };
1124
1125    Dropzone.prototype.removeFile = function(file) {
1126      if (file.status === Dropzone.UPLOADING) {
1127        this.cancelUpload(file);
1128      }
1129      this.files = without(this.files, file);
1130      this.emit("removedfile", file);
1131      if (this.files.length === 0) {
1132        return this.emit("reset");
1133      }
1134    };
1135
1136    Dropzone.prototype.removeAllFiles = function(cancelIfNecessary) {
1137      var file, _i, _len, _ref;
1138      if (cancelIfNecessary == null) {
1139        cancelIfNecessary = false;
1140      }
1141      _ref = this.files.slice();
1142      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1143        file = _ref[_i];
1144        if (file.status !== Dropzone.UPLOADING || cancelIfNecessary) {
1145          this.removeFile(file);
1146        }
1147      }
1148      return null;
1149    };
1150
1151    Dropzone.prototype.createThumbnail = function(file, callback) {
1152      var fileReader;
1153      fileReader = new FileReader;
1154      fileReader.onload = (function(_this) {
1155        return function() {
1156          if (file.type === "image/svg+xml") {
1157            _this.emit("thumbnail", file, fileReader.result);
1158            if (callback != null) {
1159              callback();
1160            }
1161            return;
1162          }
1163          return _this.createThumbnailFromUrl(file, fileReader.result, callback);
1164        };
1165      })(this);
1166      return fileReader.readAsDataURL(file);
1167    };
1168
1169    Dropzone.prototype.createThumbnailFromUrl = function(file, imageUrl, callback, crossOrigin) {
1170      var img;
1171      img = document.createElement("img");
1172      if (crossOrigin) {
1173        img.crossOrigin = crossOrigin;
1174      }
1175      img.onload = (function(_this) {
1176        return function() {
1177          var canvas, ctx, resizeInfo, thumbnail, _ref, _ref1, _ref2, _ref3;
1178          file.width = img.width;
1179          file.height = img.height;
1180          resizeInfo = _this.options.resize.call(_this, file);
1181          if (resizeInfo.trgWidth == null) {
1182            resizeInfo.trgWidth = resizeInfo.optWidth;
1183          }
1184          if (resizeInfo.trgHeight == null) {
1185            resizeInfo.trgHeight = resizeInfo.optHeight;
1186          }
1187          canvas = document.createElement("canvas");
1188          ctx = canvas.getContext("2d");
1189          canvas.width = resizeInfo.trgWidth;
1190          canvas.height = resizeInfo.trgHeight;
1191          drawImageIOSFix(ctx, img, (_ref = resizeInfo.srcX) != null ? _ref : 0, (_ref1 = resizeInfo.srcY) != null ? _ref1 : 0, resizeInfo.srcWidth, resizeInfo.srcHeight, (_ref2 = resizeInfo.trgX) != null ? _ref2 : 0, (_ref3 = resizeInfo.trgY) != null ? _ref3 : 0, resizeInfo.trgWidth, resizeInfo.trgHeight);
1192          thumbnail = canvas.toDataURL("image/png");
1193          _this.emit("thumbnail", file, thumbnail);
1194          if (callback != null) {
1195            return callback();
1196          }
1197        };
1198      })(this);
1199      if (callback != null) {
1200        img.onerror = callback;
1201      }
1202      return img.src = imageUrl;
1203    };
1204
1205    Dropzone.prototype.processQueue = function() {
1206      var i, parallelUploads, processingLength, queuedFiles;
1207      parallelUploads = this.options.parallelUploads;
1208      processingLength = this.getUploadingFiles().length;
1209      i = processingLength;
1210      if (processingLength >= parallelUploads) {
1211        return;
1212      }
1213      queuedFiles = this.getQueuedFiles();
1214      if (!(queuedFiles.length > 0)) {
1215        return;
1216      }
1217      if (this.options.uploadMultiple) {
1218        return this.processFiles(queuedFiles.slice(0, parallelUploads - processingLength));
1219      } else {
1220        while (i < parallelUploads) {
1221          if (!queuedFiles.length) {
1222            return;
1223          }
1224          this.processFile(queuedFiles.shift());
1225          i++;
1226        }
1227      }
1228    };
1229
1230    Dropzone.prototype.processFile = function(file) {
1231      return this.processFiles([file]);
1232    };
1233
1234    Dropzone.prototype.processFiles = function(files) {
1235      var file, _i, _len;
1236      for (_i = 0, _len = files.length; _i < _len; _i++) {
1237        file = files[_i];
1238        file.processing = true;
1239        file.status = Dropzone.UPLOADING;
1240        this.emit("processing", file);
1241      }
1242      if (this.options.uploadMultiple) {
1243        this.emit("processingmultiple", files);
1244      }
1245      return this.uploadFiles(files);
1246    };
1247
1248    Dropzone.prototype._getFilesWithXhr = function(xhr) {
1249      var file, files;
1250      return files = (function() {
1251        var _i, _len, _ref, _results;
1252        _ref = this.files;
1253        _results = [];
1254        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1255          file = _ref[_i];
1256          if (file.xhr === xhr) {
1257            _results.push(file);
1258          }
1259        }
1260        return _results;
1261      }).call(this);
1262    };
1263
1264    Dropzone.prototype.cancelUpload = function(file) {
1265      var groupedFile, groupedFiles, _i, _j, _len, _len1, _ref;
1266      if (file.status === Dropzone.UPLOADING) {
1267        groupedFiles = this._getFilesWithXhr(file.xhr);
1268        for (_i = 0, _len = groupedFiles.length; _i < _len; _i++) {
1269          groupedFile = groupedFiles[_i];
1270          groupedFile.status = Dropzone.CANCELED;
1271        }
1272        file.xhr.abort();
1273        for (_j = 0, _len1 = groupedFiles.length; _j < _len1; _j++) {
1274          groupedFile = groupedFiles[_j];
1275          this.emit("canceled", groupedFile);
1276        }
1277        if (this.options.uploadMultiple) {
1278          this.emit("canceledmultiple", groupedFiles);
1279        }
1280      } else if ((_ref = file.status) === Dropzone.ADDED || _ref === Dropzone.QUEUED) {
1281        file.status = Dropzone.CANCELED;
1282        this.emit("canceled", file);
1283        if (this.options.uploadMultiple) {
1284          this.emit("canceledmultiple", [file]);
1285        }
1286      }
1287      if (this.options.autoProcessQueue) {
1288        return this.processQueue();
1289      }
1290    };
1291
1292    resolveOption = function() {
1293      var args, option;
1294      option = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
1295      if (typeof option === 'function') {
1296        return option.apply(this, args);
1297      }
1298      return option;
1299    };
1300
1301    Dropzone.prototype.uploadFile = function(file) {
1302      return this.uploadFiles([file]);
1303    };
1304
1305    Dropzone.prototype.uploadFiles = function(files) {
1306      var file, formData, handleError, headerName, headerValue, headers, i, input, inputName, inputType, key, method, option, progressObj, response, updateProgress, url, value, xhr, _i, _j, _k, _l, _len, _len1, _len2, _len3, _m, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
1307      xhr = new XMLHttpRequest();
1308      for (_i = 0, _len = files.length; _i < _len; _i++) {
1309        file = files[_i];
1310        file.xhr = xhr;
1311      }
1312      method = resolveOption(this.options.method, files);
1313      url = resolveOption(this.options.url, files);
1314      xhr.open(method, url, true);
1315      xhr.withCredentials = !!this.options.withCredentials;
1316      response = null;
1317      handleError = (function(_this) {
1318        return function() {
1319          var _j, _len1, _results;
1320          _results = [];
1321          for (_j = 0, _len1 = files.length; _j < _len1; _j++) {
1322            file = files[_j];
1323            _results.push(_this._errorProcessing(files, response || _this.options.dictResponseError.replace("{{statusCode}}", xhr.status), xhr));
1324          }
1325          return _results;
1326        };
1327      })(this);
1328      updateProgress = (function(_this) {
1329        return function(e) {
1330          var allFilesFinished, progress, _j, _k, _l, _len1, _len2, _len3, _results;
1331          if (e != null) {
1332            progress = 100 * e.loaded / e.total;
1333            for (_j = 0, _len1 = files.length; _j < _len1; _j++) {
1334              file = files[_j];
1335              file.upload = {
1336                progress: progress,
1337                total: e.total,
1338                bytesSent: e.loaded
1339              };
1340            }
1341          } else {
1342            allFilesFinished = true;
1343            progress = 100;
1344            for (_k = 0, _len2 = files.length; _k < _len2; _k++) {
1345              file = files[_k];
1346              if (!(file.upload.progress === 100 && file.upload.b
1346ytesSent === file.upload.total)) {
1347                allFilesFinished = false;
1348              }
1349              file.upload.progress = progress;
1350              file.upload.bytesSent = file.upload.total;
1351            }
1352            if (allFilesFinished) {
1353              return;
1354            }
1355          }
1356          _results = [];
1357          for (_l = 0, _len3 = files.length; _l < _len3; _l++) {
1358            file = files[_l];
1359            _results.push(_this.emit("uploadprogress", file, progress, file.upload.bytesSent));
1360          }
1361          return _results;
1362        };
1363      })(this);
1364      xhr.onload = (function(_this) {
1365        return function(e) {
1366          var _ref;
1367          if (files[0].status === Dropzone.CANCELED) {
1368            return;
1369          }
1370          if (xhr.readyState !== 4) {
1371            return;
1372          }
1373          response = xhr.responseText;
1374          if (xhr.getResponseHeader("content-type") && ~xhr.getResponseHeader("content-type").indexOf("application/json")) {
1375            try {
1376              response = JSON.parse(response);
1377            } catch (_error) {
1378              e = _error;
1379              response = "Invalid JSON response from server.";
1380            }
1381          }
1382          updateProgress();
1383          if (!((200 <= (_ref = xhr.status) && _ref < 300))) {
1384            return handleError();
1385          } else {
1386            return _this._finished(files, response, e);
1387          }
1388        };
1389      })(this);
1390      xhr.onerror = (function(_this) {
1391        return function() {
1392          if (files[0].status === Dropzone.CANCELED) {
1393            return;
1394          }
1395          return handleError();
1396        };
1397      })(this);
1398      progressObj = (_ref = xhr.upload) != null ? _ref : xhr;
1399      progressObj.onprogress = updateProgress;
1400      headers = {
1401        "Accept": "application/json",
1402        "Cache-Control": "no-cache",
1403        "X-Requested-With": "XMLHttpRequest"
1404      };
1405      if (this.options.headers) {
1406        extend(headers, this.options.headers);
1407      }
1408      for (headerName in headers) {
1409        headerValue = headers[headerName];
1410        if (headerValue) {
1411          xhr.setRequestHeader(headerName, headerValue);
1412        }
1413      }
1414      formData = new FormData();
1415      if (this.options.params) {
1416        _ref1 = this.options.params;
1417        for (key in _ref1) {
1418          value = _ref1[key];
1419          formData.append(key, value);
1420        }
1421      }
1422      for (_j = 0, _len1 = files.length; _j < _len1; _j++) {
1423        file = files[_j];
1424        this.emit("sending", file, xhr, formData);
1425      }
1426      if (this.options.uploadMultiple) {
1427        this.emit("sendingmultiple", files, xhr, formData);
1428      }
1429      if (this.element.tagName === "FORM") {
1430        _ref2 = this.element.querySelectorAll("input, textarea, select, button");
1431        for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
1432          input = _ref2[_k];
1433          inputName = input.getAttribute("name");
1434          inputType = input.getAttribute("type");
1435          if (input.tagName === "SELECT" && input.hasAttribute("multiple")) {
1436            _ref3 = input.options;
1437            for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
1438              option = _ref3[_l];
1439              if (option.selected) {
1440                formData.append(inputName, option.value);
1441              }
1442            }
1443          } else if (!inputType || ((_ref4 = inputType.toLowerCase()) !== "checkbox" && _ref4 !== "radio") || input.checked) {
1444            formData.append(inputName, input.value);
1445          }
1446        }
1447      }
1448      for (i = _m = 0, _ref5 = files.length - 1; 0 <= _ref5 ? _m <= _ref5 : _m >= _ref5; i = 0 <= _ref5 ? ++_m : --_m) {
1449        formData.append(this._getParamName(i), files[i], files[i].name);
1450      }
1451      return this.submitRequest(xhr, formData, files);
1452    };
1453
1454    Dropzone.prototype.submitRequest = function(xhr, formData, files) {
1455      return xhr.send(formData);
1456    };
1457
1458    Dropzone.prototype._finished = function(files, responseText, e) {
1459      var file, _i, _len;
1460      for (_i = 0, _len = files.length; _i < _len; _i++) {
1461        file = files[_i];
1462        file.status = Dropzone.SUCCESS;
1463        this.emit("success", file, responseText, e);
1464        this.emit("complete", file);
1465      }
1466      if (this.options.uploadMultiple) {
1467        this.emit("successmultiple", files, responseText, e);
1468        this.emit("completemultiple", files);
1469      }
1470      if (this.options.autoProcessQueue) {
1471        return this.processQueue();
1472      }
1473    };
1474
1475    Dropzone.prototype._errorProcessing = function(files, message, xhr) {
1476      var file, _i, _len;
1477      for (_i = 0, _len = files.length; _i < _len; _i++) {
1478        file = files[_i];
1479        file.status = Dropzone.ERROR;
1480        this.emit("error", file, message, xhr);
1481        this.emit("complete", file);
1482      }
1483      if (this.options.uploadMultiple) {
1484        this.emit("errormultiple", files, message, xhr);
1485        this.emit("completemultiple", files);
1486      }
1487      if (this.options.autoProcessQueue) {
1488        return this.processQueue();
1489      }
1490    };
1491
1492    return Dropzone;
1493
1494  })(Emitter);
1495
1496  Dropzone.version = "4.2.0";
1497
1498  Dropzone.options = {};
1499
1500  Dropzone.optionsForElement = function(element) {
1501    if (element.getAttribute("id")) {
1502      return Dropzone.options[camelize(element.getAttribute("id"))];
1503    } else {
1504      return void 0;
1505    }
1506  };
1507
1508  Dropzone.instances = [];
1509
1510  Dropzone.forElement = function(element) {
1511    if (typeof element === "string") {
1512      element = document.querySelector(element);
1513    }
1514    if ((element != null ? element.dropzone : void 0) == null) {
1515      throw new Error("No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone.");
1516    }
1517    return element.dropzone;
1518  };
1519
1520  Dropzone.autoDiscover = true;
1521
1522  Dropzone.discover = function() {
1523    var checkElements, dropzone, dropzones, _i, _len, _results;
1524    if (document.querySelectorAll) {
1525      dropzones = document.querySelectorAll(".dropzone");
1526    } else {
1527      dropzones = [];
1528      checkElements = function(elements) {
1529        var el, _i, _len, _results;
1530        _results = [];
1531        for (_i = 0, _len = elements.length; _i < _len; _i++) {
1532          el = elements[_i];
1533          if (/(^| )dropzone($| )/.test(el.className)) {
1534            _results.push(dropzones.push(el));
1535          } else {
1536            _results.push(void 0);
1537          }
1538        }
1539        return _results;
1540      };
1541      checkElements(document.getElementsByTagName("div"));
1542      checkElements(document.getElementsByTagName("form"));
1543    }
1544    _results = [];
1545    for (_i = 0, _len = dropzones.length; _i < _len; _i++) {
1546      dropzone = dropzones[_i];
1547      if (Dropzone.optionsForElement(dropzone) !== false) {
1548        _results.push(new Dropzone(dropzone));
1549      } else {
1550        _results.push(void 0);
1551      }
1552    }
1553    return _results;
1554  };
1555
1556  Dropzone.blacklistedBrowsers = [/opera.*Macintosh.*version\/12/i];
1557
1558  Dropzone.isBrowserSupported = function() {
1559    var capableBrowser, regex, _i, _len, _ref;
1560    capableBrowser = true;
1561    if (window.File && window.FileReader && window.FileList && window.Blob && window.FormData && document.querySelector) {
1562      if (!("classList" in document.createElement("a"))) {
1563        capableBrowser = false;
1564      } else {
1565        _ref = Dropzone.blacklistedBrowsers;
1566        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1567          regex = _ref[_i];
1568          if (regex.test(navigator.userAgent)) {
1569            capableBrowser = false;
1570            continue;
1571          }
1572        }
1573      }
1574    } else {
1575      capableBrowser = false;
1576    }
1577    return capableBrowser;
1578  };
1579
1580  without = function(list, rejectedItem) {
1581    var item, _i, _len, _results;
1582    _results = [];
1583    for (_i = 0, _len = list.length; _i < _len; _i++) {
1584      item = list[_i];
1585      if (item !== rejectedItem) {
1586        _results.push(item);
1587      }
1588    }
1589    return _results;
1590  };
1591
1592  camelize = function(str) {
1593    return str.replace(/[\-_](\w)/g, function(match) {
1594      return match.charAt(1).toUpperCase();
1595    });
1596  };
1597
1598  Dropzone.createElement = function(string) {
1599    var div;
1600    div = document.createElement("div");
1601    div.innerHTML = string;
1602    return div.childNodes[0];
1603  };
1604
1605  Dropzone.elementInside = function(element, container) {
1606    if (element === container) {
1607      return true;
1608    }
1609    while (element = element.parentNode) {
1610      if (element === container) {
1611        return true;
1612      }
1613    }
1614    return false;
1615  };
1616
1617  Dropzone.getElement = function(el, name) {
1618    var element;
1619    if (typeof el === "string") {
1620      element = document.querySelector(el);
1621    } else if (el.nodeType != null) {
1622      element = el;
1623    }
1624    if (element == null) {
1625      throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector or a plain HTML element.");
1626    }
1627    return element;
1628  };
1629
1630  Dropzone.getElements = function(els, name) {
1631    var e, el, elements, _i, _j, _len, _len1, _ref;
1632    if (els instanceof Array) {
1633      elements = [];
1634      try {
1635        for (_i = 0, _len = els.length; _i < _len; _i++) {
1636          el = els[_i];
1637          elements.push(this.getElement(el, name));
1638        }
1639      } catch (_error) {
1640        e = _error;
1641        elements = null;
1642      }
1643    } else if (typeof els === "string") {
1644      elements = [];
1645      _ref = document.querySelectorAll(els);
1646      for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
1647        el = _ref[_j];
1648        elements.push(el);
1649      }
1650    } else if (els.nodeType != null) {
1651      elements = [els];
1652    }
1653    if (!((elements != null) && elements.length)) {
1654      throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector, a plain HTML element or a list of those.");
1655    }
1656    return elements;
1657  };
1658
1659  Dropzone.confirm = function(question, accepted, rejected) {
1660    if (window.confirm(question)) {
1661      return accepted();
1662    } else if (rejected != null) {
1663      return rejected();
1664    }
1665  };
1666
1667  Dropzone.isValidFile = function(file, acceptedFiles) {
1668    var baseMimeType, mimeType, validType, _i, _len;
1669    if (!acceptedFiles) {
1670      return true;
1671    }
1672    acceptedFiles = acceptedFiles.split(",");
1673    mimeType = file.type;
1674    baseMimeType = mimeType.replace(/\/.*$/, "");
1675    for (_i = 0, _len = acceptedFiles.length; _i < _len; _i++) {
1676      validType = acceptedFiles[_i];
1677      validType = validType.trim();
1678      if (validType.charAt(0) === ".") {
1679        if (file.name.toLowerCase().indexOf(validType.toLowerCase(), file.name.length - validType.length) !== -1) {
1680          return true;
1681        }
1682      } else if (/\/\*$/.test(validType)) {
1683        if (baseMimeType === validType.replace(/\/.*$/, "")) {
1684          return true;
1685        }
1686      } else {
1687        if (mimeType === validType) {
1688          return true;
1689        }
1690      }
1691    }
1692    return false;
1693  };
1694
1695  if (typeof jQuery !== "undefined" && jQuery !== null) {
1696    jQuery.fn.dropzone = function(options) {
1697      return this.each(function() {
1698        return new Dropzone(this, options);
1699      });
1700    };
1701  }
1702
1703  if (typeof module !== "undefined" && module !== null) {
1704    module.exports = Dropzone;
1705  } else {
1706    window.Dropzone = Dropzone;
1707  }
1708
1709  Dropzone.ADDED = "added";
1710
1711  Dropzone.QUEUED = "queued";
1712
1713  Dropzone.ACCEPTED = Dropzone.QUEUED;
1714
1715  Dropzone.UPLOADING = "uploading";
1716
1717  Dropzone.PROCESSING = Dropzone.UPLOADING;
1718
1719  Dropzone.CANCELED = "canceled";
1720
1721  Dropzone.ERROR = "error";
1722
1723  Dropzone.SUCCESS = "success";
1724
1725
1726  /*
1727  
1728  Bugfix for iOS 6 and 7
1729  Source: http://stackoverflow.com/questions/11929099/html5-canvas-drawimage-ratio-bug-ios
1730  based on the work of https://github.com/stomita/ios-imagefile-megapixel
1731   */
1732
1733  detectVerticalSquash = function(img) {
1734    var alpha, canvas, ctx, data, ey, ih, iw, py, ratio, sy;
1735    iw = img.naturalWidth;
1736    ih = img.naturalHeight;
1737    canvas = document.createElement("canvas");
1738    canvas.width = 1;
1739    canvas.height = ih;
1740    ctx = canvas.getContext("2d");
1741    ctx.drawImage(img, 0, 0);
1742    data = ctx.getImageData(0, 0, 1, ih).data;
1743    sy = 0;
1744    ey = ih;
1745    py = ih;
1746    while (py > sy) {
1747      alpha = data[(py - 1) * 4 + 3];
1748      if (alpha === 0) {
1749        ey = py;
1750      } else {
1751        sy = py;
1752      }
1753      py = (ey + sy) >> 1;
1754    }
1755    ratio = py / ih;
1756    if (ratio === 0) {
1757      return 1;
1758    } else {
1759      return ratio;
1760    }
1761  };
1762
1763  drawImageIOSFix = function(ctx, img, sx, sy, sw, sh, dx, dy, dw, dh) {
1764    var vertSquashRatio;
1765    vertSquashRatio = detectVerticalSquash(img);
1766    return ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh / vertSquashRatio);
1767  };
1768
1769
1770  /*
1771   * contentloaded.js
1772   *
1773   * Author: Diego Perini (diego.perini at gmail.com)
1774   * Summary: cross-browser wrapper for DOMContentLoaded
1775   * Updated: 20101020
1776   * License: MIT
1777   * Version: 1.2
1778   *
1779   * URL:
1780   * http://javascript.nwbox.com/ContentLoaded/
1781   * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE
1782   */
1783
1784  contentLoaded = function(win, fn) {
1785    var add, doc, done, init, poll, pre, rem, root, top;
1786    done = false;
1787    top = true;
1788    doc = win.document;
1789    root = doc.documentElement;
1790    add = (doc.addEventListener ? "addEventListener" : "attachEvent");
1791    rem = (doc.addEventListener ? "removeEventListener" : "detachEvent");
1792    pre = (doc.addEventListener ? "" : "on");
1793    init = function(e) {
1794      if (e.type === "readystatechange" && doc.readyState !== "complete") {
1795        return;
1796      }
1797      (e.type === "load" ? win : doc)[rem](pre + e.type, init, false);
1798      if (!done && (done = true)) {
1799        return fn.call(win, e.type || e);
1800      }
1801    };
1802    poll = function() {
1803      var e;
1804      try {
1805        root.doScroll("left");
1806      } catch (_error) {
1807        e = _error;
1808        setTimeout(poll, 50);
1809        return;
1810      }
1811      return init("poll");
1812    };
1813    if (doc.readyState !== "complete") {
1814      if (doc.createEventObject && root.doScroll) {
1815        try {
1816          top = !win.frameElement;
1817        } catch (_error) {}
1818        if (top) {
1819          poll();
1820        }
1821      }
1822      doc[add](pre + "DOMContentLoaded", init, false);
1823      doc[add](pre + "readystatechange", init, false);
1824      return win[add](pre + "load", init, false);
1825    }
1826  };
1827
1828  Dropzone._autoDiscoverFunction = function() {
1829    if (Dropzone.autoDiscover) {
1830      return Dropzone.discover();
1831    }
1832  };
1833
1834  contentLoaded(window, Dropzone._autoDiscoverFunction);
1835
1836}).call(this);
1837;
1838/*jslint devel: true, bitwise: true, regexp: true, browser: true, confusion: true, unparam: true, eqeq: true, white: true, nomen: true, plusplus: true, maxerr: 50, indent: 4 */
1839/*globals jQuery,Color,define */
1840
1841/*!
1842 * ColorPicker
1843 *
1844 * Copyright (c) 2011-2016 Martijn W. van der Lee
1845 * Licensed under the MIT.
1846 */
1847/* Full-featured colorpicker for jQueryUI with full theming support.
1848 * Most images from jPicker by Christopher T. Tillman.
1849 * Sourcecode created from scratch by Martijn W. van der Lee.
1850 */
1851(function( factory ) {
1852	if ( typeof define === "function" && define.amd ) {
1853
1854		// AMD. Register as an anonymous module.
1855		define([
1856			"jquery"
1857		], factory );
1858	} else {
1859
1860		// Browser globals
1861		factory( jQuery );
1862	}
1863} (function ($) {
1864	"use strict";
1865
1866	var _colorpicker_index = 0,
1867
1868		_container_popup = '<div class="ui-colorpicker ui-colorpicker-dialog ui-dialog ui-widget ui-widget-content ui-corner-all" style="display: none;"></div>',
1869		_container_inlineFrame = '<div class="ui-colorpicker ui-colorpicker-inline ui-dialog ui-widget ui-widget-content ui-corner-all"></div>',
1870		_container_inline = '<div class="ui-colorpicker ui-colorpicker-inline"></div>',
1871
1872		_intToHex = function (dec) {
1873			var result = Math.round(dec).toString(16);
1874			if (result.length === 1) {
1875				result = ('0' + result);
1876			}
1877			return result.toLowerCase();
1878		},
1879		
1880		_keycode = {
1881			isPrint: function(keycode) {
1882				return keycode == 32						// spacebar
1883					|| (keycode >= 48 && keycode <= 57)		// number keys
1884					|| (keycode >= 65 && keycode <= 90)		// letter keys
1885					|| (keycode >= 96 && keycode <= 111)	// numpad keys
1886					|| (keycode >= 186 && keycode < 192)	// ;=,-./` (in order)
1887					|| (keycode >= 219 && keycode < 222);	// [\]' (in order)
1888			},			
1889			isHex: function(keycode) {
1890				return (keycode >= 48 && keycode <= 57)		// number keys
1891					|| (keycode >= 65 && keycode <= 70);	// a-f
1892			}
1893		},
1894
1895		_layoutTable = function(layout, callback) {
1896			var bitmap,
1897				x, y,
1898				width, height,
1899				columns, rows,
1900				index,
1901				cell,
1902				html,
1903				w, h,
1904				colspan,
1905				walked;
1906
1907			layout.sort(function(a, b) {
1908				if (a.pos[1] === b.pos[1]) {
1909					return a.pos[0] - b.pos[0];
1910				}
1911				return a.pos[1] - b.pos[1];
1912			});
1913
1914			// Determine dimensions of the table
1915			width = 0;
1916			height = 0;
1917			$.each (layout, function(index, part) {
1918				width = Math.max(width, part.pos[0] + part.pos[2]);
1919				height = Math.max(height, part.pos[1] + part.pos[3]);
1920			});
1921
1922			// Initialize bitmap
1923			bitmap = [];
1924			for (x = 0; x < width; ++x) {
1925				bitmap.push([]);
1926			}
1927
1928			// Mark rows and columns which have layout assigned
1929			rows	= [];
1930			columns = [];
1931			$.each(layout, function(index, part) {
1932				// mark columns
1933				for (x = 0; x < part.pos[2]; x += 1) {
1934					columns[part.pos[0] + x] = true;
1935				}
1936				for (y = 0; y < part.pos[3]; y += 1) {
1937					rows[part.pos[1] + y] = true;
1938				}
1939			});
1940
1941			// Generate the table
1942			html = '';
1943			cell = layout[index = 0];
1944			for (y = 0; y < height; ++y) {
1945				html += '<tr>';
1946                x = 0;
1947                while (x < width) {
1948					if (typeof cell !== 'undefined' && x === cell.pos[0] && y === cell.pos[1]) {
1949						// Create a "real" cell
1950						html += callback(cell, x, y);
1951
1952						for (h = 0; h < cell.pos[3]; h +=1) {
1953							for (w = 0; w < cell.pos[2]; w +=1) {
1954								bitmap[x + w][y + h] = true;
1955							}
1956						}
1957
1958						x += cell.pos[2];
1959						cell = layout[++index];
1960					} else {
1961						// Fill in the gaps
1962						colspan = 0;
1963						walked = false;
1964
1965						while (x < width && bitmap[x][y] === undefined && (cell === undefined || y < cell.pos[1] || (y === cell.pos[1] && x < cell.pos[0]))) {
1966							if (columns[x] === true) {
1967								colspan += 1;
1968							}
1969							walked = true;
1970							x += 1;
1971						}
1972
1973						if (colspan > 0) {
1974							html += '<td colspan="'+colspan+'"></td>';
1975						} else if (!walked) {
1976							x += 1;
1977						}
1978					}
1979				}
1980				html += '</tr>';
1981			}
1982
1983			return '<table cellspacing="0" cellpadding="0" border="0"><tbody>' + html + '</tbody></table>';
1984		};
1985
1986	$.colorpicker = new function() {
1987		this.regional = {
1988			'':	{
1989				ok:				'OK',
1990				cancel:			'Cancel',
1991				none:			'None',
1992				button:			'Color',
1993				title:			'Pick a color',
1994				transparent:	'Transparent',
1995				hsvH:			'H',
1996				hsvS:			'S',
1997				hsvV:			'V',
1998				rgbR:			'R',
1999				rgbG:			'G',
2000				rgbB:			'B',
2001				labL:			'L',
2002				labA:			'a',
2003				labB:			'b',
2004				hslH:			'H',
2005				hslS:			'S',
2006				hslL:			'L',
2007				cmykC:			'C',
2008				cmykM:			'M',
2009				cmykY:			'Y',
2010				cmykK:			'K',
2011				alphaA:			'A'
2012			}
2013		};
2014
2015		this.swatchesNames = {
2016			'html':		'HTML'
2017		};
2018
2019		this.swatches = {
2020			'html':	[
2021				{name: 'black',					r: 0, g: 0, b: 0},
2022				{name: 'dimgray',				r: 0.4117647058823529, g: 0.4117647058823529, b: 0.4117647058823529},
2023				{name: 'gray',					r: 0.5019607843137255, g: 0.5019607843137255, b: 0.5019607843137255},
2024				{name: 'darkgray',				r: 0.6627450980392157, g: 0.6627450980392157, b: 0.6627450980392157},
2025				{name: 'silver',				r: 0.7529411764705882, g: 0.7529411764705882, b: 0.7529411764705882},
2026				{name: 'lightgrey',				r: 0.8274509803921568, g: 0.8274509803921568, b: 0.8274509803921568},
2027				{name: 'gainsboro',				r: 0.8627450980392157, g: 0.8627450980392157, b: 0.8627450980392157},
2028				{name: 'whitesmoke',			r: 0.9607843137254902, g: 0.9607843137254902, b: 0.9607843137254902},
2029				{name: 'white',					r: 1, g: 1, b: 1},
2030				{name: 'rosybrown',				r: 0.7372549019607844, g: 0.5607843137254902, b: 0.5607843137254902},
2031				{name: 'indianred',				r: 0.803921568627451, g: 0.3607843137254902, b: 0.3607843137254902},
2032				{name: 'brown',					r: 0.6470588235294118, g: 0.16470588235294117, b: 0.16470588235294117},
2033				{name: 'firebrick',				r: 0.6980392156862745, g: 0.13333333333333333, b: 0.13333333333333333},
2034				{name: 'lightcoral',			r: 0.9411764705882353, g: 0.5019607843137255, b: 0.5019607843137255},
2035				{name: 'maroon',				r: 0.5019607843137255, g: 0, b: 0},
2036				{name: 'darkred',				r: 0.5450980392156862, g: 0, b: 0},
2037				{name: 'red',					r: 1, g: 0, b: 0},
2038				{name: 'snow',					r: 1, g: 0.9803921568627451, b: 0.9803921568627451},
2039				{name: 'salmon',				r: 0.9803921568627451, g: 0.5019607843137255, b: 0.4470588235294118},
2040				{name: 'mistyrose',				r: 1, g: 0.8941176470588236, b: 0.8823529411764706},
2041				{name: 'tomato',				r: 1, g: 0.38823529411764707, b: 0.2784313725490196},
2042				{name: 'darksalmon',			r: 0.9137254901960784, g: 0.5882352941176471, b: 0.47843137254901963},
2043				{name: 'orangered',				r: 1, g: 0.27058823529411763, b: 0},
2044				{name: 'coral',					r: 1, g: 0.4980392156862745, b: 0.3137254901960784},
2045				{name: 'lightsalmon',			r: 1, g: 0.6274509803921569, b: 0.47843137254901963},
2046				{name: 'sienna',				r: 0.6274509803921569, g: 0.3215686274509804, b: 0.17647058823529413},
2047				{name: 'seashell',				r: 1, g: 0.9607843137254902, b: 0.9333333333333333},
2048				{name: 'chocolate',				r: 0.8235294117647058, g: 0.4117647058823529, b: 0.11764705882352941},
2049				{name: 'saddlebrown',			r: 0.5450980392156862, g: 0.27058823529411763, b: 0.07450980392156863},
2050				{name: 'sandybrown',			r: 0.9568627450980393, g: 0.6431372549019608, b: 0.3764705882352941},
2051				{name: 'peachpuff',				r: 1, g: 0.8549019607843137, b: 0.7254901960784313},
2052				{name: 'peru',					r: 0.803921568627451, g: 0.5215686274509804, b: 0.24705882352941178},
2053				{name: 'linen',					r: 0.9803921568627451, g: 0.9411764705882353, b: 0.9019607843137255},
2054				{name: 'darkorange',			r: 1, g: 0.5490196078431373, b: 0},
2055				{name: 'bisque',				r: 1, g: 0.8941176470588236, b: 0.7686274509803922},
2056				{name: 'burlywood',				r: 0.8705882352941177, g: 0.7215686274509804, b: 0.5294117647058824},
2057				{name: 'tan',					r: 0.8235294117647058, g: 0.7058823529411765, b: 0.5490196078431373},
2058				{name: 'antiquewhite',			r: 0.9803921568627451, g: 0.9215686274509803, b: 0.8431372549019608},
2059				{name: 'navajowhite',			r: 1, g: 0.8705882352941177, b: 0.6784313725490196},
2060				{name: 'blanchedalmond',		r: 1, g: 0.9215686274509803, b: 0.803921568627451},
2061				{name: 'papayawhip',			r: 1, g: 0.9372549019607843, b: 0.8352941176470589},
2062				{name: 'orange',				r: 1, g: 0.6470588235294118, b: 0},
2063				{name: 'moccasin',				r: 1, g: 0.8941176470588236, b: 0.7098039215686275},
2064				{name: 'wheat',					r: 0.9607843137254902, g: 0.8705882352941177, b: 0.7019607843137254},
2065				{name: 'oldlace',				r: 0.9921568627450981, g: 0.9607843137254902, b: 0.9019607843137255},
2066				{name: 'floralwhite',			r: 1, g: 0.9803921568627451, b: 0.9411764705882353},
2067				{name: 'goldenrod',				r: 0.8549019607843137, g: 0.6470588235294118, b: 0.12549019607843137},
2068				{name: 'darkgoldenrod',			r: 0.7215686274509804, g: 0.5254901960784314, b: 0.043137254901960784},
2069				{name: 'cornsilk',				r: 1, g: 0.9725490196078431, b: 0.8627450980392157},
2070				{name: 'gold',					r: 1, g: 0.8431372549019608, b: 0},
2071				{name: 'palegoldenrod',			r: 0.9333333333333333, g: 0.9098039215686274, b: 0.6666666666666666},
2072				{name: 'khaki',					r: 0.9411764705882353, g: 0.9019607843137255, b: 0.5490196078431373},
2073				{name: 'lemonchiffon',			r: 1, g: 0.9803921568627451, b: 0.803921568627451},
2074				{name: 'darkkhaki',				r: 0.7411764705882353, g: 0.7176470588235294, b: 0.4196078431372549},
2075				{name: 'beige',					r: 0.9607843137254902, g: 0.9607843137254902, b: 0.8627450980392157},
2076				{name: 'lightgoldenrodyellow',	r: 0.9803921568627451, g: 0.9803921568627451, b: 0.82352941176470
207658},
2077				{name: 'olive',					r: 0.5019607843137255, g: 0.5019607843137255, b: 0},
2078				{name: 'yellow',				r: 1, g: 1, b: 0},
2079				{name: 'lightyellow',			r: 1, g: 1, b: 0.8784313725490196},
2080				{name: 'ivory',					r: 1, g: 1, b: 0.9411764705882353},
2081				{name: 'olivedrab',				r: 0.4196078431372549, g: 0.5568627450980392, b: 0.13725490196078433},
2082				{name: 'yellowgreen',			r: 0.6039215686274509, g: 0.803921568627451, b: 0.19607843137254902},
2083				{name: 'darkolivegreen',		r: 0.3333333333333333, g: 0.4196078431372549, b: 0.1843137254901961},
2084				{name: 'greenyellow',			r: 0.6784313725490196, g: 1, b: 0.1843137254901961},
2085				{name: 'lawngreen',				r: 0.48627450980392156, g: 0.9882352941176471, b: 0},
2086				{name: 'chartreuse',			r: 0.4980392156862745, g: 1, b: 0},
2087				{name: 'darkseagreen',			r: 0.5607843137254902, g: 0.7372549019607844, b: 0.5607843137254902},
2088				{name: 'forestgreen',			r: 0.13333333333333333, g: 0.5450980392156862, b: 0.13333333333333333},
2089				{name: 'limegreen',				r: 0.19607843137254902, g: 0.803921568627451, b: 0.19607843137254902},
2090				{name: 'lightgreen',			r: 0.5647058823529412, g: 0.9333333333333333, b: 0.5647058823529412},
2091				{name: 'palegreen',				r: 0.596078431372549, g: 0.984313725490196, b: 0.596078431372549},
2092				{name: 'darkgreen',				r: 0, g: 0.39215686274509803, b: 0},
2093				{name: 'green',					r: 0, g: 0.5019607843137255, b: 0},
2094				{name: 'lime',					r: 0, g: 1, b: 0},
2095				{name: 'honeydew',				r: 0.9411764705882353, g: 1, b: 0.9411764705882353},
2096				{name: 'mediumseagreen',		r: 0.23529411764705882, g: 0.7019607843137254, b: 0.44313725490196076},
2097				{name: 'seagreen',				r: 0.1803921568627451, g: 0.5450980392156862, b: 0.3411764705882353},
2098				{name: 'springgreen',			r: 0, g: 1, b: 0.4980392156862745},
2099				{name: 'mintcream',				r: 0.9607843137254902, g: 1, b: 0.9803921568627451},
2100				{name: 'mediumspringgreen',		r: 0, g: 0.9803921568627451, b: 0.6039215686274509},
2101				{name: 'mediumaquamarine',		r: 0.4, g: 0.803921568627451, b: 0.6666666666666666},
2102				{name: 'aquamarine',			r: 0.4980392156862745, g: 1, b: 0.8313725490196079},
2103				{name: 'turquoise',				r: 0.25098039215686274, g: 0.8784313725490196, b: 0.8156862745098039},
2104				{name: 'lightseagreen',			r: 0.12549019607843137, g: 0.6980392156862745, b: 0.6666666666666666},
2105				{name: 'mediumturquoise',		r: 0.2823529411764706, g: 0.8196078431372549, b: 0.8},
2106				{name: 'darkslategray',			r: 0.1843137254901961, g: 0.30980392156862746, b: 0.30980392156862746},
2107				{name: 'paleturquoise',			r: 0.6862745098039216, g: 0.9333333333333333, b: 0.9333333333333333},
2108				{name: 'teal',					r: 0, g: 0.5019607843137255, b: 0.5019607843137255},
2109				{name: 'darkcyan',				r: 0, g: 0.5450980392156862, b: 0.5450980392156862},
2110				{name: 'darkturquoise',			r: 0, g: 0.807843137254902, b: 0.8196078431372549},
2111				{name: 'aqua',					r: 0, g: 1, b: 1},
2112				{name: 'cyan',					r: 0, g: 1, b: 1},
2113				{name: 'lightcyan',				r: 0.8784313725490196, g: 1, b: 1},
2114				{name: 'azure',					r: 0.9411764705882353, g: 1, b: 1},
2115				{name: 'cadetblue',				r: 0.37254901960784315, g: 0.6196078431372549, b: 0.6274509803921569},
2116				{name: 'powderblue',			r: 0.6901960784313725, g: 0.8784313725490196, b: 0.9019607843137255},
2117				{name: 'lightblue',				r: 0.6784313725490196, g: 0.8470588235294118, b: 0.9019607843137255},
2118				{name: 'deepskyblue',			r: 0, g: 0.7490196078431373, b: 1},
2119				{name: 'skyblue',				r: 0.5294117647058824, g: 0.807843137254902, b: 0.9215686274509803},
2120				{name: 'lightskyblue',			r: 0.5294117647058824, g: 0.807843137254902, b: 0.9803921568627451},
2121				{name: 'steelblue',				r: 0.27450980392156865, g: 0.5098039215686274, b: 0.7058823529411765},
2122				{name: 'aliceblue',				r: 0.9411764705882353, g: 0.9725490196078431, b: 1},
2123				{name: 'dodgerblue',			r: 0.11764705882352941, g: 0.5647058823529412, b: 1},
2124				{name: 'slategray',				r: 0.4392156862745098, g: 0.5019607843137255, b: 0.5647058823529412},
2125				{name: 'lightslategray',		r: 0.4666666666666667, g: 0.5333333333333333, b: 0.6},
2126				{name: 'lightsteelblue',		r: 0.6901960784313725, g: 0.7686274509803922, b: 0.8705882352941177},
2127				{name: 'cornflowerblue',		r: 0.39215686274509803, g: 0.5843137254901961, b: 0.92941176470
212758824},
2128				{name: 'royalblue',				r: 0.2549019607843137, g: 0.4117647058823529, b: 0.8823529411764706},
2129				{name: 'midnightblue',			r: 0.09803921568627451, g: 0.09803921568627451, b: 0.4392156862745098},
2130				{name: 'lavender',				r: 0.9019607843137255, g: 0.9019607843137255, b: 0.9803921568627451},
2131				{name: 'navy',					r: 0, g: 0, b: 0.5019607843137255},
2132				{name: 'darkblue',				r: 0, g: 0, b: 0.5450980392156862},
2133				{name: 'mediumblue',			r: 0, g: 0, b: 0.803921568627451},
2134				{name: 'blue',					r: 0, g: 0, b: 1},
2135				{name: 'ghostwhite',			r: 0.9725490196078431, g: 0.9725490196078431, b: 1},
2136				{name: 'darkslateblue',			r: 0.2823529411764706, g: 0.23921568627450981, b: 0.5450980392156862},
2137				{name: 'slateblue',				r: 0.41568627450980394, g: 0.35294117647058826, b: 0.803921568627451},
2138				{name: 'mediumslateblue',		r: 0.4823529411764706, g: 0.40784313725490196, b: 0.9333333333333333},
2139				{name: 'mediumpurple',			r: 0.5764705882352941, g: 0.4392156862745098, b: 0.8588235294117647},
2140				{name: 'blueviolet',			r: 0.5411764705882353, g: 0.16862745098039217, b: 0.8862745098039215},
2141				{name: 'indigo',				r: 0.29411764705882354, g: 0, b: 0.5098039215686274},
2142				{name: 'darkorchid',			r: 0.6, g: 0.19607843137254902, b: 0.8},
2143				{name: 'darkviolet',			r: 0.5803921568627451, g: 0, b: 0.8274509803921568},
2144				{name: 'mediumorchid',			r: 0.7294117647058823, g: 0.3333333333333333, b: 0.8274509803921568},
2145				{name: 'thistle',				r: 0.8470588235294118, g: 0.7490196078431373, b: 0.8470588235294118},
2146				{name: 'plum',					r: 0.8666666666666667, g: 0.6274509803921569, b: 0.8666666666666667},
2147				{name: 'violet',				r: 0.9333333333333333, g: 0.5098039215686274, b: 0.9333333333333333},
2148				{name: 'purple',				r: 0.5019607843137255, g: 0, b: 0.5019607843137255},
2149				{name: 'darkmagenta',			r: 0.5450980392156862, g: 0, b: 0.5450980392156862},
2150				{name: 'magenta',				r: 1, g: 0, b: 1},
2151				{name: 'fuchsia',				r: 1, g: 0, b: 1},
2152				{name: 'orchid',				r: 0.8549019607843137, g: 0.4392156862745098, b: 0.8392156862745098},
2153				{name: 'mediumvioletred',		r: 0.7803921568627451, g: 0.08235294117647059, b: 0.5215686274509804},
2154				{name: 'deeppink',				r: 1, g: 0.0784313725490196, b: 0.5764705882352941},
2155				{name: 'hotpink',				r: 1, g: 0.4117647058823529, b: 0.7058823529411765},
2156				{name: 'palevioletred',			r: 0.8588235294117647, g: 0.4392156862745098, b: 0.5764705882352941},
2157				{name: 'lavenderblush',			r: 1, g: 0.9411764705882353, b: 0.9607843137254902},
2158				{name: 'crimson',				r: 0.8627450980392157, g: 0.0784313725490196, b: 0.23529411764705882},
2159				{name: 'pink',					r: 1, g: 0.7529411764705882, b: 0.796078431372549},
2160				{name: 'lightpink',				r: 1, g: 0.7137254901960784, b: 0.7568627450980392}
2161			]
2162		};
2163
2164		this.writers = {
2165			'#HEX':		function(color, that) {
2166							return that._formatColor('#rxgxbx', color);
2167						}
2168		,	'#HEX3':	function(color, that) {
2169							var hex3 = $.colorpicker.writers.HEX3(color);
2170							return hex3 === false? false : '#'+hex3;
2171						}
2172		,	'HEX':		function(color, that) {
2173							return that._formatColor('rxgxbx', color);
2174						}
2175		,	'HEX3':		function(color, that) {
2176							var rgb = color.getRGB(),
2177								r = Math.round(rgb.r * 255),
2178								g = Math.round(rgb.g * 255),
2179								b = Math.round(rgb.b * 255);
2180
2181							if (((r >>> 4) === (r &= 0xf))
2182							 && ((g >>> 4) === (g &= 0xf))
2183							 && ((b >>> 4) === (b &= 0xf))) {
2184								return r.toString(16)+g.toString(16)+b.toString(16);
2185							}
2186							return false;
2187						}
2188		,	'#HEXA':	function(color, that) {
2189							return that._formatColor('#rxgxbxax', color);
2190						}						
2191		,	'#HEXA4':	function(color, that) {
2192							var hexa4 = $.colorpicker.writers.HEXA4(color, that);
2193							return hexa4 === false? false : '#'+hexa4;
2194						}						
2195		,	'HEXA':	function(color, that) {
2196							return that._formatColor('rxgxbxax', color);
2197						}		
2198		,	'HEXA4':		function(color, that) {
2199							var a = Math.round(color.getAlpha() * 255);
2200						
2201							if ((a >>> 4) === (a &= 0xf)) {
2202								return $.colorpicker.writers.HEX3(color, that)+a.toString(16);
2203							}
2204							return false;
2205						}						
2206		,	'RGB':		function(color, that) {
2207							return color.getAlpha() >= 1
2208									? that._formatColor('rgb(rd,gd,bd)', color)
2209									: false;
2210						}
2211		,	'RGBA':		function(color, that) {
2212							return that._formatColor('rgba(rd,gd,bd,af)', color);
2213						}
2214		,	'RGB%':		function(color, that) {
2215							return color.getAlpha() >= 1
2216									? that._formatColor('rgb(rp%,gp%,bp%)', color)
2217									: false;
2218						}
2219		,	'RGBA%':	function(color, that) {
2220							return that._formatColor('rgba(rp%,gp%,bp%,af)', color);
2221						}
2222		,	'HSL':		function(color, that) {
2223							return color.getAlpha() >= 1
2224									? that._formatColor('hsl(hd,sd,vd)', color)
2225									: false;
2226						}
2227		,	'HSLA':		function(color, that) {
2228							return that._formatColor('hsla(hd,sd,vd,af)', color);
2229						}
2230		,	'HSL%':		function(color, that) {
2231							return color.getAlpha() >= 1
2232									? that._formatColor('hsl(hp%,sp%,vp%)', color)
2233									: false;
2234						}
2235		,	'HSLA%':	function(color, that) {
2236							return that._formatColor('hsla(hp%,sp%,vp%,af)', color);
2237						}
2238		,	'NAME':		function(color, that) {
2239							return that._closestName(color);
2240						}
2241		,	'EXACT':	function(color, that) {
2242							return that._exactName(color);
2243						}
2244		};
2245
2246		this.parsers = {
2247			'':			function(color) {
2248				            if (color === '') {
2249								return new $.colorpicker.Color();
2250							}
2251						}
2252		,	'NAME':		function(color, that) {
2253							var c = that._getSwatch($.trim(color));
2254							if (c) {
2255								return new $.colorpicker.Color(c.r, c.g, c.b);
2256							}
2257						}
2258		,	'RGBA':		function(color) {
2259							var m = /^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)$/.exec(color);
2260							if (m) {
2261								return new $.colorpicker.Color(
2262									m[1] / 255,
2263									m[2] / 255,
2264									m[3] / 255,
2265									parseFloat(m[4])
2266								);
2267							}
2268						}
2269		,	'RGBA%':	function(color) {
2270							var m = /^rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)$/.exec(color);
2271							if (m) {
2272								return new $.colorpicker.Color(
2273									m[1] / 100,
2274									m[2] / 100,
2275									m[3] / 100,
2276									m[4] / 100
2277								);
2278							}
2279						}
2280		,	'HSLA':		function(color) {
2281							var m = /^hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)$/.exec(color);
2282							if (m) {
2283								return (new $.colorpicker.Color()).setHSL(
2284									m[1] / 255,
2285									m[2] / 255,
2286									m[3] / 255).setAlpha(parseFloat(m[4]));
2287							}
2288						}
2289		,	'HSLA%':	function(color) {
2290							var m = /^hsla?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)$/.exec(color);
2291							if (m) {
2292								return (new $.colorpicker.Color()).setHSL(
2293									m[1] / 100,
2294									m[2] / 100,
2295									m[3] / 100).setAlpha(m[4] / 100);
2296							}
2297						}
2298		,	'#HEX':		function(color) {
2299							var m = /^#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})$/.exec(color);
2300							if (m) {
2301								return new $.colorpicker.Color(
2302									parseInt(m[1], 16) / 255,
2303									parseInt(m[2], 16) / 255,
2304									parseInt(m[3], 16) / 255
2305								);
2306							}
2307						}
2308		,	'#HEX3':	function(color) {
2309							var m = /^#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])$/.exec(color);
2310							if (m) {
2311								return new $.colorpicker.Color(
2312								   parseInt(String(m[1]) + m[1], 16) / 255,
2313								   parseInt(String(m[2]) + m[2], 16) / 255,
2314								   parseInt(String(m[3]) + m[3], 16) / 255
2315								);
2316							}
2317						}
2318		,	'HEX':		function(color) {
2319							var m = /^([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})$/.exec(color);
2320							if (m) {
2321								return new $.colorpicker.Color(
2322									parseInt(m[1], 16) / 255,
2323									parseInt(m[2], 16) / 255,
2324									parseInt(m[3], 16) / 255
2325								);
2326							}
2327						}
2328		,	'HEX3':		function(color) {
2329							var m = /^([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])$/.exec(color);
2330							if (m) {
2331								return new $.colorpicker.Color(
2332								   parseInt(String(m[1]) + m[1], 16) / 255,
2333								   parseInt(String(m[2]) + m[2], 16) / 255,
2334								   parseInt(String(m[3]) + m[3], 16) / 255
2335								);
2336							}
2337						}
2338		,	'#HEXA':	function(color) {
2339							var m = /^#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})$/.exec(color);
2340							if (m) {
2341								return new $.colorpicker.Color(
2342									parseInt(m[1], 16) / 255,
2343									parseInt(m[2], 16) / 255,
2344									parseInt(m[3], 16) / 255,
2345									parseInt(m[4], 16) / 255
2346								);
2347							}
2348						}
2349		,	'#HEXA4':	function(color) {
2350							var m = /^#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])$/.exec(color);
2351							if (m) {
2352								return new $.colorpicker.Color(
2353								   parseInt(String(m[1]) + m[1], 16) / 255,
2354								   parseInt(String(m[2]) + m[2], 16) / 255,
2355								   parseInt(String(m[3]) + m[3], 16) / 255,
2356								   parseInt(String(m[4]) + m[4], 16) / 255
2357								);
2358							}
2359						}
2360		,	'HEXA':		function(color) {
2361							var m = /^([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})$/.exec(color);
2362							if (m) {
2363								return new $.colorpicker.Color(
2364									parseInt(m[1], 16) / 255,
2365									parseInt(m[2], 16) / 255,
2366									parseInt(m[3], 16) / 255,
2367									parseInt(m[4], 16) / 255
2368								);
2369							}
2370						}
2371		,	'HEXA4':	function(color) {
2372							var m = /^([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])$/.exec(color);
2373							if (m) {
2374								return new $.colorpicker.Color(
2375								   parseInt(String(m[1]) + m[1], 16) / 255,
2376								   parseInt(String(m[2]) + m[2], 16) / 255,
2377								   parseInt(String(m[3]) + m[3], 16) / 255,
2378								   parseInt(String(m[4]) + m[4], 16) / 255
2379								);
2380							}
2381						}
2382		};
2383
2384		this.partslists = {
2385			'full':			['header', 'map', 'bar', 'hex', 'hsv', 'rgb', 'alpha', 'lab', 'cmyk', 'preview', 'swatches', 'footer'],
2386			'popup':		['map', 'bar', 'hex', 'hsv', 'rgb', 'alpha', 'preview', 'footer'],
2387			'draggable':	['header', 'map', 'bar', 'hex', 'hsv', 'rgb', 'alpha', 'preview', 'footer'],
2388			'inline':		['map', 'bar', 'hex', 'hsv', 'rgb', 'alpha', 'preview']
2389		};
2390
2391		this.limits = {
2392			'websafe':		function(color) {
2393								color.limit(6);
2394							},
2395			'nibble':		function(color) {
2396								color.limit(16);
2397							},
2398			'binary':		function(color) {
2399								color.limit(2);
2400							},
2401			'name':			function(color, that) {
2402								var swatch = that._getSwatch(that._closestName(color));
2403								color.setRGB(swatch.r, swatch.g, swatch.b);
2404							}
2405		};
2406
2407		this.parts = {
2408			header: function (inst) {
2409				var that	= this,
2410					part	= null,
2411					_html	= function() {
2412						var title = inst.options.title || inst._getRegional('title'),
2413							html = '<span class="ui-dialog-title">' + title + '</span>';
2414
2415						if (!inst.inline && inst.options.showCloseButton) {
2416							html += '<a href="#" class="ui-dialog-titlebar-close ui-corner-all" role="button">'
2417								+ '<span class="ui-icon ui-icon-closethick">close</span></a>';
2418						}
2419
2420						return '<div class="ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix">' + html + '</div>';
2421					},
2422					_onclick = function(event) {
2423						event.preventDefault();
2424						inst.close(inst.options.revert);
2425					};
2426
2427				this.init = function() {
2428					part = $(_html()).prependTo(inst.dialog);
2429
2430					var close = $('.ui-dialog-titlebar-close', part);
2431					inst._hoverable(close);
2432					inst._focusable(close);
2433					close.bind('click', _onclick);
2434
2435					if (!inst.inline && inst.options.draggable) {
2436						var draggableOptions = {
2437							handle: part,
2438						}
2439						if (inst.options.containment) {
2440							draggableOptions.containment = inst.options.containment;
2441						}
2442						inst.dialog.draggable(draggableOptions);
2443					}
2444				};
2445
2446				this.disable = function (disable) {
2447					$('.ui-dialog-titlebar-close', part)[disable ? 'unbind' : 'bind']('click', _onclick);
2448				};
2449			},
2450
2451			map: function (inst) {
2452				var that	= this,
2453					part	= null,
2454					pointer, width, height, layers = {},
2455					_mousedown, _mouseup, _mousemove, _keydown, _html;
2456
2457				_mousedown = function (event) {
2458					if (!inst.opened) {
2459						return;
2460					}
2461
2462					var offset	= layers.p.offset(),
2463						x		= event.pageX - offset.left,
2464						y		= event.pageY - offset.top;
2465
2466					if (x >= 0 && x < width && y >= 0 && y < height) {
2467						event.stopImmediatePropagation();
2468						event.preventDefault();
2469						part.unbind('mousedown', _mousedown).focus();
2470						$(document).bind('mouseup', _mouseup);
2471						$(document).bind('mousemove', _mousemove);
2472						_mousemove(event);
2473					}
2474				};
2475
2476				_mouseup = function (event) {
2477					event.stopImmediatePropagation();
2478					event.preventDefault();
2479					$(document).unbind('mouseup', _mouseup);
2480					$(document).unbind('mousemove', _mousemove);
2481					part.bind('mousedown', _mousedown);
2482					
2483					inst._callback('stop');
2484				};
2485
2486				_mousemove = function (event) {
2487					event.stopImmediatePropagation();
2488					event.preventDefault();
2489
2490					if (event.pageX === that.x && event.pageY === that.y) {
2491						return;
2492					}
2493					that.x = event.pageX;
2494					that.y = event.pageY;
2495
2496					var offset	= layers.p.offset(),
2497						x		= event.pageX - offset.left,
2498						y		= event.pageY - offset.top;
2499
2500					x = Math.max(0, Math.min(x / width, 1));
2501					y = Math.max(0, Math.min(y / height, 1));
2502
2503					// interpret values
2504					switch (inst.mode) {
2505						case 'h':
2506							inst.color.setHSV(null, x, 1 - y);
2507							break;
2508
2509						case 's':
2510						case 'a':
2511							inst.color.setHSV(x, null, 1 - y);
2512							break;
2513
2514						case 'v':
2515							inst.color.setHSV(x, 1 - y, null);
2516							break;
2517
2518						case 'r':
2519							inst.color.setRGB(null, 1 - y, x);
2520							break;
2521
2522						case 'g':
2523							inst.color.setRGB(1 - y, null, x);
2524							break;
2525
2526						case 'b':
2527							inst.color.setRGB(x, 1 - y, null);
2528							break;
2529					}
2530
2531					inst._change(false);
2532				};
2533
2534				_keydown = function(event) {
2535					var x_channel_map = {
2536							'h': 's',
2537							's': 'h',
2538							'v': 'h',
2539							'r': 'b',
2540							'g': 'b',
2541							'b': 'r',
2542							'a': 'h'
2543						},
2544						x_change = {
2545							37: -1,
2546							39: 1,
2547						},
2548						y_channel_map = {
2549							'h': 'v',
2550							's': 'v',
2551							'v': 's',
2552							'r': 'g',
2553							'g': 'r',
2554							'b': 'g',
2555							'a': 'v'
2556						},
2557						y_change = {
2558							38: 1,
2559							40: -1
2560						},
2561						set = {
2562							35: 0,
2563							36: 1
2564						},
2565						change, value;
2566
2567						if (typeof x_change[event.which] !== 'undefined') {
2568							value = inst.color.getChannel(x_channel_map[inst.mode]) * width;
2569							change = x_change[event.which];
2570							if (event.shiftKey) {
2571								change *= 10;
2572							} else if (event.ctrlKey) {
2573								change *= width;
2574							}
2575							inst.color.setChannel(x_channel_map[inst.mode], (value + change) / width);
2576							inst._change(false);
2577						} else if (typeof y_change[event.which] !== 'undefined') {
2578							value = inst.color.getChannel(y_channel_map[inst.mode]) * height;
2579							change = y_change[event.which];
2580							if (event.shiftKey) {
2581								change *= 10;
2582							} else if (event.ctrlKey) {
2583								change *= height;
2584							}
2585							inst.color.setChannel(y_channel_map[inst.mode], (value + change) / height);
2586							inst._change(false);
2587						} else if (typeof set[event.which] !== 'undefined') {
2588							inst.color.setChannel(x_channel_map[inst.mode], 1 - set[event.which]);
2589							inst.color.setChannel(y_channel_map[inst.mode], set[event.which]);
2590							inst._change(false);							
2591						}					
2592				};
2593
2594				_html = function () {
2595					var html = '<div class="ui-colorpicker-map ui-colorpicker-map-'+(inst.options.part.map.size || 256)+' ui-colorpicker-border" tabindex="0">'
2596							+ '<span class="ui-colorpicker-map-layer-1">&nbsp;</span>'
2597							+ '<span class="ui-colorpicker-map-layer-2">&nbsp;</span>'
2598							+ (inst.options.alpha ? '<span class="ui-colorpicker-map-layer-alpha">&nbsp;</span>' : '')
2599							+ '<span class="ui-colorpicker-map-layer-pointer"><span class="ui-colorpicker-map-pointer"></span></span></div>';
2600					return html;
2601				};
2602
2603				this.init = function () {
2604					part = $(_html()).appendTo($('.ui-colorpicker-map-container', inst.dialog));
2605
2606					part.bind('mousedown', _mousedown);
2607					part.bind('keydown', _keydown);
2608					
2609					// cache					
2610					layers[1]	= $('.ui-colorpicker-map-layer-1', part);
2611					layers[2]	= $('.ui-colorpicker-map-layer-2', part);
2612					layers.a	= $('.ui-colorpicker-map-layer-alpha', part);
2613					layers.p	= $('.ui-colorpicker-map-layer-pointer', part);
2614					width		= layers.p.width();
2615					height		= layers.p.height();
2616					
2617					pointer		= $('.ui-colorpicker-map-pointer', part);
2618				};
2619
2620				this.update = function () {
2621					var step = ((inst.options.part.map.size || 256) * 65 / 64);
2622
2623					switch (inst.mode) {
2624						case 'h':
2625							layers[1].css({'background-position': '0 0', 'opacity': ''}).show();
2626							layers[2].hide();
2627							break;
2628
2629						case 's':
2630						case 'a':
2631							layers[1].css({'background-position': '0 ' + (-step) + 'px', 'opacity': ''}).show();
2632							layers[2].css({'background-position': '0 ' + (-step * 2) + 'px', 'opacity': ''}).show();
2633							break;
2634
2635						case 'v':
2636							part.css('background-color', 'black');
2637							layers[1].css({'background-position': '0 ' + (-step * 3) + 'px', 'opacity': ''}).show();
2638							layers[2].hide();
2639							break;
2640
2641						case 'r':
2642							layers[1].css({'background-position': '0 ' + (-step * 4) + 'px', 'opacity': ''}).show();
2643							layers[2].css({'background-position': '0 ' + (-step * 5) + 'px', 'opacity': ''}).show();
2644							break;
2645
2646						case 'g':
2647							layers[1].css({'background-position': '0 ' + (-step * 6) + 'px', 'opacity': ''}).show();
2648							layers[2].css({'background-position': '0 ' + (-step * 7) + 'px', 'opacity': ''}).show();
2649							break;
2650
2651						case 'b':
2652							layers[1].css({'background-position': '0 ' + (-step * 8) + 'px', 'opacity': ''}).show();
2653							layers[2].css({'background-position': '0 ' + (-step * 9) + 'px', 'opacity': ''}).show();
2654							break;
2655					}
2656					
2657					that.repaint();
2658				};
2659
2660				this.repaint = function () {
2661					var x = 0,
2662						y = 0;
2663
2664					switch (inst.mode) {
2665						case 'h':
2666							var hsv = inst.color.getHSV();
2667							x = hsv.s * width;
2668							y = (1 - hsv.v) * width;
2669							part.css('background-color', inst.color.copy().setHSV(null, 1, 1).toCSS());
2670							break;
2671
2672						case 's':
2673						case 'a':
2674							var hsv = inst.color.getHSV();
2675							x = hsv.h * width;
2676							y = (1 - hsv.v) * width;
2677							layers[2].css('opacity', 1 - hsv.s);
2678							break;
2679
2680						case 'v':
2681							var hsv = inst.color.getHSV();
2682							x = hsv.h * width;
2683							y = (1 - hsv.s) * width;
2684							layers[1].css('opacity', hsv.v);
2685							break;
2686
2687						case 'r':
2688							var rgb = inst.color.getRGB()
2689							x = rgb.b * width;
2690							y = (1 - rgb.g) * width;
2691							layers[2].css('opacity', rgb.r);
2692							break;
2693
2694						case 'g':
2695							var rgb = inst.color.getRGB();
2696							x = rgb.b * width;
2697							y = (1 - rgb.r) * width;
2698							layers[2].css('opacity', rgb.g);
2699							break;
2700
2701						case 'b':
2702							var rgb = inst.color.getRGB()
2703							x = rgb.r * width;
2704							y = (1 - rgb.g) * width;
2705							layers[2].css('opacity', rgb.b);
2706							break;
2707					}
2708
2709					if (inst.options.alpha) {
2710						layers.a.css('opacity', 1 - inst.color.getAlpha());
2711					}
2712
2713					pointer.css({
2714						'left': x - 7,
2715						'top': y - 7
2716					});
2717				};
2718
2719				this.disable = function (disable) {
2720					part[disable ? 'unbind' : 'bind']('mousedown', _mousedown);
2721					part[disable ? 'unbind' : 'bind']('keydown', _keydown);
2722				};
2723			},
2724
2725			bar: function (inst) {
2726				var that		= this,
2727					part		= null,
2728					pointer, width, height, layers = {},
2729					_mousedown, _mouseup, _mousemove, _keydown, _html;
2730
2731				_mousedown = function (event) {
2732					if (!inst.opened) {
2733						return;
2734					}
2735
2736					var offset	= layers.p.offset(),
2737						x		= event.pageX - offset.left,
2738						y		= event.pageY - offset.top;
2739
2740					if (x >= 0 && x < width && y >= 0 && y < height) {
2741						event.stopImmediatePropagation();
2742						event.preventDefault();
2743						part.unbind('mousedown', _mousedown).focus();
2744						$(document).bind('mouseup', _mouseup);
2745						$(document).bind('mousemove', _mousemove);
2746						_mousemove(event);
2747					}
2748				};
2749
2750				_mouseup = function (event) {
2751					event.stopImmediatePropagation();
2752					event.preventDefault();
2753					$(document).unbind('mouseup', _mouseup);
2754					$(document).unbind('mousemove', _mousemove);
2755					part.bind('mousedown', _mousedown);
2756					
2757					inst._callback('stop');					
2758				};
2759
2760				_mousemove = function (event) {
2761					event.stopImmediatePropagation();
2762					event.preventDefault();
2763
2764					if (event.pageY === that.y) {
2765						return;
2766					}
2767					that.y = event.pageY;
2768
2769					var offset  = layers.p.offset(),
2770						y		= event.pageY - offset.top;
2771
2772					y = Math.max(0, Math.min(y / height, 1));
2773
2774					// interpret values
2775					switch (inst.mode) {
2776						case 'h':
2777							inst.color.setHSV(1 - y, null, null);
2778							break;
2779
2780						case 's':
2781							inst.color.setHSV(null, 1 - y, null);
2782							break;
2783
2784						case 'v':
2785							inst.color.setHSV(null, null, 1 - y);
2786							break;
2787
2788						case 'r':
2789							inst.color.setRGB(1 - y, null, null);
2790							break;
2791
2792						case 'g':
2793							inst.color.setRGB(null, 1 - y, null);
2794							break;
2795
2796						case 'b':
2797							inst.color.setRGB(null, null, 1 - y);
2798							break;
2799
2800						case 'a':
2801							inst.color.setAlpha(1 - y);
2802							break;
2803					}
2804
2805					inst._change(false);
2806				};
2807				
2808				_keydown = function(event) {
2809					var change = {
2810							38: 1,
2811							40: -1,
2812							33: 10,
2813							34: -10
2814						},
2815						set = {
2816							35: 0,
2817							36: 1
2818						},
2819						change, value;
2820
2821					if (typeof change[event.which] !== 'undefined') {
2822						value = inst.color.getChannel(inst.mode) * height;
2823						change = change[event.which];
2824						if (event.shiftKey) {
2825							change *= 10;
2826						} else if (event.ctrlKey) {
2827							change *= height;
2828						}
2829						inst.color.setChannel(inst.mode, (value + change) / height);
2830						inst._change(false);
2831					} else if (typeof set[event.which] !== 'undefined') {
2832						inst.color.setChannel(inst.mode, set[event.which]);
2833						inst._change(false);							
2834					}					
2835				};
2836
2837				_html = function () {
2838					var html = '<div class="ui-colorpicker-bar ui-colorpicker-bar-'+(inst.options.part.bar.size || 256)+'  ui-colorpicker-border" tabindex="0">'
2839							+ '<span class="ui-colorpicker-bar-layer-1">&nbsp;</span>'
2840							+ '<span class="ui-colorpicker-bar-layer-2">&nbsp;</span>'
2841							+ '<span class="ui-colorpicker-bar-layer-3">&nbsp;</span>'
2842							+ '<span class="ui-colorpicker-bar-layer-4">&nbsp;</span>';
2843
2844					if (inst.options.alpha) {
2845						html += '<span class="ui-colorpicker-bar-layer-alpha">&nbsp;</span>'
2846							+ '<span class="ui-colorpicker-bar-layer-alphabar">&nbsp;</span>';
2847					}
2848
2849					html += '<span class="ui-colorpicker-bar-layer-pointer"><span class="ui-colorpicker-bar-pointer"></span></span></div>';
2850
2851					return html;
2852				};
2853
2854				this.init = function () {
2855					part = $(_html()).appendTo($('.ui-colorpicker-bar-container', inst.dialog));
2856
2857					part.bind('mousedown', _mousedown);
2858					part.bind('keydown', _keydown);
2859					
2860					// cache				
2861					layers[1]	= $('.ui-colorpicker-bar-layer-1', part);
2862					layers[2]	= $('.ui-colorpicker-bar-layer-2', part);
2863					layers[3]	= $('.ui-colorpicker-bar-layer-3', part);
2864					layers[4]	= $('.ui-colorpicker-bar-layer-4', part);
2865					layers.a	= $('.ui-colorpicker-bar-layer-alpha', part);
2866					layers.ab	= $('.ui-colorpicker-bar-layer-alphabar', part);
2867					layers.p	= $('.ui-colorpicker-bar-layer-pointer', part);
2868					width		= layers.p.width();
2869					height		= layers.p.height();		
2870					
2871					pointer		= $('.ui-colorpicker-bar-pointer', part);
2872				};
2873				
2874				this.update = function () {
2875					var step = ((inst.options.part.bar.size || 256) * 65 / 64);
2876
2877					switch (inst.mode) {
2878						case 'h':
2879						case 's':
2880						case 'v':
2881						case 'r':
2882						case 'g':
2883						case 'b':
2884							layers.a.show();
2885							layers.ab.hide();
2886							break;
2887
2888						case 'a':
2889							layers.a.hide();
2890							layers.ab.show();
2891							break;
2892					}
2893
2894					switch (inst.mode) {
2895						case 'h':
2896							layers[1].css({'background-position': '0 0', 'opacity': ''}).show();
2897							layers[2].hide();
2898							layers[3].hide();
2899							layers[4].hide();
2900							break;
2901
2902						case 's':
2903							layers[1].css({'background-position': '0 ' + (-step) + 'px', 'opacity': ''}).show();
2904							layers[2].css({'background-position': '0 ' + (-step * 2) + 'px', 'opacity': ''}).show();
2905							layers[3].hide();
2906							layers[4].hide();
2907							break;
2908
2909						case 'v':
2910							layers[1].css({'background-position': '0 ' + (-step * 2) + 'px', 'opacity': ''}).show();
2911							layers[2].hide();
2912							layers[3].hide();
2913							layers[4].hide();
2914							break;
2915
2916						case 'r':
2917							layers[1].css({'background-position': '0 ' + (-step * 6) + 'px', 'opacity': ''}).show();
2918							layers[2].css({'background-position': '0 ' + (-step * 5) + 'px', 'opacity': ''}).show();
2919							layers[3].css({'background-position': '0 ' + (-step * 3) + 'px', 'opacity': ''}).show();
2920							layers[4].css({'background-position': '0 ' + (-step * 4) + 'px', 'opacity': ''}).show();
2921							break;
2922
2923						case 'g':
2924							layers[1].css({'background-position': '0 ' + (-step * 10) + 'px', 'opacity': ''}).show();
2925							layers[2].css({'background-position': '0 ' + (-step * 9) + 'px', 'opacity': ''}).show();
2926							layers[3].css({'background-position': '0 ' + (-step * 7) + 'px', 'opacity': ''}).show();
2927							layers[4].css({'background-position': '0 ' + (-step * 8) + 'px', 'opacity': ''}).show();
2928							break;
2929
2930						case 'b':
2931							layers[1].css({'background-position': '0 ' + (-step * 14) + 'px', 'opacity': ''}).show();
2932							layers[2].css({'background-position': '0 ' + (-step * 13) + 'px', 'opacity': ''}).show();
2933							layers[3].css({'background-position': '0 ' + (-step * 11) + 'px', 'opacity': ''}).show();
2934							layers[4].css({'background-position': '0 ' + (-step * 12) + 'px', 'opacity': ''}).show();
2935							break;
2936
2937						case 'a':
2938							layers[1].hide();
2939							layers[2].hide();
2940							layers[3].hide();
2941							layers[4].hide();
2942							break;
2943					}
2944					
2945					that.repaint();
2946				};
2947
2948				this.repaint = function () {
2949					var y = 0;
2950
2951					switch (inst.mode) {
2952						case 'h':
2953							y = (1 - inst.color.getHSV().h) * height;
2954							break;
2955
2956						case 's':
2957							var hsv = inst.color.getHSV();
2958							y = (1 - hsv.s) * height;
2959							layers[2].css('opacity', 1 - hsv.v);
2960							part.css('background-color', inst.color.copy().setHSV(null, 1, null).toCSS());
2961							break;
2962
2963						case 'v':
2964							y = (1 - inst.color.getHSV().v) * height;
2965							part.css('background-color', inst.color.copy().setHSV(null, null, 1).toCSS());
2966							break;
2967
2968						case 'r':
2969							var rgb = inst.color.getRGB();
2970							y = (1 - rgb.r) * height;
2971							layers[2].css('opacity', Math.max(0, (rgb.b - rgb.g)));
2972							layers[3].css('opacity', Math.max(0, (rgb.g - rgb.b)));
2973							layers[4].css('opacity', Math.min(rgb.b, rgb.g));
2974							break;
2975
2976						case 'g':
2977							var rgb = inst.color.getRGB();
2978							y = (1 - rgb.g) * height;
2979							layers[2].css('opacity', Math.max(0, (rgb.b - rgb.r)));
2980							layers[3].css('opacity', Math.max(0, (rgb.r - rgb.b)));
2981							layers[4].css('opacity', Math.min(rgb.r, rgb.b));
2982							break;
2983
2984						case 'b':
2985							var rgb = inst.color.getRGB();
2986							y = (1 - rgb.b) * height;
2987							layers[2].css('opacity', Math.max(0, (rgb.r - rgb.g)));
2988							layers[3].css('opacity', Math.max(0, (rgb.g - rgb.r)));
2989							layers[4].css('opacity', Math.min(rgb.r, rgb.g));
2990							break;
2991
2992						case 'a':
2993							y = (1 - inst.color.getAlpha()) * height;
2994							part.css('background-color', inst.color.copy().toCSS());
2995							break;
2996					}
2997
2998					if (inst.mode !== 'a') {
2999						layers.a.css('opacity', 1 - inst.color.getAlpha());
3000					}
3001
3002					pointer.css('top', y - 3);
3003				};
3004
3005				this.disable = function (disable) {
3006					part[disable ? 'unbind' : 'bind']('mousedown', _mousedown);
3007					part[disable ? 'unbind' : 'bind']('keydown', _keydown);
3008				};
3009			},
3010
3011			preview: function (inst) {
3012				var that = this,
3013					part = null,
3014					both,
3015					initial, initial_alpha,
3016					current, current_alpha,
3017					_html,
3018					onclick = function () {
3019						inst.color = inst.currentColor.copy();
3020						inst._change();
3021					};
3022
3023				_html = function () {
3024					return '<div class="ui-colorpicker-preview ui-colorpicker-border">'
3025						+ '<div class="ui-colorpicker-preview-initial"><div class="ui-colorpicker-preview-initial-alpha"></div></div>'
3026						+ '<div class="ui-colorpicker-preview-current"><div class="ui-colorpicker-preview-current-alpha"></div></div>'
3027						+ '</div>';
3028				};
3029
3030				this.init = function () {
3031					part = $(_html()).appendTo($('.ui-colorpicker-preview-container', inst.dialog));
3032
3033					$('.ui-colorpicker-preview-initial', part).bind('click', onclick);
3034
3035					// cache
3036					initial			= $('.ui-colorpicker-preview-initial', part);
3037					initial_alpha	= $('.ui-colorpicker-preview-initial-alpha', part);
3038					current			= $('.ui-colorpicker-preview-current', part);
3039					current_alpha	= $('.ui-colorpicker-preview-current-alpha', part);
3040					both			= $('.ui-colorpicker-preview-initial-alpha, .ui-colorpicker-preview-current-alpha', part);
3041				};
3042
3043				this.update = function () {
3044					both[inst.options.alpha ? 'show' : 'hide']();
3045
3046					this.repaint();
3047				};
3048
3049				this.repaint = function () {
3050					initial.css('background-color', inst.currentColor.set ? inst.currentColor.toCSS() : '').attr('title', inst.currentColor.set ? inst.currentColor.toCSS() : '');
3051					initial_alpha.css('opacity', 1 - inst.currentColor.getAlpha());
3052					current.css('background-color', inst.color.set ? inst.color.toCSS() : '').attr('title', inst.color.set ? inst.color.toCSS() : '');
3053					current_alpha.css('opacity', 1 - inst.color.getAlpha());
3054				};
3055
3056				this.disable = function (disable) {
3057					$('.ui-colorpicker-preview-initial', part)[disable ? 'unbind' : 'bind']('click', onclick);
3058				};
3059			},
3060
3061			hsv: function (inst) {
3062				var that = this,
3063					part = null,
3064					inputs = {},
3065					_html;
3066
3067				_html = function () {
3068					var html = '';
3069
3070					if (inst.options.hsv) {
3071						html +=	'<div class="ui-colorpicker-hsv-h"><input class="ui-colorpicker-mode" type="radio" value="h"/><label>' + inst._getRegional('hsvH') + '</label><input class="ui-colorpicker-number" type="number" min="0" max="360" size="10"/><span class="ui-colorpicker-unit">&deg;</span></div>'
3072							+ '<div class="ui-colorpicker-hsv-s"><input class="ui-colorpicker-mode" type="radio" value="s"/><label>' + inst._getRegional('hsvS') + '</label><input class="ui-colorpicker-number" type="number" min="0" max="100" size="10"/><span class="ui-colorpicker-unit">%</span></div>'
3073							+ '<div class="ui-colorpicker-hsv-v"><input class="ui-colorpicker-mode" type="radio" value="v"/><label>' + inst._getRegional('hsvV') + '</label><input class="ui-colorpicker-number" type="number" min="0" max="100" size="10"/><span class="ui-colorpicker-unit">%</span></div>';
3074					}
3075
3076					return '<div class="ui-colorpicker-hsv">' + html + '</div>';
3077				};
3078
3079				this.init = function () {
3080					part = $(_html()).appendTo($('.ui-colorpicker-hsv-container', inst.dialog));
3081
3082					$('.ui-colorpicker-mode', part).click(function () {
3083						inst.mode = $(this).val();
3084						inst._updateAllParts();
3085					});
3086					
3087					inputs.h = $('.ui-colorpicker-hsv-h .ui-colorpicker-number', part);
3088					inputs.s = $('.ui-colorpicker-hsv-s .ui-colorpicker-number', part);
3089					inputs.v = $('.ui-colorpicker-hsv-v .ui-colorpicker-number', part);
3090
3091					$('.ui-colorpicker-number', part).bind('change keyup', function () {
3092						inst.color.setHSV(
3093							inputs.h.val() / 360,
3094							inputs.s.val() / 100,
3095							inputs.v.val() / 100
3096						);
3097						inst._change();
3098					});
3099				};
3100
3101				this.repaint = function () {
3102					var hsv = inst.color.getHSV();
3103					inputs.h.val(Math.round(hsv.h * 360));
3104					inputs.s.val(Math.round(hsv.s * 100));
3105					inputs.v.val(Math.round(hsv.v * 100));
3106				};
3107
3108				this.update = function () {
3109					$('.ui-colorpicker-mode', part).each(function () {
3110						var $this = $(this);
3111						$this.attr('checked', $this.val() === inst.mode);
3112					});
3113					this.repaint();
3114				};
3115
3116				this.disable = function (disable) {
3117					$(':input', part).prop('disabled', disable);
3118				};
3119			},
3120
3121			rgb: function (inst) {
3122				var that = this,
3123					part = null,
3124					inputs = {},
3125					_html;
3126
3127				_html = function () {
3128					var html = '';
3129
3130					if (inst.options.rgb) {
3131						html += '<div class="ui-colorpicker-rgb-r"><input class="ui-colorpicker-mode" type="radio" value="r"/><label>' + inst._getRegional('rgbR') + '</label><input class="ui-colorpicker-number" type="number" min="0" max="255"/></div>'
3132							+ '<div class="ui-colorpicker-rgb-g"><input class="ui-colorpicker-mode" type="radio" value="g"/><label>' + inst._getRegional('rgbG') + '</label><input class="ui-colorpicker-number" type="number" min="0" max="255"/></div>'
3133							+ '<div class="ui-colorpicker-rgb-b"><input class="ui-colorpicker-mode" type="radio" value="b"/><label>' + inst._getRegional('rgbB') + '</label><input class="ui-colorpicker-number" type="number" min="0" max="255"/></div>';
3134					}
3135
3136					return '<div class="ui-colorpicker-rgb">' + html + '</div>';
3137				};
3138
3139				this.init = function () {
3140					part = $(_html()).appendTo($('.ui-colorpicker-rgb-container', inst.dialog));
3141
3142					$('.ui-colorpicker-mode', part).click(function () {
3143						inst.mode = $(this).val();
3144						inst._updateAllParts();
3145					});
3146					
3147					inputs.r = $('.ui-colorpicker-rgb-r .ui-colorpicker-number', part);
3148					inputs.g = $('.ui-colorpicker-rgb-g .ui-colorpicker-number', part);
3149					inputs.b = $('.ui-colorpicker-rgb-b .ui-colorpicker-number', part);
3150
3151					$('.ui-colorpicker-number', part).bind('change keyup', function () {
3152						var r = $('.ui-colorpicker-rgb-r .ui-colorpicker-number', part).val();
3153						inst.color.setRGB(
3154							inputs.r.val() / 255,
3155							inputs.g.val() / 255,
3156							inputs.b.val() / 255
3157						);
3158
3159						inst._change();
3160					});
3161				};
3162
3163				this.repaint = function () {
3164					var rgb = inst.color.getRGB();
3165					inputs.r.val(Math.round(rgb.r * 255));
3166					inputs.g.val(Math.round(rgb.g * 255));
3167					inputs.b.val(Math.round(rgb.b * 255));
3168				};
3169
3170				this.update = function () {
3171					$('.ui-colorpicker-mode', part).each(function () {
3172						var $this = $(this);
3173						$this.attr('checked', $this.val() === inst.mode);
3174					});
3175					this.repaint();
3176				};
3177
3178				this.disable = function (disable) {
3179					$(':input', part).prop('disabled', disable);
3180				};
3181			},
3182
3183			lab: function (inst) {
3184				var that = this,
3185					part = null,
3186					inputs = {},
3187					html = function () {
3188						var html = '';
3189
3190						if (inst.options.hsv) {
3191							html +=	'<div class="ui-colorpicker-lab-l"><label>' + inst._getRegional('labL') + '</label><input class="ui-colorpicker-number" type="number" min="0" max="100"/></div>'
3192								+ '<div class="ui-colorpicker-lab-a"><label>' + inst._getRegional('labA') + '</label><input class="ui-colorpicker-number" type="number" min="-128" max="127"/></div>'
3193								+ '<div class="ui-colorpicker-lab-b"><label>' + inst._getRegional('labB') + '</label><input class="ui-colorpicker-number" type="number" min="-128" max="127"/></div>';
3194						}
3195
3196						return '<div class="ui-colorpicker-lab">' + html + '</div>';
3197					};
3198
3199				this.init = function () {
3200					var data = 0;
3201
3202					part = $(html()).appendTo($('.ui-colorpicker-lab-container', inst.dialog));
3203					
3204					inputs.l = $('.ui-colorpicker-lab-l .ui-colorpicker-number', part);
3205					inputs.a = $('.ui-colorpicker-lab-a .ui-colorpicker-number', part);
3206					inputs.b = $('.ui-colorpicker-lab-b .ui-colorpicker-number', part);
3207
3208					$('.ui-colorpicker-number', part).bind('change keyup', function (event) {
3209						inst.color.setLAB(
3210							parseInt(inputs.l.val(), 10) / 100,
3211							(parseInt(inputs.a.val(), 10) + 128) / 255,
3212							(parseInt(inputs.b.val(), 10) + 128) / 255
3213						);
3214						inst._change();
3215					});
3216				};
3217
3218				this.repaint = function () {
3219					var lab = inst.color.getLAB();
3220					inputs.l.val(Math.round(lab.l * 100));
3221					inputs.a.val(Math.round(lab.a * 255) - 128);
3222					inputs.b.val(Math.round(lab.b * 255) - 128);
3223				};
3224
3225				this.update = this.repaint;
3226				
3227				this.disable = function (disable) {
3228					$(':input', part).prop('disabled', disable);
3229				};
3230			},
3231
3232			cmyk: function (inst) {
3233				var that = this,
3234					part = null,
3235					inputs = {},
3236					html = function () {
3237						var html = '';
3238
3239						if (inst.options.hsv) {
3240							html +=	'<div class="ui-colorpicker-cmyk-c"><label>' + inst._getRegional('cmykC') + '</label><input class="ui-colorpicker-number" type="number" min="0" max="100"/>
3240<span class="ui-colorpicker-unit">%</span></div>'
3241								+ '<div class="ui-colorpicker-cmyk-m"><label>' + inst._getRegional('cmykM') + '</label><input class="ui-colorpicker-number" type="number" min="0" max="100"/><span class="ui-colorpicker-unit">%</span></div>'
3242								+ '<div class="ui-colorpicker-cmyk-y"><label>' + inst._getRegional('cmykY') + '</label><input class="ui-colorpicker-number" type="number" min="0" max="100"/><span class="ui-colorpicker-unit">%</span></div>'
3243								+ '<div class="ui-colorpicker-cmyk-k"><label>' + inst._getRegional('cmykK') + '</label><input class="ui-colorpicker-number" type="number" min="0" max="100"/><span class="ui-colorpicker-unit">%</span></div>';
3244						}
3245
3246						return '<div class="ui-colorpicker-cmyk">' + html + '</div>';
3247					};
3248
3249				this.init = function () {
3250					part = $(html()).appendTo($('.ui-colorpicker-cmyk-container', inst.dialog));
3251					
3252					inputs.c = $('.ui-colorpicker-cmyk-c .ui-colorpicker-number', part);
3253					inputs.m = $('.ui-colorpicker-cmyk-m .ui-colorpicker-number', part);
3254					inputs.y = $('.ui-colorpicker-cmyk-y .ui-colorpicker-number', part);
3255					inputs.k = $('.ui-colorpicker-cmyk-k .ui-colorpicker-number', part);
3256					
3257					$('.ui-colorpicker-number', part).bind('change keyup', function (event) {
3258						inst.color.setCMYK(
3259							parseInt(inputs.c.val(), 10) / 100,
3260							parseInt(inputs.m.val(), 10) / 100,
3261							parseInt(inputs.y.val(), 10) / 100,
3262							parseInt(inputs.k.val(), 10) / 100
3263						);
3264						inst._change();
3265					});
3266				};
3267
3268				this.repaint = function () {
3269					var cmyk = inst.color.getCMYK();
3270					inputs.c.val(Math.round(cmyk.c * 100));
3271					inputs.m.val(Math.round(cmyk.m * 100));
3272					inputs.y.val(Math.round(cmyk.y * 100));
3273					inputs.k.val(Math.round(cmyk.k * 100));
3274				};
3275
3276				this.update = this.repaint;
3277
3278				this.disable = function (disable) {
3279					$(':input', part).prop('disabled', disable);
3280				};
3281			},
3282
3283			alpha: function (inst) {
3284				var that = this,
3285					part = null,
3286					input,
3287					html = function () {
3288						var html = '';
3289
3290						if (inst.options.alpha) {
3291							html += '<div class="ui-colorpicker-a"><input class="ui-colorpicker-mode" name="mode" type="radio" value="a"/><label>' + inst._getRegional('alphaA') + '</label><input class="ui-colorpicker-number" type="number" min="0" max="100"/><span class="ui-colorpicker-unit">%</span></div>';
3292						}
3293
3294						return '<div class="ui-colorpicker-alpha">' + html + '</div>';
3295					};
3296
3297				this.init = function () {
3298					part = $(html()).appendTo($('.ui-colorpicker-alpha-container', inst.dialog));
3299
3300					$('.ui-colorpicker-mode', part).click(function () {
3301						inst.mode = $(this).val();
3302						inst._updateAllParts();
3303					});
3304					
3305					input = $('.ui-colorpicker-a .ui-colorpicker-number', part);
3306
3307					$('.ui-colorpicker-number', part).bind('change keyup', function () {
3308						inst.color.setAlpha(input.val() / 100);
3309						inst._change();
3310					});
3311				};
3312
3313				this.update = function () {
3314					$('.ui-colorpicker-mode', part).each(function () {
3315						$(this).attr('checked', $(this).val() === inst.mode);
3316					});
3317					this.repaint();
3318				};
3319
3320				this.repaint = function () {
3321					input.val(Math.round(inst.color.getAlpha() * 100));
3322				};
3323				
3324				this.disable = function (disable) {
3325					$(':input', part).prop('disabled', disable);
3326				};
3327			},
3328
3329			hex: function (inst) {
3330				var that = this,
3331					part = null,
3332					inputs = {},
3333					parseHex = function(color) {
3334						var c,
3335							m;
3336
3337						// {#}rgb
3338						m = /^#?([a-fA-F0-9]{1,3})$/.exec(color);
3339						if (m) {
3340							c = parseInt(m[1], 16);
3341							return new $.colorpicker.Color(
3342								((c >> 8) & 0xF) / 15,
3343								((c >> 4) & 0xF) / 15,
3344								(c & 0xF) / 15
3345							);
3346						}
3347
3348						// {#}rrggbb
3349						m = /^#?([a-fA-F0-9]{1,6})$/.exec(color);
3350						if (m) {
3351							c = parseInt(m[1], 16);
3352							return new $.colorpicker.Color(
3353								((c >> 16) & 0xFF) / 255,
3354								((c >>  8) & 0xFF) / 255,
3355								(c & 0xFF) / 255
3356							);
3357						}
3358						
3359						return new $.colorpicker.Color();
3360					},
3361					html = function () {
3362						var html = '';
3363
3364						if (inst.options.alpha) {
3365							html += '<input class="ui-colorpicker-hex-alpha" type="text" maxlength="2" size="2"/>';
3366						}
3367
3368						html += '<input class="ui-colorpicker-hex-input" type="text" maxlength="6" size="6"/>';
3369
3370						return '<div class="ui-colorpicker-hex"><label>#</label>' + html + '</div>';
3371					};
3372
3373				this.init = function () {
3374					part = $(html()).appendTo($('.ui-colorpicker-hex-container', inst.dialog));
3375
3376					inputs.color = $('.ui-colorpicker-hex-input', part);
3377					inputs.alpha = $('.ui-colorpicker-hex-alpha', part);
3378
3379					inputs.color.bind('keydown keyup', function(e) {	
3380						return e.ctrlKey || _keycode.isHex(e.which) || !_keycode.isPrint(e.which);
3381					});
3382
3383					// repeat here makes the invalid input disappear faster
3384					inputs.color.bind('change', function () {
3385						if (/[^a-fA-F0-9]/.test(inputs.color.val())) {
3386							inputs.color.val(inputs.color.val().replace(/[^a-fA-F0-9]/, ''));
3387						}
3388					});
3389
3390					inputs.color.bind('change keyup', function () {
3391						// repeat here makes sure that the invalid input doesn't get parsed
3392						inst.color = parseHex(inputs.color.val()).setAlpha(inst.color.getAlpha());
3393						inst._change();
3394					});
3395
3396					inputs.alpha.bind('keydown keyup', function(e) {
3397						return e.ctrlKey || _keycode.isHex(e.which) || !_keycode.isPrint(e.which);
3398					});
3399					
3400					inputs.alpha.bind('change', function () {
3401						if (/[^a-fA-F0-9]/.test(inputs.alpha)) {
3402							inputs.alpha.val(inputs.alpha.val().replace(/[^a-fA-F0-9]/, ''));
3403						}
3404					});
3405
3406					inputs.alpha.bind('change keyup', function () {
3407						inst.color.setAlpha(parseInt(inputs.alpha.val(), 16) / 255);
3408						inst._change();
3409					});
3410				};
3411
3412				this.repaint = function () {
3413					if (!inputs.color.is(':focus')) {
3414						inputs.color.val(inst.color.toHex(true));
3415					}
3416
3417					if (!inputs.alpha.is(':focus')) {
3418						inputs.alpha.val(_intToHex(inst.color.getAlpha() * 255));
3419					}
3420				};
3421
3422				this.update = this.repaint;
3423
3424				this.disable = function (disable) {
3425					$(':input', part).prop('disabled', disable);
3426				};
3427			},
3428
3429			swatches: function (inst) {
3430				var that = this,
3431					part = null,
3432					html = function () {
3433						var html = '';
3434
3435						inst._eachSwatch(function (name, color) {
3436							var c = new $.colorpicker.Color(color.r, color.g, color.b),
3437								css = c.toCSS();
3438							html += '<div class="ui-colorpicker-swatch" style="background-color:' + css + '" title="' + name + '"></div>';
3439						});
3440
3441						return '<div class="ui-colorpicker-swatches ui-colorpicker-border" style="width:' + inst.options.swatchesWidth + 'px">' + html + '</div>';
3442					},
3443					onclick = function () {
3444						inst.color	= inst._parseColor($(this).css('background-color')) || new $.colorpicker.Color();
3445						inst._change();
3446					};
3447
3448				this.init = function () {
3449					part = $(html());
3450					$('.ui-colorpicker-swatches-container', inst.dialog).html(part);
3451					$('.ui-colorpicker-swatch', part).bind('click', onclick);
3452				};
3453
3454				this.disable = function (disable) {
3455					$('.ui-colorpicker-swatch', part)[disable ? 'unbind' : 'bind']('click', onclick);
3456				};
3457			},
3458
3459			footer: function (inst) {
3460				var that = this,
3461					part = null,
3462					id_transparent = 'ui-colorpicker-special-transparent-' + inst.colorpicker_index,
3463					id_none = 'ui-colorpicker-special-none-' + inst.colorpicker_index,
3464					html = function () {
3465						var html = '';
3466
3467						if (inst.options.alpha || (!inst.inline && inst.options.showNoneButton)) {
3468							html += '<div class="ui-colorpicker-buttonset">';
3469
3470							if (inst.options.alpha) {
3471								html += '<input type="radio" name="ui-colorpicker-special" id="'+id_transparent+'" class="ui-colorpicker-special-transparent"/><label for="'+id_transparent+'">' + inst._getRegional('transparent') + '</label>';
3472							}
3473							if (!inst.inline && inst.options.showNoneButton) {
3474								html += '<input type="radio" name="ui-colorpicker-special" id="'+id_none+'" class="ui-colorpicker-special-none"><label for="'+id_none+'">' + inst._getRegional('none') + '</label>';
3475							}
3476							html += '</div>';
3477						}
3478
3479						if (!inst.inline) {
3480							html += '<div class="ui-dialog-buttonset">';
3481							if (inst.options.showCancelButton) {
3482								html += '<button class="btn btn-custom btn-custom-secondary ui-colorpicker-cancel">' + inst._getRegional('cancel') + '</button>';
3483							}
3484							html += '<button class="btn btn-custom btn-custom-secondary ui-colorpicker-ok">' + inst._getRegional('ok') + '</button>';
3485							html += '</div>';
3486						}
3487
3488						return '<div class="ui-dialog-buttonpane ui-widget-content">' + html + '</div>';
3489					};
3490
3491				this.init = function () {
3492					part = $(html()).appendTo(inst.dialog);
3493
3494					$('.ui-colorpicker-ok', part).button().click(function () {
3495						inst.close();
3496					});
3497
3498					$('.ui-colorpicker-cancel', part).button().click(function () {
3499						inst.close(true);   //cancel
3500					});
3501
3502					$('.ui-colorpicker-buttonset', part)[$.fn.controlgroup ? 'controlgroup' : 'buttonset']();
3503
3504					$('.ui-colorpicker-special-color', part).click(function () {
3505						inst._change();
3506					});
3507
3508					$('#'+id_none, part).click(function () {
3509						inst.color.set = false;
3510						inst._change();
3511					});
3512
3513					$('#'+id_transparent, part).click(function () {
3514						inst.color.setAlpha(0);
3515						inst._change();
3516					});
3517				};
3518
3519				this.repaint = function () {
3520					$('.ui-colorpicker-special-none', part).prop('checked', !inst.color.set).button('refresh');
3521					$('.ui-colorpicker-special-transparent', part).prop('checked', inst.color.set && inst.color.getAlpha() === 0).button('refresh');
3522					$('.ui-colorpicker-ok', part).button(inst.changed ? 'enable' : 'disable');
3523				};
3524
3525				this.update = function () {};
3526
3527				this.disable = function (disabled) {
3528					$(':input, :button', part).button(disabled ? 'disable' : 'enable');
3529					if (!disabled) {
3530						$('.ui-colorpicker-ok', part).button(inst.changed ? 'enable' : 'disable');
3531					}
3532				};
3533			}
3534		};
3535
3536		this.Color = function () {
3537			var spaces = {	rgb:	{r: 0, g: 0, b: 0},
3538							hsv:	{h: 0, s: 0, v: 0},
3539							hsl:	{h: 0, s: 0, l: 0},
3540							lab:	{l: 0, a: 0, b: 0},
3541							cmyk:	{c: 0, m: 0, y: 0, k: 1}
3542						},
3543				a = 1,
3544				illuminant = [0.9504285, 1, 1.0889],	// CIE-L*ab D65/2' 1931
3545				args = arguments,
3546				_clip = function(v) {
3547					if (isNaN(v) || v === null) {
3548						return 0;
3549					}
3550					if (typeof v == 'string') {
3551						v = parseInt(v, 10);
3552					}
3553					return Math.max(0, Math.min(v, 1));
3554				},
3555				_hexify = function (number) {
3556					var number = Math.round(number),
3557						digits = '0123456789abcdef',
3558						lsd = number % 16,
3559						msd = (number - lsd) / 16,
3560						hexified = digits.charAt(msd) + digits.charAt(lsd);
3561					return hexified;
3562				},
3563				_rgb_to_xyz = function(rgb) {
3564					var r = (rgb.r > 0.04045) ? Math.pow((rgb.r + 0.055) / 1.055, 2.4) : rgb.r / 12.92,
3565						g = (rgb.g > 0.04045) ? Math.pow((rgb.g + 0.055) / 1.055, 2.4) : rgb.g / 12.92,
3566						b = (rgb.b > 0.04045) ? Math.pow((rgb.b + 0.055) / 1.055, 2.4) : rgb.b / 12.92;
3567
3568					return {
3569						x: r * 0.4124 + g * 0.3576 + b * 0.1805,
3570						y: r * 0.2126 + g * 0.7152 + b * 0.0722,
3571						z: r * 0.0193 + g * 0.1192 + b * 0.9505
3572					};
3573				},
3574				_xyz_to_rgb = function(xyz) {
3575					var rgb = {
3576						r: xyz.x *  3.2406 + xyz.y * -1.5372 + xyz.z * -0.4986,
3577						g: xyz.x * -0.9689 + xyz.y *  1.8758 + xyz.z *  0.0415,
3578						b: xyz.x *  0.0557 + xyz.y * -0.2040 + xyz.z *  1.0570
3579					};
3580
3581					rgb.r = (rgb.r > 0.0031308) ? 1.055 * Math.pow(rgb.r, (1 / 2.4)) - 0.055 : 12.92 * rgb.r;
3582					rgb.g = (rgb.g > 0.0031308) ? 1.055 * Math.pow(rgb.g, (1 / 2.4)) - 0.055 : 12.92 * rgb.g;
3583					rgb.b = (rgb.b > 0.0031308) ? 1.055 * Math.pow(rgb.b, (1 / 2.4)) - 0.055 : 12.92 * rgb.b;
3584
3585					return rgb;
3586				},
3587				_rgb_to_hsv = function(rgb) {
3588					var minVal = Math.min(rgb.r, rgb.g, rgb.b),
3589						maxVal = Math.max(rgb.r, rgb.g, rgb.b),
3590						delta = maxVal - minVal,
3591						del_R, del_G, del_B,
3592						hsv = {
3593							h: 0,
3594							s: 0,
3595							v: maxVal
3596						};
3597
3598					if (delta === 0) {
3599						hsv.h = 0;
3600						hsv.s = 0;
3601					} else {
3602						hsv.s = delta / maxVal;
3603
3604						del_R = (((maxVal - rgb.r) / 6) + (delta / 2)) / delta;
3605						del_G = (((maxVal - rgb.g) / 6) + (delta / 2)) / delta;
3606						del_B = (((maxVal - rgb.b) / 6) + (delta / 2)) / delta;
3607
3608						if (rgb.r === maxVal) {
3609							hsv.h = del_B - del_G;
3610						} else if (rgb.g === maxVal) {
3611							hsv.h = (1 / 3) + del_R - del_B;
3612						} else if (rgb.b === maxVal) {
3613							hsv.h = (2 / 3) + del_G - del_R;
3614						}
3615
3616						if (hsv.h < 0) {
3617							hsv.h += 1;
3618						} else if (hsv.h > 1) {
3619							hsv.h -= 1;
3620						}
3621					}
3622
3623					return hsv;
3624				},
3625				_hsv_to_rgb = function(hsv) {
3626					var rgb = {
3627							r: 0,
3628							g: 0,
3629							b: 0
3630						},
3631						var_h,
3632						var_i,
3633						var_1,
3634						var_2,
3635						var_3;
3636
3637					if (hsv.s === 0) {
3638						rgb.r = rgb.g = rgb.b = hsv.v;
3639					} else {
3640						var_h = hsv.h === 1 ? 0 : hsv.h * 6;
3641						var_i = Math.floor(var_h);
3642						var_1 = hsv.v * (1 - hsv.s);
3643						var_2 = hsv.v * (1 - hsv.s * (var_h - var_i));
3644						var_3 = hsv.v * (1 - hsv.s * (1 - (var_h - var_i)));
3645
3646						if (var_i === 0) {
3647							rgb.r = hsv.v;
3648							rgb.g = var_3;
3649							rgb.b = var_1;
3650						} else if (var_i === 1) {
3651							rgb.r = var_2;
3652							rgb.g = hsv.v;
3653							rgb.b = var_1;
3654						} else if (var_i === 2) {
3655							rgb.r = var_1;
3656							rgb.g = hsv.v;
3657							rgb.b = var_3;
3658						} else if (var_i === 3) {
3659							rgb.r = var_1;
3660							rgb.g = var_2;
3661							rgb.b = hsv.v;
3662						} else if (var_i === 4) {
3663							rgb.r = var_3;
3664							rgb.g = var_1;
3665							rgb.b = hsv.v;
3666						} else {
3667							rgb.r = hsv.v;
3668							rgb.g = var_1;
3669							rgb.b = var_2;
3670						}
3671					}
3672
3673					return rgb;
3674				},
3675				_rgb_to_hsl = function(rgb) {
3676					var minVal = Math.min(rgb.r, rgb.g, rgb.b),
3677						maxVal = Math.max(rgb.r, rgb.g, rgb.b),
3678						delta = maxVal - minVal,
3679						del_R, del_G, del_B,
3680						hsl = {
3681							h: 0,
3682							s: 0,
3683							l: (maxVal + minVal) / 2
3684						};
3685
3686					if (delta === 0) {
3687						hsl.h = 0;
3688						hsl.s = 0;
3689					} else {
3690						hsl.s = hsl.l < 0.5 ? delta / (maxVal + minVal) : delta / (2 - maxVal - minVal);
3691
3692						del_R = (((maxVal - rgb.r) / 6) + (delta / 2)) / delta;
3693						del_G = (((maxVal - rgb.g) / 6) + (delta / 2)) / delta;
3694						del_B = (((maxVal - rgb.b) / 6) + (delta / 2)) / delta;
3695
3696						if (rgb.r === maxVal) {
3697							hsl.h = del_B - del_G;
3698						} else if (rgb.g === maxVal) {
3699							hsl.h = (1 / 3) + del_R - del_B;
3700						} else if (rgb.b === maxVal) {
3701							hsl.h = (2 / 3) + del_G - del_R;
3702						}
3703
3704						if (hsl.h < 0) {
3705							hsl.h += 1;
3706						} else if (hsl.h > 1) {
3707							hsl.h -= 1;
3708						}
3709					}
3710
3711					return hsl;
3712				},
3713				_hsl_to_rgb = function(hsl) {
3714					var var_1,
3715						var_2,
3716						hue_to_rgb	= function(v1, v2, vH) {
3717										if (vH < 0) {
3718											vH += 1;
3719										}
3720										if (vH > 1) {
3721											vH -= 1;
3722										}
3723										if ((6 * vH) < 1) {
3724											return v1 + (v2 - v1) * 6 * vH;
3725										}
3726										if ((2 * vH) < 1) {
3727											return v2;
3728										}
3729										if ((3 * vH) < 2) {
3730											return v1 + (v2 - v1) * ((2 / 3) - vH) * 6;
3731										}
3732										return v1;
3733									};
3734
3735					if (hsl.s === 0) {
3736						return {
3737							r: hsl.l,
3738							g: hsl.l,
3739							b: hsl.l
3740						};
3741					}
3742
3743					var_2 = (hsl.l < 0.5) ? hsl.l * (1 + hsl.s) : (hsl.l + hsl.s) - (hsl.s * hsl.l);
3744					var_1 = 2 * hsl.l - var_2;
3745
3746					return {
3747						r: hue_to_rgb(var_1, var_2, hsl.h + (1 / 3)),
3748						g: hue_to_rgb(var_1, var_2, hsl.h),
3749						b: hue_to_rgb(var_1, var_2, hsl.h - (1 / 3))
3750					};
3751				},
3752				_xyz_to_lab = function(xyz) {
3753					var x = xyz.x / illuminant[0],
3754						y = xyz.y / illuminant[1],
3755						z = xyz.z / illuminant[2];
3756
3757					x = (x > 0.008856) ? Math.pow(x, (1/3)) : (7.787 * x) + (16/116);
3758					y = (y > 0.008856) ? Math.pow(y, (1/3)) : (7.787 * y) + (16/116);
3759					z = (z > 0.008856) ? Math.pow(z, (1/3)) : (7.787 * z) + (16/116);
3760
3761					return {
3762						l: ((116 * y) - 16) / 100,	// [0,100]
3763						a: ((500 * (x - y)) + 128) / 255,	// [-128,127]
3764						b: ((200 * (y - z))	+ 128) / 255	// [-128,127]
3765					};
3766				},
3767				_lab_to_xyz = function(lab) {
3768					var lab2 = {
3769							l: lab.l * 100,
3770							a: (lab.a * 255) - 128,
3771							b: (lab.b * 255) - 128
3772						},
3773						xyz = {
3774							x: 0,
3775							y: (lab2.l + 16) / 116,
3776							z: 0
3777						};
3778
3779					xyz.x = lab2.a / 500 + xyz.y;
3780					xyz.z = xyz.y - lab2.b / 200;
3781
3782					xyz.x = (Math.pow(xyz.x, 3) > 0.008856) ? Math.pow(xyz.x, 3) : (xyz.x - 16 / 116) / 7.787;
3783					xyz.y = (Math.pow(xyz.y, 3) > 0.008856) ? Math.pow(xyz.y, 3) : (xyz.y - 16 / 116) / 7.787;
3784					xyz.z = (Math.pow(xyz.z, 3) > 0.008856) ? Math.pow(xyz.z, 3) : (xyz.z - 16 / 116) / 7.787;
3785
3786					xyz.x *= illuminant[0];
3787					xyz.y *= illuminant[1];
3788					xyz.z *= illuminant[2];
3789
3790					return xyz;
3791				},
3792				_rgb_to_cmy = function(rgb) {
3793					return {
3794						c: 1 - (rgb.r),
3795						m: 1 - (rgb.g),
3796						y: 1 - (rgb.b)
3797					};
3798				},
3799				_cmy_to_rgb = function(cmy) {
3800					return {
3801						r: 1 - (cmy.c),
3802						g: 1 - (cmy.m),
3803						b: 1 - (cmy.y)
3804					};
3805				},
3806				_cmy_to_cmyk = function(cmy) {
3807					var K = 1;
3808
3809					if (cmy.c < K) {
3810						K = cmy.c;
3811					}
3812					if (cmy.m < K) {
3813						K = cmy.m;
3814					}
3815					if (cmy.y < K) {
3816						K = cmy.y;
3817					}
3818
3819					if (K === 1) {
3820						return {
3821							c: 0,
3822							m: 0,
3823							y: 0,
3824							k: 1
3825						};
3826					}
3827
3828					return {
3829						c: (cmy.c - K) / (1 - K),
3830						m: (cmy.m - K) / (1 - K),
3831						y: (cmy.y - K) / (1 - K),
3832						k: K
3833					};
3834				},
3835				_cmyk_to_cmy = function(cmyk) {
3836					return {
3837						c: cmyk.c * (1 - cmyk.k) + cmyk.k,
3838						m: cmyk.m * (1 - cmyk.k) + cmyk.k,
3839						y: cmyk.y * (1 - cmyk.k) + cmyk.k
3840					};
3841				};
3842
3843			this.set = false;
3844
3845			this.setAlpha = function(_a) {
3846				if (_a !== null) {
3847					a = _clip(_a);
3848				}
3849				this.set = true;
3850
3851				return this;
3852			};
3853
3854			this.getAlpha = function() {
3855				return a;
3856			};
3857
3858			this.setRGB = function(r, g, b) {
3859				spaces = { rgb: this.getRGB() };
3860				if (r !== null) {
3861					spaces.rgb.r = _clip(r);
3862				}
3863				if (g !== null) {
3864					spaces.rgb.g = _clip(g);
3865				}
3866				if (b !== null) {
3867					spaces.rgb.b = _clip(b);
3868				}
3869				this.set = true;
3870				
3871				return this;
3872			};
3873			
3874			this.getChannel = function(channel) {
3875				switch (channel) {
3876					case 'h':
3877					case 's':
3878					case 'v':
3879						return this.getHSV()[channel];
3880
3881					case 'r':
3882					case 'g':
3883					case 'b':
3884						return this.getRGB()[channel];
3885
3886					case 'a':
3887						return this.getAlpha();
3888				}			
3889				
3890				return null;
3891			};
3892			
3893			this.setChannel = function(channel, value) {
3894				switch (channel) {
3895					case 'h':
3896						return this.setHSV(value, null, null);
3897						
3898					case 's':
3899						return this.setHSV(null, value, null);
3900						
3901					case 'v':
3902						return this.setHSV(null, null, value);
3903
3904					case 'r':
3905						return this.setRGB(value, null, null);
3906						
3907					case 'g':
3908						return this.setRGB(null, value, null);
3909						
3910					case 'b':
3911						return this.setRGB(null, null, value);
3912
3913					case 'a':
3914						return this.setAlpha(value);
3915				}
3916				
3917				return this;
3918			};
3919
3920			this.setHSV = function(h, s, v) {
3921				spaces = {hsv: this.getHSV()};
3922				if (h !== null) {
3923					spaces.hsv.h = _clip(h);
3924				}
3925				if (s !== null)	{
3926					spaces.hsv.s = _clip(s);
3927				}
3928				if (v !== null)	{
3929					spaces.hsv.v = _clip(v);
3930				}
3931				this.set = true;
3932
3933				return this;
3934			};
3935
3936			this.setHSL = function(h, s, l) {
3937				spaces = {hsl: this.getHSL()};
3938				if (h !== null)	{
3939					spaces.hsl.h = _clip(h);
3940				}
3941				if (s !== null) {
3942					spaces.hsl.s = _clip(s);
3943				}
3944				if (l !== null) {
3945					spaces.hsl.l = _clip(l);
3946				}
3947				this.set = true;
3948
3949				return this;
3950			};
3951
3952			this.setLAB = function(l, a, b) {
3953				spaces = {lab: this.getLAB()};
3954				if (l !== null) {
3955					spaces.lab.l = _clip(l);
3956				}
3957				if (a !== null) {
3958					spaces.lab.a = _clip(a);
3959				}
3960				if (b !== null) {
3961					spaces.lab.b = _clip(b);
3962				}
3963				this.set = true;
3964
3965				return this;
3966			};
3967
3968			this.setCMYK = function(c, m, y, k) {
3969				spaces = {cmyk: this.getCMYK()};
3970				if (c !== null) {
3971					spaces.cmyk.c = _clip(c);
3972				}
3973				if (m !== null) {
3974					spaces.cmyk.m = _clip(m);
3975				}
3976				if (y !== null) {
3977					spaces.cmyk.y = _clip(y);
3978				}
3979				if (k !== null) {
3980					spaces.cmyk.k = _clip(k);
3981				}
3982				this.set = true;
3983
3984				return this;
3985			};
3986
3987			this.getRGB = function() {
3988				if (!spaces.rgb) {
3989					spaces.rgb	= spaces.lab ?	_xyz_to_rgb(_lab_to_xyz(spaces.lab))
3990								: spaces.hsv ?	_hsv_to_rgb(spaces.hsv)
3991								: spaces.hsl ?	_hsl_to_rgb(spaces.hsl)
3992								: spaces.cmyk ?	_cmy_to_rgb(_cmyk_to_cmy(spaces.cmyk))
3993								: {r: 0, g: 0, b: 0};
3994					spaces.rgb.r = _clip(spaces.rgb.r);
3995					spaces.rgb.g = _clip(spaces.rgb.g);
3996					spaces.rgb.b = _clip(spaces.rgb.b);
3997				}
3998				return $.extend({}, spaces.rgb);
3999			};
4000
4001			this.getHSV = function() {
4002				if (!spaces.hsv) {
4003					spaces.hsv	= spaces.lab ? _rgb_to_hsv(this.getRGB())
4004								: spaces.rgb ?	_rgb_to_hsv(spaces.rgb)
4005								: spaces.hsl ?	_rgb_to_hsv(this.getRGB())
4006								: spaces.cmyk ?	_rgb_to_hsv(this.getRGB())
4007								: {h: 0, s: 0, v: 0};
4008					spaces.hsv.h = _clip(spaces.hsv.h);
4009					spaces.hsv.s = _clip(spaces.hsv.s);
4010					spaces.hsv.v = _clip(spaces.hsv.v);
4011				}
4012				return $.extend({}, spaces.hsv);
4013			};
4014
4015			this.getHSL = function() {
4016				if (!spaces.hsl) {
4017					spaces.hsl	= spaces.rgb ?	_rgb_to_hsl(spaces.rgb)
4018								: spaces.hsv ?	_rgb_to_hsl(this.getRGB())
4019								: spaces.cmyk ?	_rgb_to_hsl(this.getRGB())
4020								: spaces.hsv ?	_rgb_to_hsl(this.getRGB())
4021								: {h: 0, s: 0, l: 0};
4022					spaces.hsl.h = _clip(spaces.hsl.h);
4023					spaces.hsl.s = _clip(spaces.hsl.s);
4024					spaces.hsl.l = _clip(spaces.hsl.l);
4025				}
4026				return $.extend({}, spaces.hsl);
4027			};
4028
4029			this.getCMYK = function() {
4030				if (!spaces.cmyk) {
4031					spaces.cmyk	= spaces.rgb ?	_cmy_to_cmyk(_rgb_to_cmy(spaces.rgb))
4032								: spaces.hsv ?	_cmy_to_cmyk(_rgb_to_cmy(this.getRGB()))
4033								: spaces.hsl ?	_cmy_to_cmyk(_rgb_to_cmy(this.getRGB()))
4034								: spaces.lab ?	_cmy_to_cmyk(_rgb_to_cmy(this.getRGB()))
4035								: {c: 0, m: 0, y: 0, k: 1};
4036					spaces.cmyk.c = _clip(spaces.cmyk.c);
4037					spaces.cmyk.m = _clip(spaces.cmyk.m);
4038					spaces.cmyk.y = _clip(spaces.cmyk.y);
4039					spaces.cmyk.k = _clip(spaces.cmyk.k);
4040				}
4041				return $.extend({}, spaces.cmyk);
4042			};
4043
4044			this.getLAB = function() {
4045				if (!spaces.lab) {
4046					spaces.lab	= spaces.rgb ?	_xyz_to_lab(_rgb_to_xyz(spaces.rgb))
4047								: spaces.hsv ?	_xyz_to_lab(_rgb_to_xyz(this.getRGB()))
4048								: spaces.hsl ?	_xyz_to_lab(_rgb_to_xyz(this.getRGB()))
4049								: spaces.cmyk ?	_xyz_to_lab(_rgb_to_xyz(this.getRGB()))
4050								: {l: 0, a: 0, b: 0};
4051					spaces.lab.l = _clip(spaces.lab.l);
4052					spaces.lab.a = _clip(spaces.lab.a);
4053					spaces.lab.b = _clip(spaces.lab.b);
4054				}
4055				return $.extend({}, spaces.lab);
4056			};
4057
4058			this.getChannels = function() {
4059				return {
4060					r:	this.getRGB().r,
4061					g:	this.getRGB().g,
4062					b:	this.getRGB().b,
4063					a:	this.getAlpha(),
4064					h:	this.getHSV().h,
4065					s:	this.getHSV().s,
4066					v:	this.getHSV().v,
4067					c:	this.getCMYK().c,
4068					m:	this.getCMYK().m,
4069					y:	this.getCMYK().y,
4070					k:	this.getCMYK().k,
4071					L:	this.getLAB().l,
4072					A:	this.getLAB().a,
4073					B:	this.getLAB().b
4074				};
4075			};
4076
4077			this.getSpaces = function() {
4078				return $.extend(true, {}, spaces);
4079			};
4080
4081			this.distance = function(color) {
4082				var space	= 'lab',
4083					getter	= 'get'+space.toUpperCase(),
4084					a = this[getter](),
4085					b = color[getter](),
4086					distance = 0,
4087					channel;
4088
4089				for (channel in a) {
4090					distance += Math.pow(a[channel] - b[channel], 2);
4091				}
4092
4093				return distance;
4094			};
4095
4096			this.equals = function(color) {
4097				if (color) {
4098					var a = this.getRGB(),
4099						b = color.getRGB();
4100
4101					return this.set === color.set
4102						&& this.getAlpha() === color.getAlpha()
4103						&& a.r === b.r
4104						&& a.g === b.g
4105						&& a.b === b.b;
4106				}
4107				return false;
4108			};
4109
4110			this.limit = function(steps) {
4111				steps -= 1;
4112				var rgb = this.getRGB();
4113				this.setRGB(
4114					Math.round(rgb.r * steps) / steps,
4115					Math.round(rgb.g * steps) / steps,
4116					Math.round(rgb.b * steps) / steps
4117				);
4118			};
4119
4120			this.toHex = function() {
4121				var rgb = this.getRGB();
4122				return _hexify(rgb.r * 255) + _hexify(rgb.g * 255) + _hexify(rgb.b * 255);
4123			};
4124
4125			this.toCSS = function() {
4126				return '#' + this.toHex();
4127			};
4128
4129			this.copy = function() {
4130				var color = new $.colorpicker.Color(this.getSpaces(), this.getAlpha());
4131				color.set = this.set;
4132				return color;
4133			};
4134
4135			// Construct
4136			if (args.length === 2) {
4137				spaces = args[0];
4138				this.setAlpha(args[1] === 0 ? 0 : args[1] || 1);
4139				this.set = true;
4140			}
4141			if (args.length > 2) {
4142				this.setRGB(args[0], args[1], args[2]);
4143				this.setAlpha(args[3] === 0 ? 0 : args[3] || 1);
4144				this.set = true;
4145			}
4146		};
4147	}();
4148
4149	$.widget("vanderlee.colorpicker", {
4150		options: {
4151			alpha:				false,		// Show alpha controls and mode
4152			altAlpha:			true,		// change opacity of altField as well?
4153			altField:			'',			// selector for DOM elements which change background color on change.
4154			altOnChange:		true,		// true to update on each change, false to update only on close.
4155			altProperties:		'background-color',	// comma separated list of any of 'background-color', 'color', 'border-color', 'outline-color'
4156			autoOpen:			false,		// Open dialog automatically upon creation
4157			buttonClass:		null,		// If set, the button will get this/these classname(s).
4158			buttonColorize:		false,
4159			buttonImage:		'/Content/images/ui-colorpicker.png',
4160			buttonImageOnly:	false,
4161			buttonText:			null,		// Text on the button and/or title of button image.
4162			closeOnEscape:		true,		// Close the dialog when the escape key is pressed.
4163			closeOnOutside:		true,		// Close the dialog when clicking outside the dialog (not for inline)
4164			color:				'#00FF00',	// Initial color (for inline only)
4165			colorFormat:		'HEX',		// Format string for output color format
4166			disabled:			false,		// Disable or enable the colorpicker initially
4167			draggable:			true,		// Make popup dialog draggable if header is visible.
4168			containment:		null,		// Constrains dragging to within the bounds of the specified element or region.
4169			duration:			'fast',
4170			hideOn:				'button',	// 'focus', 'click', 'button', 'alt', 'all'
4171			hsv:				true,		// Show HSV controls and modes
4172			inline:				true,		// Show any divs as inline by default
4173			inlineFrame:		true,		// Show a border and background when inline.
4174			layout: {
4175				map:		[0, 0, 1, 5],	// Left, Top, Width, Height (in table cells).
4176				bar:		[1, 0, 1, 5],
4177				preview:	[2, 0, 1, 1],
4178				hsv:		[2, 1, 1, 1],
4179				rgb:		[2, 2, 1, 1],
4180				alpha:		[2, 3, 1, 1],
4181				hex:		[2, 4, 1, 1],
4182				lab:		[3, 1, 1, 1],
4183				cmyk:		[3, 2, 1, 2],
4184				swatches:	[4, 0, 1, 5]
4185			},
4186			limit:				'',			// Limit color "resolution": '', 'websafe', 'nibble', 'binary', 'name'
4187			modal:				false,		// Modal dialog?
4188			mode:				'h',		// Initial editing mode, h, s, v, r, g, b or a
4189			okOnEnter:			false,		// Close (with OK) when pressing the enter key
4190			parts:				'',			// leave empty for automatic selection
4191			part: {
4192				map:		{ size: 256 },
4193				bar:		{ size: 256 }
4194			},			// options per part
4195			position:			null,
4196			regional:			'',
4197			revert:				false,		// Revert color upon non
4198			rgb:				true,		// Show RGB controls and modes
4199			showAnim:			'fadeIn',
4200			showCancelButton:	true,
4201			showNoneButton:		false,
4202			showCloseButton:	true,
4203			showOn:				'focus click alt',		// 'focus', 'click', 'button', 'alt', 'all'
4204			showOptions:		{},
4205			swatches:			null,		// null for default or kv-object or names swatches set
4206			swatchesWidth:		84,			// width (in number of pixels) of swatches box.
4207			title:				null,
4208
4209			cancel:             null,
4210            close:              null,
4211			init:				null,
4212            ok:                 null,
4213			open:               null,
4214			select:             null,
4215			stop:				null
4216		},
4217		
4218		_create: function () {
4219			var that = this,
4220				text;
4221
4222			that.colorpicker_index = _colorpicker_index++;
4223
4224			that.widgetEventPrefix = 'colorpicker';
4225
4226			that.opened		= false;
4227			that.generated	= false;
4228			that.inline		= false;
4229			that.changed	= false;
4230
4231			that.dialog		= null;
4232			that.button		= null;
4233			that.image		= null;
4234			that.overlay	= null;
4235			
4236			that.events = {
4237				window_resize:			null,
4238				document_keydown:		null,
4239				document_click_html:	null
4240			};
4241
4242			that.mode		= that.options.mode;
4243
4244			if (that.element.is('input') || that.options.inline === false) {
4245				// Initial color
4246				that._setColor(that.element.is('input') ? that.element.val() : that.options.color);
4247				that._callback('init');
4248
4249				// showOn focus
4250				if (/\bfocus|all|both\b/.test(that.options.showOn)) {
4251					that.element.bind('focus', function () {
4252						that.open();
4253					});
4254				}
4255				if (/\bfocus|all|both\b/.test(that.options.hideOn)) {
4256					that.element.bind('focusout', function (e) {
4257						that.close();
4258					});
4259				}
4260
4261				// showOn click
4262				if (/\bclick|all|both\b/.test(that.options.showOn)) {
4263					that.element.bind('click', function (e) {						
4264						if (that.opened && /\bclick|all|both\b/.test(that.options.hideOn)) {
4265							that.close();
4266						} else {
4267							that.open();
4268						}
4269					});
4270				}
4271
4272				// showOn button
4273				if (/\bbutton|all|both\b/.test(that.options.showOn)) {
4274					if (that.options.buttonImage !== '') {
4275						text = that.options.buttonText || that._getRegional('button');
4276
4277						that.image = $('<img/>').attr({
4278							'src':		that.options.buttonImage,
4279							'alt':		text,
4280							'title':	text
4281						});
4282						if (that.options.buttonClass) {
4283							that.image.attr('class', that.options.buttonClass);
4284						}
4285
4286						that._setImageBackground();
4287					}
4288
4289					if (that.options.buttonImageOnly && that.image) {
4290						that.button = that.image;
4291					} else {
4292						that.button = $('<button type="button"></button>').html(that.image || that.options.buttonText).button();
4293						that.image = that.image ? $('img', that.button).first() : null;
4294					}
4295					that.button.insertAfter(that.element).click(function () {
4296						if (that.opened && /\bbutton|all|both\b/.test(that.options.hideOn)) {
4297							that.close();
4298						} else {
4299							that.open();
4300						}
4301					});
4302				}
4303
4304				// showOn alt
4305				if (/\balt|all|both\b/.test(that.options.showOn)) {					
4306					$(that.options.altField).bind('click', function () {
4307						if (that.opened && /\balt|all|both\b/.test(that.options.hideOn)) {
4308							that.close();
4309						} else {
4310							that.open();
4311						}
4312					});
4313				}
4314
4315				if (that.options.autoOpen) {
4316					that.open();
4317				}
4318
4319			} else {
4320				that.inline = true;
4321
4322				that._generate();
4323				that.opened = true;
4324			}
4325
4326			// Disable Widget-style
4327			(that.element.is(':disabled') || that.options.disabled) && that.disable();
4328
4329			return this;
4330		},
4331
4332		_setOption: function(key, value) {
4333			switch (key) {
4334				case 'disabled':
4335					this[value ? 'disable' : 'enable']();
4336					break;
4337
4338				case 'swatches':
4339					this.options.swatches = value;
4340					this.parts.swatches.init();
4341					break;
4342			}
4343
4344			$.Widget.prototype._setOption.apply(this, arguments);
4345		},
4346
4347		enable: function () {
4348			//$.Widget.prototype.enable.call(this);
4349			this.element && this.element.prop('disabled', false);
4350			this.button && this.button.prop('disabled', false);
4351			this.dialog && this.dialog.removeClass('ui-colorpicker-disabled');
4352			this.options.disabled = false;
4353
4354			this.parts && $.each(this.parts, function (index, part) {
4355				part.disable && part.disable(false);
4356			});
4357		},
4358		
4359		disable: function () {
4360			//$.Widget.prototype.disable.call(this);
4361			this.element && this.element.prop('disabled', true);
4362			this.button && this.button.prop('disabled', true);
4363			this.dialog && this.dialog.addClass('ui-colorpicker-disabled');
4364			this.options.disabled = true;
4365
4366			this.parts && $.each(this.parts, function (index, part) {
4367				part.disable && part.disable(true);
4368			});
4369		},
4370
4371		_setImageBackground: function() {
4372			if (this.image && this.options.buttonColorize) {
4373				this.image.css('background-color', this.color.set? this._formatColor('RGBA', this.color) : '');
4374			}
4375		},
4376
4377		/**
4378		 * If an alternate field is specified, set it according to the current color.
4379		 */
4380		_setAltField: function () {
4381			if (this.options.altOnChange && this.options.altField && this.options.altProperties) {
4382				var index,
4383					property,
4384					properties = this.options.altProperties.split(',');
4385
4386				for (index = 0; index <= properties.length; ++index) {
4387					property = $.trim(properties[index]);
4388					switch (property) {
4389						case 'color':
4390						case 'fill':
4391						case 'stroke':
4392						case 'background-color':
4393						case 'backgroundColor':
4394						case 'outline-color':
4395						case 'border-color':
4396							$(this.options.altField).css(property, this.color.set? this.color.toCSS() : '');
4397							break;
4398					}
4399				}
4400
4401				this.options.altAlpha && 
4402					$(this.options.altField).css('opacity', this.color.set? this.color.getAlpha() : '');
4403			}
4404		},
4405
4406		_setColor: function (text) {
4407			this.color			= this._parseColor(text) || new $.colorpicker.Color();
4408			this.currentColor	= this.color.copy();
4409
4410			this._setImageBackground();
4411			this._setAltField();
4412		},
4413
4414		setColor: function(text) {
4415			this._setColor(text);
4416			this._change();
4417		},
4418
4419		getColor: function (format) {
4420		    if (!this.color.set)
4421		        return null
4422            else
4423			    return this._formatColor(format || this.options.colorFormat, this.color);
4424		},
4425
4426		_generateInline: function() {
4427			var that = this;
4428
4429			$(that.element).html(that.options.inlineFrame ? _container_inlineFrame : _container_inline);
4430
4431			that.dialog = $('.ui-colorpicker', that.element);
4432		},
4433
4434		_generatePopup: function() {
4435			var that = this;
4436
4437			that.dialog = $(_container_popup).appendTo('body');
4438
4439			// Close on clicking outside window and controls
4440			if (that.events.document_click_html === null) {
4441				$(document).delegate('html', 'touchstart click', that.events.document_click_html = function (event) {
4442					if (!that.opened || event.target === that.element[0] || that.overlay) {
4443						return;
4444					}
4445
4446					// Check if clicked on any part of dialog
4447					if (that.dialog.is(event.target) || that.dialog.has(event.target).length > 0) {
4448						that.element.blur();	// inside window!
4449						return;
4450					}
4451
4452					// Check if clicked on known external elements
4453					var p,
4454						parents = $(event.target).parents();
4455					// add the event.target in case of buttonImageOnly and closeOnOutside both are set to true
4456					parents.push(event.target);
4457					for (p = 0; p <= parents.length; ++p) {
4458						// button
4459						if (that.button !== null && parents[p] === that.button[0]) {
4460							return;
4461						}
4462						// showOn alt
4463						if (/\balt|all|both\b/.test(that.options.showOn) && $(that.options.altField).is(parents[p])) {
4464							return;
4465						}
4466					}
4467
4468					// no closeOnOutside
4469					if (!that.options.closeOnOutside) {
4470						return;
4471					}
4472
4473					that.close(that.options.revert);
4474				});
4475			}
4476
4477			if (that.events.document_keydown === null) {
4478				$(document).bind('keydown', that.events.document_keydown = function (event) {
4479					// close on ESC key
4480					if (that.opened && event.keyCode === 27 && that.options.closeOnEscape) {
4481						that.close(that.options.revert);
4482					}
4483
4484					// OK on Enter key
4485					if (that.opened && event.keyCode === 13 && that.options.okOnEnter) {
4486						that.close();
4487					}
4488				});
4489			}
4490
4491			// Close (with OK) on tab key in element
4492			that.element.keydown(function (event) {
4493				if (event.keyCode === 9) {
4494					that.close();
4495				}
4496			}).keyup(function (event) {
4497				var color = that._parseColor(that.element.val());
4498				if (color && !that.color.equals(color)) {
4499					that.color = color;
4500					that._change();
4501				}
4502			});
4503		},
4504
4505		_generate: function () {
4506			var that = this,
4507				index,
4508				part,
4509				parts_list,
4510				layout_parts,
4511				table,
4512				classes;
4513
4514			that._setColor(that.inline || !that.element.is('input') ? that.options.color : that.element.val());
4515
4516			that[that.inline ? '_generateInline' : '_generatePopup']();
4517
4518			// Determine the parts to include in this colorpicker
4519			if (typeof that.options.parts === 'string') {
4520				if ($.colorpicker.partslists[that.options.parts]) {
4521					parts_list = $.colorpicker.partslists[that.options.parts];
4522				} else {
4523					// automatic
4524					parts_list = $.colorpicker.partslists[that.inline ? 'inline' : 'popup'];
4525				}
4526			} else {
4527				parts_list = that.options.parts;
4528			}
4529
4530			// Add any parts to the internal parts list
4531			that.parts = {};
4532			$.each(parts_list, function(index, part) {
4533				if ($.colorpicker.parts[part]) {
4534					that.parts[part] = new $.colorpicker.parts[part](that);
4535				}
4536			});
4537
4538			if (!that.generated) {
4539				layout_parts = [];
4540
4541				$.each(that.options.layout, function(part, pos) {
4542					if (that.parts[part]) {
4543						layout_parts.push({
4544							'part': part,
4545							'pos':  pos
4546						});
4547					}
4548				});
4549
4550				table = $(_layoutTable(layout_parts, function(cell, x, y) {
4551					classes = ['ui-colorpicker-' + cell.part + '-container'];
4552
4553					if (x > 0) {
4554						classes.push('ui-colorpicker-padding-left');
4555					}
4556
4557					if (y > 0) {
4558						classes.push('ui-colorpicker-padding-top');
4559					}
4560
4561					return '<td  class="' + classes.join(' ') + '"'
4562						+ (cell.pos[2] > 1 ? ' colspan="' + cell.pos[2] + '"' : '')
4563						+ (cell.pos[3] > 1 ? ' rowspan="' + cell.pos[3] + '"' : '')
4564						+ ' valign="top"></td>';
4565				})).appendTo(that.dialog);
4566				if (that.options.inlineFrame) {
4567					table.addClass('ui-dialog-content ui-widget-content');
4568				}
4569
4570				that._initAllParts();
4571				that._updateAllParts();
4572				that.generated = true;
4573			}
4574		},
4575
4576		_effectGeneric: function (element, show, slide, fade, callback) {
4577			var that = this;
4578
4579			if ($.effects && $.effects[that.options.showAnim]) {
4580				element[show](that.options.showAnim, that.options.showOptions, that.options.duration, callback);
4581			} else {
4582				element[(that.options.showAnim === 'slideDown' ?
4583								slide
4584							:	(that.options.showAnim === 'fadeIn' ?
4585									fade
4586								:	show))]((that.options.showAnim ? that.options.duration : null), callback);
4587				if (!that.options.showAnim || !that.options.duration) {
4588					callback();
4589				}
4590			}
4591		},
4592
4593		_effectShow: function(element, callback) {
4594			this._effectGeneric(element, 'show', 'slideDown', 'fadeIn', callback);
4595		},
4596
4597		_effectHide: function(element, callback) {
4598			this._effectGeneric(element, 'hide', 'slideUp', 'fadeOut', callback);
4599		},
4600				
4601		open: function() {
4602			var that = this,
4603				offset,
4604				bottom, right,
4605				height, width,
4606				x, y,
4607				zIndex,
4608				element,
4609				position;
4610
4611			if (!that.opened) {
4612				that._generate();
4613				
4614				if (that.element.is(':hidden')) {
4615					element = $('<div/>').insertBefore(that.element);
4616				} else {
4617					element = that.element;
4618				}			
4619				
4620				if (that.element.is(':hidden')) {
4621					element.remove();
4622				}
4623				
4624				// Automatically find highest z-index.
4625				zIndex = 0;
4626				$(that.element[0]).parents().each(function() {
4627					var z = $(this).css('z-index');
4628					if ((typeof(z) === 'number' || typeof(z) === 'string') && z !== '' && !isNaN(z)) {
4629						if (z > zIndex) {
4630							zIndex = parseInt(z, 10);
4631							return false;
4632						}
4633					}
4634					else {
4635						$(this).siblings().each(function() {
4636							var z = $(this).css('z-index');
4637							if ((typeof(z) === 'number' || typeof(z) === 'string') && z !== '' && !isNaN(z)) {
4638								if (z > zIndex) {
4639									zIndex = parseInt(z, 10);
4640								}
4641							}
4642						});
4643					}
4644				});
4645
4646				zIndex += 2;
4647				that.dialog.css('z-index', zIndex);
4648								
4649				if (that.options.modal) {
4650					that.overlay = $('<div class="ui-widget-overlay"></div>').appendTo('body').css('z-index', zIndex - 1);										
4651
4652					if (that.events.window_resize !== null) {
4653						$(window).unbind('resize', that.events.window_resize);					
4654					}
4655					
4656					that.events.window_resize = function() {
4657						if (that.overlay) {
4658							that.overlay.width($(document).width());
4659							that.overlay.height($(document).height());					
4660						}
4661					},
4662															
4663					$(window).bind('resize', that.events.window_resize);
4664					that.events.window_resize();			
4665				}
4666
4667				that._effectShow(this.dialog);
4668
4669				if (that.options.position) {
4670					position = $.extend({}, that.options.position);
4671					if (position.of === 'element') {
4672						position.of = element;
4673					}
4674				} else {
4675					position = {
4676						my:			'left top',
4677						at:			'left bottom',
4678						of:			element,
4679						collision:	'flip'
4680					};
4681				}
4682				that.dialog.position(position);
4683				
4684				that.opened = true;
4685				that._callback('open', true);
4686
4687				// Without waiting for domready the width of the map is 0 and we
4688				// wind up with the cursor stuck in the upper left corner
4689				$(function() {
4690					that._repaintAllParts();
4691				});
4692			}
4693		},
4694
4695		close: function (cancel) {
4696			var that = this;
4697
4698			if (!that.opened) {
4699				return;
4700			}				
4701				
4702            if (cancel) {
4703				that.color = that.currentColor.copy();
4704                that._change();
4705                that._callback('cancel', true);
4706            } else {
4707				that.currentColor	= that.color.copy();
4708                that._callback('ok', true);
4709            }
4710			that.changed		= false;
4711
4712			if (that.overlay) {
4713				$(window).unbind('resize', that.events.window_resize);					
4714				that.overlay.remove();
4715			}
4716			
4717			// tear down the interface
4718			that._effectHide(that.dialog, function () {
4719				that.dialog.remove();
4720				that.dialog		= null;
4721				that.generated	= false;
4722
4723				that.opened		= false;
4724				that._callback('close', true);
4725			});
4726		},
4727
4728		destroy: function() {
4729			var that = this;
4730			if (that.events.document_click_html !== null) {
4731				$(document).undelegate('html', 'touchstart click', that.events.document_click_html);
4732			}
4733			
4734			if (that.events.document_keydown !== null) {
4735				$(document).unbind('keydown', that.events.document_keydown);
4736			}
4737			
4738			if (that.events.resizeOverlay !== null) {
4739				$(window).unbind('resize', that.events.resizeOverlay);					
4740			}			
4741			
4742			this.element.unbind();
4743
4744			if (this.overlay) {
4745				this.overlay.remove();
4746			}
4747			
4748			if (this.dialog !== null) {
4749				this.dialog.remove();
4750			}
4751			
4752			if (this.image !== null) {
4753				this.image.remove();
4754			}
4755
4756			if (this.button !== null) {
4757				this.button.remove();
4758			}
4759		},
4760
4761		_callback: function (callback, spaces) {
4762			var that = this,
4763				data,
4764				lab;
4765
4766			if (that.color.set) {
4767				data = {
4768					formatted: that._formatColor(that.options.colorFormat, that.color),
4769					colorPicker: that
4770				};
4771
4772				lab = that.color.getLAB();
4773				lab.a = (lab.a * 2) - 1;
4774				lab.b = (lab.b * 2) - 1;
4775
4776				if (spaces === true) {
4777					data.a		= that.color.getAlpha();
4778					data.rgb	= that.color.getRGB();
4779					data.hsv	= that.color.getHSV();
4780					data.cmyk	= that.color.getCMYK();
4781					data.hsl	= that.color.getHSL();
4782					data.lab	= lab;
4783				}
4784
4785				return that._trigger(callback, null, data);
4786			} else {
4787				return that._trigger(callback, null, {
4788					formatted: '',
4789					colorPicker: that
4790				});
4791			}
4792		},
4793
4794		_initAllParts: function () {
4795			$.each(this.parts, function (index, part) {
4796				if (part.init) {
4797					part.init();
4798				}
4799			});
4800		},
4801
4802		_updateAllParts: function () {
4803			$.each(this.parts, function (index, part) {
4804				if (part.update) {
4805					part.update();
4806				}
4807			});
4808		},
4809
4810		_repaintAllParts: function () {
4811			$.each(this.parts, function (index, part) {
4812				if (part.repaint) {
4813					part.repaint();
4814				}
4815			});
4816		},
4817		
4818		_change: function (stoppedChanging /* = true */) {
4819			// Limit color palette
4820			if (this.color.set && this.options.limit && $.colorpicker.limits[this.options.limit]) {
4821				$.colorpicker.limits[this.options.limit](this.color, this);
4822			}
4823
4824			// Set changed if different from starting color
4825			this.changed = !this.color.equals(this.currentColor);
4826
4827			// update input element content
4828			if (!this.inline) {
4829				if (!this.color.set) {
4830					if (this.element.val() !== '') {
4831						this.element.val('').change();
4832					}
4833				} else if (!this.color.equals(this._parseColor(this.element.val()))) {
4834					this.element.val(this._formatColor(this.options.colorFormat, this.color)).change();
4835				}
4836
4837				this._setImageBackground();
4838			}
4839
4840			// Set the alt field
4841			this._setAltField();
4842
4843			// update color option
4844			this.options.color = this.color.set ? this.color.toCSS() : '';
4845
4846			if (this.opened) {
4847				this._repaintAllParts();
4848			}
4849
4850			// callbacks
4851			this._callback('select');
4852
4853			if (typeof stoppedChanging === 'undefined' ? true : !!stoppedChanging) {
4854				this._callback('stop');
4855			}
4856		},
4857
4858		// This will be deprecated by jQueryUI 1.9 widget
4859		_hoverable: function (e) {
4860			e.hover(function () {
4861				e.addClass("ui-state-hover");
4862			}, function () {
4863				e.removeClass("ui-state-hover");
4864			});
4865		},
4866
4867		// This will be deprecated by jQueryUI 1.9 widget
4868		_focusable: function (e) {
4869			e.focus(function () {
4870				e.addClass("ui-state-focus");
4871			}).blur(function () {
4872				e.removeClass("ui-state-focus");
4873			});
4874		},
4875
4876		_getRegional: function(name) {
4877			return $.colorpicker.regional[this.options.regional][name] !== undefined ?
4878				$.colorpicker.regional[this.options.regional][name] : $.colorpicker.regional[''][name];
4879        },
4880
4881		_getSwatches: function() {
4882			if (typeof(this.options.swatches) === 'string') {
4883				return $.colorpicker.swatches[this.options.swatches];
4884			}
4885
4886			if ($.isPlainObject(this.options.swatches)) {
4887				return this.options.swatches;
4888			}
4889
4890			return $.colorpicker.swatches.html;
4891		},
4892		
4893		_eachSwatch: function (callback) {
4894			var currentSwatches = this._getSwatches(),
4895				name;
4896			$.each(currentSwatches, function (nameOrIndex, swatch) {
4897				name = $.isArray(currentSwatches) ? swatch.name : nameOrIndex;
4898				return callback(name, swatch);
4899			});
4900		},
4901
4902		_getSwatch: function(name) {
4903			var swatch = false;
4904
4905			this._eachSwatch(function(swatchName, current) {
4906				if (swatchName.toLowerCase() == name.toLowerCase()) {
4907					swatch = current;
4908					return false;
4909				}
4910			});
4911
4912			return swatch;
4913        },
4914		
4915		_parseFormat: function(format, text) {
4916			var that = this,
4917				typeRegexps = {
4918					x:	function() {return '([0-9a-fA-F]{2})';}
4919				,	d:	function() {return '([12]?[0-9]{1,2})';}
4920				,	f:	function() {return '([0-9]*\\.?[0-9]*)';}
4921				,	p:	function() {return '([0-9]*\\.?[0-9]*)';}
4922				},
4923				typeConverters = {
4924					x:	function(v)	{return parseInt(v, 16) / 255.;}
4925				,	d:	function(v)	{return v / 255.;}
4926				,	f:	function(v)	{return v;}
4927				,	p:	function(v)	{return v * 0.01;}
4928				},
4929				setters = {
4930					r:	'setRGB'
4931				,	g:	'setRGB'
4932				,	b:	'setRGB'
4933				,	h:	'setHSV'
4934				,	s:	'setHSV'
4935				,	v:	'setHSV'
4936				,	c:	'setCMYK'
4937				,	m:	'setCMYK'
4938				,	y:	'setCMYK'
4939				,	k:	'setCMYK'
4940				,	L:	'setLAB'
4941				,	A:	'setLAB'
4942				,	B:	'setLAB'
4943				},
4944				setterChannels = {
4945					setRGB:		[ 'r', 'g', 'b']
4946				,	setHSV:		[ 'h', 's', 'v' ]
4947				,	setCMYK:	[ 'c', 'm', 'y', 'k' ]
4948				,	setLAB:		[ 'L', 'A', 'B' ]
4949				},
4950				channels = [],
4951				converters = [],						
4952				setter = null,
4953				color,
4954				pattern;
4955
4956			// Construct pattern
4957			pattern = format.replace(/[()\\^$.|?*+[\]]/g, function(m) {
4958				return '\\'+m;
4959			});
4960
4961
4962			pattern = pattern.replace(/\\?[argbhsvcmykLAB][xdfp]/g, function(variable) {
4963				if (variable.match(/^\\/)) {
4964					return variable.slice(1);
4965				}
4966
4967				var channel = variable.charAt(0),
4968					type = variable.charAt(1);
4969
4970				channels.push(channel);
4971				converters.push(typeConverters[type]);
4972				if (setters[channel]) {
4973					setter = setters[channel];
4974				}
4975
4976				return typeRegexps[type]();
4977			});
4978
4979			if (setter) {
4980				var values = text.match(new RegExp(pattern));
4981				if (values) {
4982					var args = [],
4983						channelIndex;
4984					
4985					values.shift();
4986									
4987					$.each(setterChannels[setter], function(index, channel) {
4988						channelIndex = $.inArray(channel, channels);
4989						args[index] = converters[channelIndex](values[channelIndex]);
4990					});
4991
4992					color = new $.colorpicker.Color();
4993					color[setter].apply(color, args);
4994				}
4995			}
4996			
4997			return color;
4998		},
4999
5000        _parseColor: function(text) {
5001            var that = this,
5002				color;
5003		
5004			var formats = $.isArray(that.options.colorFormat)
5005					? that.options.colorFormat
5006					: [ that.options.colorFormat ];
5007
5008			$.each(formats, function(index, format) {
5009				if ($.colorpicker.parsers[format]) {
5010					color = $.colorpicker.parsers[format](text, that);
5011				} else {
5012					color = that._parseFormat(format, text);
5013				}
5014			
5015				if (color) {
5016					return false;
5017				}
5018			});
5019			
5020			if (!color) {
5021				// fallback; check all registered parsers
5022				$.each($.colorpicker.parsers, function(name, parser) {
5023					color = parser(text, that);
5024					if (color) {
5025						return false;
5026					}
5027				});
5028			}
5029
5030			if (color) {
5031				return color;
5032			}
5033
5034			return false;
5035        },
5036
5037		_exactName: function(color) {
5038			var name	= false;
5039
5040			this._eachSwatch(function(n, swatch) {
5041				if (color.equals(new $.colorpicker.Color(swatch.r, swatch.g, swatch.b))) {
5042					name = n;
5043					return false;
5044				}
5045			});
5046
5047			return name;
5048		},
5049
5050		_closestName: function(color) {
5051			var rgb			= color.getRGB(),
5052				distance	= null,
5053				name		= false,
5054				d;
5055
5056			this._eachSwatch(function(n, swatch) {
5057				d = color.distance(new $.colorpicker.Color(swatch.r, swatch.g, swatch.b));
5058				if (d < distance || distance === null) {
5059					name = n;
5060					if (d <= 1e-20) {	// effectively 0 by maximum rounding error
5061						return false;	// can't get much closer than 0
5062					}
5063					distance = d;	// safety net
5064				}
5065			});
5066
5067			return name;
5068		},
5069
5070		_formatColor: function (formats, color) {			
5071			var that		= this,
5072				text		= null,
5073				types		= {	'x':	function(v) {return _intToHex(v * 255);}
5074							,	'd':	function(v) {return Math.round(v * 255);}
5075							,	'f':	function(v) {return v;}
5076							,	'p':	function(v) {return v * 100.;}
5077							},
5078				channels	= color.getChannels();
5079
5080			if (!$.isArray(formats)) {
5081				formats = [formats];
5082			}
5083
5084			$.each(formats, function(index, format) {
5085				if ($.colorpicker.writers[format]) {
5086					text = $.colorpicker.writers[format](color, that);		
5087					return (text === false);
5088				} else {
5089					text = format.replace(/\\?[argbhsvcmykLAB][xdfp]/g, function(m) {
5090						if (m.match(/^\\/)) {
5091							return m.slice(1);
5092						}
5093						return types[m.charAt(1)](channels[m.charAt(0)]);
5094					});
5095					return false;
5096				}
5097			});
5098			
5099			return text;
5100		}
5101	});
5102	
5103	return $.vanderlee.colorpicker;
5104}));
5105;
5106;jQuery(function($) {
5107	$.colorpicker.swatchesNames['pantone'] = 'Pantone';
5108	$.colorpicker.swatches['pantone'] = [
5109		{name: '100', r: 0.956862745098039, g: 0.929411764705882, b: 0.486274509803922},
5110		{name: '101', r: 0.956862745098039, g: 0.929411764705882, b: 0.27843137254902},
5111		{name: '102', r: 0.976470588235294, g: 0.909803921568627, b: 0.0784313725490196},
5112		{name: '103', r: 0.776470588235294, g: 0.67843137254902, b: 0.0588235294117647},
5113		{name: '104', r: 0.67843137254902, g: 0.607843137254902, b: 0.0470588235294118},
5114		{name: '105', r: 0.509803921568627, g: 0.458823529411765, b: 0.0588235294117647},
5115		{name: '106', r: 0.968627450980392, g: 0.909803921568627, b: 0.349019607843137},
5116		{name: '107', r: 0.976470588235294, g: 0.898039215686275, b: 0.149019607843137},
5117		{name: '108', r: 0.976470588235294, g: 0.866666666666667, b: 0.0862745098039216},
5118		{name: '109', r: 0.976470588235294, g: 0.83921568627451, b: 0.0862745098039216},
5119		{name: '110', r: 0.847058823529412, g: 0.709803921568627, b: 0.0666666666666667},
5120		{name: '111', r: 0.666666666666667, g: 0.576470588235294, b: 0.0392156862745098},
5121		{name: '112', r: 0.6, g: 0.517647058823529, b: 0.0392156862745098},
5122		{name: '113', r: 0.976470588235294, g: 0.898039215686275, b: 0.356862745098039},
5123		{name: '114', r: 0.976470588235294, g: 0.886274509803922, b: 0.298039215686275},
5124		{name: '115', r: 0.976470588235294, g: 0.87843137254902, b: 0.298039215686275},
5125		{name: '116', r: 0.988235294117647, g: 0.819607843137255, b: 0.0862745098039216},
5126		{name: '116 2X', r: 0.968627450980392, g: 0.709803921568627, b: 0.0470588235294118},
5127		{name: '117', r: 0.776470588235294, g: 0.627450980392157, b: 0.0470588235294118},
5128		{name: '118', r: 0.666666666666667, g: 0.556862745098039, b: 0.0392156862745098},
5129		{name: '119', r: 0.537254901960784, g: 0.466666666666667, b: 0.0980392156862745},
5130		{name: '120', r: 0.976470588235294, g: 0.886274509803922, b: 0.498039215686275},
5131		{name: '1205', r: 0.968627450980392, g: 0.909803921568627, b: 0.666666666666667},
5132		{name: '121', r: 0.976470588235294, g: 0.87843137254902, b: 0.43921568627451},
5133		{name: '1215', r: 0.976470588235294, g: 0.87843137254902, b: 0.549019607843137},
5134		{name: '122', r: 0.988235294117647, g: 0.847058823529412, b: 0.337254901960784},
5135		{name: '1225', r: 1, g: 0.8, b: 0.286274509803922},
5136		{name: '123', r: 1, g: 0.776470588235294, b: 0.117647058823529},
5137		{name: '1235', r: 0.988235294117647, g: 0.709803921568627, b: 0.0784313725490196},
5138		{name: '124', r: 0.87843137254902, g: 0.666666666666667, b: 0.0588235294117647},
5139		{name: '1245', r: 0.749019607843137, g: 0.568627450980392, b: 0.0470588235294118},
5140		{name: '125', r: 0.709803921568627, g: 0.549019607843137, b: 0.0392156862745098},
5141		{name: '1255', r: 0.63921568627451, g: 0.498039215686275, b: 0.0784313725490196},
5142		{name: '126', r: 0.63921568627451, g: 0.509803921568627, b: 0.0196078431372549},
5143		{name: '1265', r: 0.486274509803922, g: 0.388235294117647, b: 0.0862745098039216},
5144		{name: '127', r: 0.956862745098039, g: 0.886274509803922, b: 0.529411764705882},
5145		{name: '128', r: 0.956862745098039, g: 0.858823529411765, b: 0.376470588235294},
5146		{name: '129', r: 0.949019607843137, g: 0.819607843137255, b: 0.23921568627451},
5147		{name: '130', r: 0.917647058823529, g: 0.686274509803922, b: 0.0588235294117647},
5148		{name: '130 2X', r: 0.886274509803922, g: 0.568627450980392, b: 0},
5149		{name: '131', r: 0.776470588235294, g: 0.576470588235294, b: 0.0392156862745098},
5150		{name: '132', r: 0.619607843137255, g: 0.486274509803922, b: 0.0392156862745098},
5151		{name: '133', r: 0.43921568627451, g: 0.356862745098039, b: 0.0392156862745098},
5152		{name: '134', r: 1, g: 0.847058823529412, b: 0.498039215686275},
5153		{name: '1345', r: 1, g: 0.83921568627451, b: 0.568627450980392},
5154		{name: '135', r: 0.988235294117647, g: 0.788235294117647, b: 0.388235294117647},
5155		{name: '1355', r: 0.988235294117647, g: 0.807843137254902, b: 0.529411764705882},
5156		{name: '136', r: 0.988235294117647, g: 0.749019607843137, b: 0.286274509803922},
5157		{name: '1365', r: 0.988235294117647, g: 0.729411764705882, b: 0.368627450980392},
5158		{name: '137', r: 0.988235294117647, g: 0.63921568627451, b: 0.0666666666666667},
5159		{name: '1375', r: 0.976470588235294, g: 0.607843137254902, b: 0.0470588235294118},
5160		{name: '138', r: 0.847058823529412, g: 0.549019607843137, b: 0.00784313725490196},
5161		{name: '1385', r: 0.8, g: 0.47843137254902, b: 0.00784313725490196},
5162		{name: '139', r: 0.686274509803922, g: 0.458823529411765, b: 0.0196078431372549},
5163		{name: '1395', r: 0.6, g: 0.376470588235294, b: 0.0274509803921569},
5164		{name: '140', r: 0.47843137254902, g: 0.356862745098039, b: 0.0666666666666667},
5165		{name: '1405', r: 0.419607843137255, g: 0.27843137254902, b: 0.0784313725490196},
5166		{name: '141', r: 0.949019607843137, g: 0.807843137254902, b: 0.407843137254902},
5167		{name: '142', r: 0.949019607843137, g: 0.749019607843137, b: 0.286274509803922},
5168		{name: '143', r: 0.937254901960784, g: 0.698039215686274, b: 0.176470588235294},
5169		{name: '144', r: 0.886274509803922, g: 0.549019607843137, b: 0.0196078431372549},
5170		{name: '145', r: 0.776470588235294, g: 0.498039215686275, b: 0.0274509803921569},
5171		{name: '146', r: 0.619607843137255, g: 0.419607843137255, b: 0.0196078431372549},
5172		{name: '147', r: 0.447058823529412, g: 0.368627450980392, b: 0.149019607843137},
5173		{name: '148', r: 1, g: 0.83921568627451, b: 0.607843137254902},
5174		{name: '1485', r: 1, g: 0.717647058823529, b: 0.466666666666667},
5175		{name: '149', r: 0.988235294117647, g: 0.8, b: 0.576470588235294},
5176		{name: '1495', r: 1, g: 0.6, b: 0.247058823529412},
5177		{name: '150', r: 0.988235294117647, g: 0.67843137254902, b: 0.337254901960784},
5178		{name: '1505', r: 0.956862745098039, g: 0.486274509803922, b: 0},
5179		{name: '151', r: 0.968627450980392, g: 0.498039215686275, b: 0},
5180		{name: '152', r: 0.866666666666667, g: 0.458823529411765, b: 0},
5181		{name: '1525', r: 0.709803921568627, g: 0.329411764705882, b: 0},
5182		{name: '153', r: 0.737254901960784, g: 0.427450980392157, b: 0.0392156862745098},
5183		{name: '1535', r: 0.549019607843137, g: 0.266666666666667, b: 0},
5184		{name: '154', r: 0.6, g: 0.349019607843137, b: 0.0196078431372549},
5185		{name: '1545', r: 0.27843137254902, g: 0.133333333333333, b: 0},
5186		{name: '155', r: 0.956862745098039, g: 0.858823529411765, b: 0.666666666666667},
5187		{name: '1555', r: 0.976470588235294, g: 0.749019607843137, b: 0.619607843137255},
5188		{name: '156', r: 0.949019607843137, g: 0.776470588235294, b: 0.549019607843137},
5189		{name: '1565', r: 0.988235294117647, g: 0.647058823529412, b: 0.466666666666667},
5190		{name: '157', r: 0.929411764705882, g: 0.627450980392157, b: 0.309803921568627},
5191		{name: '1575', r: 0.988235294117647, g: 0.529411764705882, b: 0.266666666666667},
5192		{name: '158', r: 0.909803921568627, g: 0.458823529411765, b: 0.0666666666666667},
5193		{name: '1585', r: 0.976470588235294, g: 0.419607843137255, b: 0.0274509803921569},
5194		{name: '159', r: 0.776470588235294, g: 0.376470588235294, b: 0.0196078431372549},
5195		{name: '1595', r: 0.819607843137255, g: 0.356862745098039, b: 0.0196078431372549},
5196		{name: '160', r: 0.619607843137255, g: 0.329411764705882, b: 0.0392156862745098},
5197		{name: '1605', r: 0.627450980392157, g: 0.309803921568627, b: 0.0666666666666667},
5198		{name: '161', r: 0.388235294117647, g: 0.227450980392157, b: 0.0666666666666667},
5199		{name: '1615', r: 0.517647058823529, g: 0.247058823529412, b: 0.0588235294117647},
5200		{name: '162', r: 0.976470588235294, g: 0.776470588235294, b: 0.666666666666667},
5201		{name: '1625', r: 0.976470588235294, g: 0.647058823529412, b: 0.549019607843137},
5202		{name: '163', r: 0.988235294117647, g: 0.619607843137255, b: 0.43921568627451},
5203		{name: '1635', r: 0.976470588235294, g: 0.556862745098039, b: 0.427450980392157},
5204		{name: '164', r: 0.988235294117647, g: 0.498039215686275, b: 0.247058823529412},
5205		{name: '1645', r: 0.976470588235294, g: 0.447058823529412, b: 0.258823529411765},
5206		{name: '165', r: 0.976470588235294, g: 0.388235294117647, b: 0.00784313725490196},
5207		{name: '165 2X', r: 0.917647058823529, g: 0.309803921568627, b: 0},
5208		{name: '1655', r: 0.976470588235294, g: 0.337254901960784, b: 0.00784313725490196},
5209		{name: '166', r: 0.866666666666667, g: 0.349019607843137, b: 0},
5210		{name: '1665', r: 0.866666666666667, g: 0.309803921568627, b: 0.0196078431372549},
5211		{name: '167', r: 0.737254901960784, g: 0.309803921568627, b: 0.0274509803921569},
5212		{name: '1675', r: 0.647058823529412, g: 0.247058823529412, b: 0.0588235294117647},
5213		{name: '168', r: 0.427450980392157, g: 0.188235294117647, b: 0.0666666666666667},
5214		{name: '1685', r: 0.517647058823529, g: 0.207843137254902, b: 0.0666666666666667},
5215		{name: '169', r: 0.976470588235294, g: 0.729411764705882, b: 0.666666666666667},
5216		{name: '170', r: 0.976470588235294, g: 0.537254901960784, b: 0.447058823529412},
5217		{name: '171', r: 0.976470588235294, g: 0.376470588235294, b: 0.227450980392157},
5218		{name: '172', r: 0.968627450980392, g: 0.286274509803922, b: 0.00784313725490196},
5219		{name: '173', r: 0.819607843137255, g: 0.266666666666667, b: 0.0784313725490196},
5220		{name: '174', r: 0.576470588235294, g: 0.2, b: 0.0666666666666667},
5221		{name: '175', r: 0.427450980392157, g: 0.2, b: 0.129411764705882},
5222		{name: '176', r: 0.976470588235294, g: 0.686274509803922, b: 0.67843137254902},
5223		{name: '1765', r: 0.976470588235294, g: 0.619607843137255, b: 0.63921568627451},
5224		{name: '1767', r: 0.976470588235294, g: 0.698039215686274, b: 0.717647058823529},
5225		{name: '177', r: 0.976470588235294, g: 0.509803921568627, b: 0.498039215686275},
5226		{name: '1775', r: 0.976470588235294, g: 0.517647058823529, b: 0.556862745098039},
5227		{name: '1777', r: 0.988235294117647, g: 0.4, b: 0.458823529411765},
5228		{name: '178', r: 0.976470588235294, g: 0.368627450980392, b: 0.349019607843137},
5229		{name: '1785', r: 0.988235294117647, g: 0.309803921568627, b: 0.349019607843137},
5230		{name: '1787', r: 0.956862745098039, g: 0.247058823529412, b: 0.309803921568627},
5231		{name: '1788', r: 0.937254901960784, g: 0.168627450980392, b: 0.176470588235294},
5232		{name: '1788 2X', r: 0.83921568627451, g: 0.129411764705882, b: 0},
5233		{name: '179', r: 0.886274509803922, g: 0.23921568627451, b: 0.156862745098039},
5234		{name: '1795', r: 0.83921568627451, g: 0.156862745098039, b: 0.156862745098039},
5235		{name: '1797', r: 0.8, g: 0.176470588235294, b: 0.188235294117647},
5236		{name: '180', r: 0.756862745098039, g: 0.219607843137255, b: 0.156862745098039},
5237		{name: '1805', r: 0.686274509803922, g: 0.149019607843137, b: 0.149019607843137},
5238		{name: '1807', r: 0.627450980392157, g: 0.188235294117647, b: 0.2},
5239		{name: '181', r: 0.486274509803922, g: 0.176470588235294, b: 0.137254901960784},
5240		{name: '1810', r: 0.486274509803922, g: 0.129411764705882, b: 0.117647058823529},
5241		{name: '1817', r: 0.356862745098039, g: 0.176470588235294, b: 0.156862745098039},
5242		{name: '182', r: 0.976470588235294, g: 0.749019607843137, b: 0.756862745098039},
5243		{name: '183', r: 0.988235294117647, g: 0.549019607843137, b: 0.6},
5244		{name: '184', r: 0.988235294117647, g: 0.368627450980392, b: 0.447058823529412},
5245		{name: '185', r: 0.909803921568627, g: 0.0666666666666667, b: 0.176470588235294},
5246		{name: '185 2X', r: 0.819607843137255, g: 0.0862745098039216, b: 0},
5247		{name: '186', r: 0.807843137254902, g: 0.0666666666666667, b: 0.149019607843137},
5248		{name: '187', r: 0.686274509803922, g: 0.117647058823529, b: 0.176470588235294},
5249		{name: '188', r: 0.486274509803922, g: 0.129411764705882, b: 0.156862745098039},
5250		{name: '189', r: 1, g: 0.63921568627451, b: 0.698039215686274},
5251		{name: '1895', r: 0.988235294117647, g: 0.749019607843137, b: 0.788235294117647},
5252		{name: '190', r: 0.988235294117647, g: 0.458823529411765, b: 0.556862745098039},
5253		{name: '1905', r: 0.988235294117647, g: 0.607843137254902, b: 0.698039215686274},
5254		{name: '191', r: 0.956862745098039, g: 0.27843137254902, b: 0.419607843137255},
5255		{name: '1915', r: 0.956862745098039, g: 0.329411764705882, b: 0.486274509803922},
5256		{name: '192', r: 0.898039215686275, g: 0.0196078431372549, b: 0.227450980392157},
5257		{name: '1925', r: 0.87843137254902, g: 0.0274509803921569, b: 0.27843137254902},
5258		{name: '193', r: 0.768627450980392, g: 0, b: 0.262745098039216},
5259		{name: '1935', r: 0.756862745098039, g: 0.0196078431372549, b: 0.219607843137255},
5260		{name: '194', r: 0.6, g: 0.129411764705882, b: 0.207843137254902},
5261		{name: '1945', r: 0.658823529411765, g: 0.0470588235294118, b: 0.207843137254902},
5262		{name: '1955', r: 0.576470588235294, g: 0.0862745098039216, b: 0.219607843137255},
5263		{name: '196', r: 0.980392156862745, g: 0.835294117647059, b: 0.882352941176471},
5264		{name: '197', r: 0.964705882352941, g: 0.647058823529412, b: 0.745098039215686},
5265		{name: '198', r: 0.937254901960784, g: 0.356862745098039, b: 0.517647058823529},
5266		{name: '199', r: 0.627450980392157, g: 0.152941176470588, b: 0.294117647058824},
5267		{name: '200', r: 0.768627450980392, g: 0.117647058823529, b: 0.227450980392157},
5268		{name: '201', r: 0.63921568627451, g: 0.149019607843137, b: 0.219607843137255},
5269		{name: '202', r: 0.549019607843137, g: 0.149019607843137, b: 0.2},
5270		{name: '203', r: 0.949019607843137, g: 0.686274509803922, b: 0.756862745098039},
5271		{name: '204', r: 0.929411764705882, g: 0.47843137254902, b: 0.619607843137255},
5272		{name: '205', r: 0.898039215686275, g: 0.298039215686275, b: 0.486274509803922},
5273		{name: '206', r: 0.827450980392157, g: 0.0196078431372549, b: 0.27843137254902},
5274		{name: '207', r: 0.752941176470588, g: 0, b: 0.305882352941176},
5275		{name: '208', r: 0.556862745098039, g: 0.137254901960784, b: 0.266666666666667},
5276		{name: '209', r: 0.458823529411765, g: 0.149019607843137, b: 0.23921568627451},
5277		{name: '210', r: 1, g: 0.627450980392157, b: 0.749019607843137},
5278		{name: '211', r: 1, g: 0.466666666666667, b: 0.658823529411765},
5279		{name: '212', r: 0.976470588235294, g: 0.309803921568627, b: 0.556862745098039},
5280		{name: '213', r: 0.917647058823529, g: 0.0588235294117647, b: 0.419607843137255},
5281		{name: '214', r: 0.8, g: 0.00784313725490196, b: 0.337254901960784},
5282		{name: '215', r: 0.647058823529412, g: 0.0196078431372549, b: 0.266666666666667},
5283		{name: '216', r: 0.486274509803922, g: 0.117647058823529, b: 0.247058823529412},
5284		{name: '217', r: 0.956862745098039, g: 0.749019607843137, b: 0.819607843137255},
5285		{name: '218', r: 0.929411764705882, g: 0.447058823529412, b: 0.666666666666667},
5286		{name: '219', r: 0.886274509803922, g: 0.156862745098039, b: 0.509803921568627},
5287		{name: '220', r: 0.666666666666667, g: 0, b: 0.309803921568627},
5288		{name: '221', r: 0.576470588235294, g: 0, b: 0.258823529411765},
5289		{name: '222', r: 0.43921568627451, g: 0.0980392156862745, b: 0.23921568627451},
5290		{name: '223', r: 0.976470588235294, g: 0.576470588235294, b: 0.768627450980392},
5291		{name: '224', r: 0.956862745098039, g: 0.419607843137255, b: 0.686274509803922},
5292		{name: '225', r: 0.929411764705882, g: 0.156862745098039, b: 0.576470588235294},
5293		{name: '226', r: 0.83921568627451, g: 0.00784313725490196, b: 0.43921568627451},
5294		{name: '227', r: 0.67843137254902, g: 0, b: 0.356862745098039},
5295		{name: '228', r: 0.549019607843137, g: 0, b: 0.298039215686275},
5296		{name: '229', r: 0.427450980392157, g: 0.129411764705882, b: 0.247058823529412},
5297		{name: '230', r: 1, g: 0.627450980392157, b: 0.8},
5298		{name: '231', r: 0.988235294117647, g: 0.43921568627451, b: 0.729411764705882},
5299		{name: '232', r: 0.956862745098039, g: 0.247058823529412, b: 0.647058823529412},
5300		{name: '233', r: 0.807843137254902, g: 0, b: 0.486274509803922},
5301		{name: '234', r: 0.666666666666667, g: 0, b: 0.4},
5302		{name: '235', r: 0.556862745098039, g: 0.0196078431372549, b: 0.329411764705882},
5303		{name: '236', r: 0.976470588235294, g: 0.686274509803922, b: 0.827450980392157},
5304		{name: '2365', r: 0.968627450980392, g: 0.768627450980392, b: 0.847058823529412},
5305		{name: '237', r: 0.956862745098039, g: 0.517647058823529, b: 0.768627450980392},
5306		{name: '2375', r: 0.917647058823529, g: 0.419607843137255, b: 0.749019607843137},
5307		{name: '238', r: 0.929411764705882, g: 0.309803921568627, b: 0.686274509803922},
5308		{name: '2385', r: 0.858823529411765, g: 0.156862745098039, b: 0.647058823529412},
5309		{name: '239', r: 0.87843137254902, g: 0.129411764705882, b: 0.619607843137255},
5310		{name: '2395', r: 0.768627450980392, g: 0, b: 0.549019607843137},
5311		{name: '240', r: 0.768627450980392, g: 0.0588235294117647, b: 0.537254901960784},
5312		{name: '2405', r: 0.658823529411765, g: 0, b: 0.47843137254902},
5313		{name: '241', r: 0.67843137254902, g: 0, b: 0.458823529411765},
5314		{name: '2415', r: 0.607843137254902, g: 0, b: 0.43921568627451},
5315		{name: '242', r: 0.486274509803922, g: 0.109803921568627, b: 0.317647058823529},
5316		{name: '2425', r: 0.529411764705882, g: 0, b: 0.356862745098039},
5317		{name: '243', r: 0.949019607843137, g: 0.729411764705882, b: 0.847058823529412},
5318		{name: '244', r: 0.929411764705882, g: 0.627450980392157, b: 0.827450980392157},
5319		{name: '245', r: 0.909803921568627, g: 0.498039215686275, b: 0.788235294117647},
5320		{name: '246', r: 0.8, g: 0, b: 0.627450980392157},
5321		{name: '247', r: 0.717647058823529, g: 0, b: 0.556862745098039},
5322		{name: '248', r: 0.63921568627451, g: 0.0196078431372549, b: 0.498039215686275},
5323		{name: '249', r: 0.498039215686275, g: 0.156862745098039, b: 0.376470588235294},
5324		{name: '250', r: 0.929411764705882, g: 0.768627450980392, b: 0.866666666666667},
5325		{name: '251', r: 0.886274509803922, g: 0.619607843137255, b: 0.83921568627451},
5326		{name: '252', r: 0.827450980392157, g: 0.419607843137255, b: 0.776470588235294},
5327		{name: '253', r: 0.686274509803922, g: 0.137254901960784, b: 0.647058823529412},
5328		{name: '254', r: 0.627450980392157, g: 0.176470588235294, b: 0.588235294117647},
5329		{name: '255', r: 0.466666666666667, g: 0.176470588235294, b: 0.419607843137255},
5330		{name: '256', r: 0.898039215686275, g: 0.768627450980392, b: 0.83921568627451},
5331		{name: '2562', r: 0.847058823529412, g: 0.658823529411765, b: 0.847058823529412},
5332		{name: '2563', r: 0.819607843137255, g: 0.627450980392157, b: 0.8},
5333		{name: '2567', r: 0.749019607843137, g: 0.576470588235294, b: 0.8},
5334		{name: '257', r: 0.827450980392157, g: 0.647058823529412, b: 0.788235294117647},
5335		{name: '2572', r: 0.776470588235294, g: 0.529411764705882, b: 0.819607843137255},
5336		{name: '2573', r: 0.729411764705882, g: 0.486274509803922, b: 0.737254901960784},
5337		{name: '2577', r: 0.666666666666667, g: 0.447058823529412, b: 0.749019607843137},
5338		{name: '258', r: 0.607843137254902, g: 0.309803921568627, b: 0.588235294117647},
5339		{name: '2582', r: 0.666666666666667, g: 0.27843137254902, b: 0.729411764705882},
5340		{name: '2583', r: 0.619607843137255, g: 0.309803921568627, b: 0.647058823529412},
5341		{name: '2587', r: 0.556862745098039, g: 0.27843137254902, b: 0.67843137254902},
5342		{name: '259', r: 0.447058823529412, g: 0.0862745098039216, b: 0.419607843137255},
5343		{name: '2592', r: 0.576470588235294, g: 0.0588235294117647, b: 0.647058823529412},
5344		{name: '2593', r: 0.529411764705882, g: 0.168627450980392, b: 0.576470588235294},
5345		{name: '2597', r: 0.4, g: 0, b: 0.549019607843137},
5346		{name: '260', r: 0.407843137254902, g: 0.117647058823529, b: 0.356862745098039},
5347		{name: '2602', r: 0.509803921568627, g: 0.0470588235294118, b: 0.556862745098039},
5348		{name: '2603', r: 0.43921568627451, g: 0.0784313725490196, b: 0.47843137254902},
5349		{name: '2607', r: 0.356862745098039, g: 0.00784313725490196, b: 0.47843137254902},
5350		{name: '261', r: 0.368627450980392, g: 0.129411764705882, b: 0.329411764705882},
5351		{name: '2612', r: 0.43921568627451, g: 0.117647058823529, b: 0.447058823529412},
5352		{name: '2613', r: 0.4, g: 0.0666666666666667, b: 0.427450980392157},
5353		{name: '2617', r: 0.337254901960784, g: 0.0470588235294118, b: 0.43921568627451},
5354		{name: '262', r: 0.329411764705882, g: 0.137254901960784, b: 0.266666666666667},
5355		{name: '2622', r: 0.376470588235294, g: 0.176470588235294, b: 0.349019607843137},
5356		{name: '2623', r: 0.356862745098039, g: 0.0980392156862745, b: 0.368627450980392},
5357		{name: '2627', r: 0.298039215686275, g: 0.0784313725490196, b: 0.368627450980392},
5358		{name: '263', r: 0.87843137254902, g: 0.807843137254902, b: 0.87843137254902},
5359		{name: '2635', r: 0.788235294117647, g: 0.67843137254902, b: 0.847058823529412},
5360		{name: '264', r: 0.776470588235294, g: 0.666666666666667, b: 0.858823529411765},
5361		{name: '2645', r: 0.709803921568627, g: 0.568627450980392, b: 0.819607843137255},
5362		{name: '265', r: 0.588235294117647, g: 0.388235294117647, b: 0.768627450980392},
5363		{name: '2655', r: 0.607843137254902, g: 0.427450980392157, b: 0.776470588235294},
5364		{name: '266', r: 0.427450980392157, g: 0.156862745098039, b: 0.666666666666667},
5365		{name: '2665', r: 0.537254901960784, g: 0.309803921568627, b: 0.749019607843137},
5366		{name: '267', r: 0.349019607843137, g: 0.0666666666666667, b: 0.556862745098039},
5367		{name: '268', r: 0.309803921568627, g: 0.129411764705882, b: 0.43921568627451},
5368		{name: '2685', r: 0.337254901960784, g: 0, b: 0.549019607843137},
5369		{name: '269', r: 0.266666666666667, g: 0.137254901960784, b: 0.349019607843137},
5370		{name: '2695', r: 0.266666666666667, g: 0.137254901960784, b: 0.368627450980392},
5371		{name: '270', r: 0.729411764705882, g: 0.686274509803922, b: 0.827450980392157},
5372		{name: '2705', r: 0.67843137254902, g: 0.619607843137255, b: 0.827450980392157},
5373		{name: '2706', r: 0.819607843137255, g: 0.807843137254902, b: 0.866666666666667},
5374		{name: '2707', r: 0.749019607843137, g: 0.819607843137255, b: 0.898039215686275},
5375		{name: '2708', r: 0.686274509803922, g: 0.737254901960784, b: 0.858823529411765},
5376		{name: '271', r: 0.619607843137255, g: 0.568627450980392, b: 0.776470588235294},
5377		{name: '2715', r: 0.576470588235294, g: 0.47843137254902, b: 0.8},
5378		{name: '2716', r: 0.647058823529412, g: 0.627450980392157, b: 0.83921568627451},
5379		{name: '2717', r: 0.647058823529412, g: 0.729411764705882, b: 0.87843137254902},
5380		{name: '2718', r: 0.356862745098039, g: 0.466666666666667, b: 0.8},
5381		{name: '272', r: 0.537254901960784, g: 0.466666666666667, b: 0.729411764705882},
5382		{name: '2725', r: 0.447058823529412, g: 0.317647058823529, b: 0.737254901960784},
5383		{name: '2726', r: 0.4, g: 0.337254901960784, b: 0.737254901960784},
5384		{name: '2727', r: 0.368627450980392, g: 0.407843137254902, b: 0.768627450980392},
5385		{name: '2728', r: 0.188235294117647, g: 0.266666666666667, b: 0.709803921568627},
5386		{name: '273', r: 0.219607843137255, g: 0.0980392156862745, b: 0.47843137254902},
5387		{name: '2735', r: 0.309803921568627, g: 0, b: 0.576470588235294},
5388		{name: '2736', r: 0.286274509803922, g: 0.188235294117647, b: 0.67843137254902},
5389		{name: '2738', r: 0.176470588235294, g: 0, b: 0.556862745098039},
5390		{name: '274', r: 0.168627450980392, g: 0.0666666666666667, b: 0.4},
5391		{name: '2745', r: 0.247058823529412, g: 0, b: 0.466666666666667},
5392		{name: '2746', r: 0.247058823529412, g: 0.156862745098039, b: 0.576470588235294},
5393		{name: '2747', r: 0.109803921568627, g: 0.0784313725490196, b: 0.419607843137255},
5394		{name: '2748', r: 0.117647058823529, g: 0.109803921568627, b: 0.466666666666667},
5395		{name: '275', r: 0.149019607843137, g: 0.0588235294117647, b: 0.32941176470
53955882},
5396		{name: '2755', r: 0.207843137254902, g: 0, b: 0.427450980392157},
5397		{name: '2756', r: 0.2, g: 0.156862745098039, b: 0.458823529411765},
5398		{name: '2757', r: 0.0784313725490196, g: 0.0862745098039216, b: 0.329411764705882},
5399		{name: '2758', r: 0.0980392156862745, g: 0.129411764705882, b: 0.407843137254902},
5400		{name: '276', r: 0.168627450980392, g: 0.129411764705882, b: 0.27843137254902},
5401		{name: '2765', r: 0.168627450980392, g: 0.0470588235294118, b: 0.337254901960784},
5402		{name: '2766', r: 0.168627450980392, g: 0.149019607843137, b: 0.356862745098039},
5403		{name: '2767', r: 0.0784313725490196, g: 0.129411764705882, b: 0.23921568627451},
5404		{name: '2768', r: 0.0666666666666667, g: 0.129411764705882, b: 0.317647058823529},
5405		{name: '277', r: 0.709803921568627, g: 0.819607843137255, b: 0.909803921568627},
5406		{name: '278', r: 0.6, g: 0.729411764705882, b: 0.866666666666667},
5407		{name: '279', r: 0.4, g: 0.537254901960784, b: 0.8},
5408		{name: '280', r: 0, g: 0.168627450980392, b: 0.498039215686275},
5409		{name: '281', r: 0, g: 0.156862745098039, b: 0.407843137254902},
5410		{name: '282', r: 0, g: 0.149019607843137, b: 0.329411764705882},
5411		{name: '283', r: 0.607843137254902, g: 0.768627450980392, b: 0.886274509803922},
5412		{name: '284', r: 0.458823529411765, g: 0.666666666666667, b: 0.858823529411765},
5413		{name: '285', r: 0.227450980392157, g: 0.458823529411765, b: 0.768627450980392},
5414		{name: '286', r: 0, g: 0.219607843137255, b: 0.658823529411765},
5415		{name: '287', r: 0, g: 0.219607843137255, b: 0.576470588235294},
5416		{name: '288', r: 0, g: 0.2, b: 0.498039215686275},
5417		{name: '289', r: 0, g: 0.149019607843137, b: 0.286274509803922},
5418		{name: '290', r: 0.768627450980392, g: 0.847058823529412, b: 0.886274509803922},
5419		{name: '2905', r: 0.576470588235294, g: 0.776470588235294, b: 0.87843137254902},
5420		{name: '291', r: 0.658823529411765, g: 0.807843137254902, b: 0.886274509803922},
5421		{name: '2915', r: 0.376470588235294, g: 0.686274509803922, b: 0.866666666666667},
5422		{name: '292', r: 0.458823529411765, g: 0.698039215686274, b: 0.866666666666667},
5423		{name: '2925', r: 0, g: 0.556862745098039, b: 0.83921568627451},
5424		{name: '293', r: 0, g: 0.317647058823529, b: 0.729411764705882},
5425		{name: '2935', r: 0, g: 0.356862745098039, b: 0.749019607843137},
5426		{name: '294', r: 0, g: 0.247058823529412, b: 0.529411764705882},
5427		{name: '2945', r: 0, g: 0.329411764705882, b: 0.627450980392157},
5428		{name: '295', r: 0, g: 0.219607843137255, b: 0.419607843137255},
5429		{name: '2955', r: 0, g: 0.23921568627451, b: 0.419607843137255},
5430		{name: '296', r: 0, g: 0.176470588235294, b: 0.27843137254902},
5431		{name: '2965', r: 0, g: 0.2, b: 0.298039215686275},
5432		{name: '297', r: 0.509803921568627, g: 0.776470588235294, b: 0.886274509803922},
5433		{name: '2975', r: 0.729411764705882, g: 0.87843137254902, b: 0.886274509803922},
5434		{name: '298', r: 0.317647058823529, g: 0.709803921568627, b: 0.87843137254902},
5435		{name: '2985', r: 0.317647058823529, g: 0.749019607843137, b: 0.886274509803922},
5436		{name: '299', r: 0, g: 0.63921568627451, b: 0.866666666666667},
5437		{name: '2995', r: 0, g: 0.647058823529412, b: 0.858823529411765},
5438		{name: '300', r: 0, g: 0.447058823529412, b: 0.776470588235294},
5439		{name: '3005', r: 0, g: 0.517647058823529, b: 0.788235294117647},
5440		{name: '301', r: 0, g: 0.356862745098039, b: 0.6},
5441		{name: '3015', r: 0, g: 0.43921568627451, b: 0.619607843137255},
5442		{name: '302', r: 0, g: 0.309803921568627, b: 0.427450980392157},
5443		{name: '3025', r: 0, g: 0.329411764705882, b: 0.419607843137255},
5444		{name: '303', r: 0, g: 0.247058823529412, b: 0.329411764705882},
5445		{name: '3035', r: 0, g: 0.266666666666667, b: 0.329411764705882},
5446		{name: '304', r: 0.647058823529412, g: 0.866666666666667, b: 0.886274509803922},
5447		{name: '305', r: 0.43921568627451, g: 0.807843137254902, b: 0.886274509803922},
5448		{name: '306', r: 0, g: 0.737254901960784, b: 0.886274509803922},
5449		{name: '306 2X', r: 0, g: 0.63921568627451, b: 0.819607843137255},
5450		{name: '307', r: 0, g: 0.47843137254902, b: 0.647058823529412},
5451		{name: '308', r: 0, g: 0.376470588235294, b: 0.486274509803922},
5452		{name: '309', r: 0, g: 0.247058823529412, b: 0.286274509803922},
5453		{name: '310', r: 0.447058823529412, g: 0.819607843137255, b: 0.866666666666667},
5454		{name: '3105', r: 0.498039215686275, g: 0.83921568627451, b: 0.858823529411765},
5455		{name: '311', r: 0.156862745098039, g: 0.768627450980392, b: 0.847058823529412},
5456		{name: '3115', r: 0.176470588235294, g: 0.776470588235294, b: 0.83921568627451},
5457		{name: '312', r: 0, g: 0.67843137254902, b: 0.776470588235294},
5458		{name: '3125', r: 0, g: 0.717647058823529, b: 0.776470588235294},
5459		{name: '313', r: 0, g: 0.6, b: 0.709803921568627},
5460		{name: '3135', r: 0, g: 0.607843137254902, b: 0.666666666666667},
5461		{name: '314', r: 0, g: 0.509803921568627, b: 0.607843137254902},
5462		{name: '3145', r: 0, g: 0.517647058823529, b: 0.556862745098039},
5463		{name: '315', r: 0, g: 0.419607843137255, b: 0.466666666666667},
5464		{name: '3155', r: 0, g: 0.427450980392157, b: 0.458823529411765},
5465		{name: '316', r: 0, g: 0.286274509803922, b: 0.309803921568627},
5466		{name: '3165', r: 0, g: 0.337254901960784, b: 0.356862745098039},
5467		{name: '317', r: 0.788235294117647, g: 0.909803921568627, b: 0.866666666666667},
5468		{name: '318', r: 0.576470588235294, g: 0.866666666666667, b: 0.858823529411765},
5469		{name: '319', r: 0.298039215686275, g: 0.807843137254902, b: 0.819607843137255},
5470		{name: '320', r: 0, g: 0.619607843137255, b: 0.627450980392157},
5471		{name: '320 2X', r: 0, g: 0.498039215686275, b: 0.509803921568627},
5472		{name: '321', r: 0, g: 0.529411764705882, b: 0.537254901960784},
5473		{name: '322', r: 0, g: 0.447058823529412, b: 0.447058823529412},
5474		{name: '323', r: 0, g: 0.4, b: 0.388235294117647},
5475		{name: '324', r: 0.666666666666667, g: 0.866666666666667, b: 0.83921568627451},
5476		{name: '3242', r: 0.529411764705882, g: 0.866666666666667, b: 0.819607843137255},
5477		{name: '3245', r: 0.549019607843137, g: 0.87843137254902, b: 0.819607843137255},
5478		{name: '3248', r: 0.47843137254902, g: 0.827450980392157, b: 0.756862745098039},
5479		{name: '325', r: 0.337254901960784, g: 0.788235294117647, b: 0.756862745098039},
5480		{name: '3252', r: 0.337254901960784, g: 0.83921568627451, b: 0.788235294117647},
5481		{name: '3255', r: 0.27843137254902, g: 0.83921568627451, b: 0.756862745098039},
5482		{name: '3258', r: 0.207843137254902, g: 0.768627450980392, b: 0.686274509803922},
5483		{name: '326', r: 0, g: 0.698039215686274, b: 0.666666666666667},
5484		{name: '3262', r: 0, g: 0.756862745098039, b: 0.709803921568627},
5485		{name: '3265', r: 0, g: 0.776470588235294, b: 0.698039215686274},
5486		{name: '3268', r: 0, g: 0.686274509803922, b: 0.6},
5487		{name: '327', r: 0, g: 0.549019607843137, b: 0.509803921568627},
5488		{name: '327 2X', r: 0, g: 0.537254901960784, b: 0.466666666666667},
5489		{name: '3272', r: 0, g: 0.666666666666667, b: 0.619607843137255},
5490		{name: '3275', r: 0, g: 0.698039215686274, b: 0.627450980392157},
5491		{name: '3278', r: 0, g: 0.607843137254902, b: 0.517647058823529},
5492		{name: '328', r: 0, g: 0.466666666666667, b: 0.43921568627451},
5493		{name: '3282', r: 0, g: 0.549019607843137, b: 0.509803921568627},
5494		{name: '3285', r: 0, g: 0.6, b: 0.529411764705882},
5495		{name: '3288', r: 0, g: 0.509803921568627, b: 0.43921568627451},
5496		{name: '329', r: 0, g: 0.427450980392157, b: 0.4},
5497		{name: '3292', r: 0, g: 0.376470588235294, b: 0.337254901960784},
5498		{name: '3295', r: 0, g: 0.509803921568627, b: 0.447058823529412},
5499		{name: '3298', r: 0, g: 0.419607843137255, b: 0.356862745098039},
5500		{name: '330', r: 0, g: 0.349019607843137, b: 0.317647058823529},
5501		{name: '3302', r: 0, g: 0.286274509803922, b: 0.247058823529412},
5502		{name: '3305', r: 0, g: 0.309803921568627, b: 0.258823529411765},
5503		{name: '3308', r: 0, g: 0.266666666666667, b: 0.219607843137255},
5504		{name: '331', r: 0.729411764705882, g: 0.917647058823529, b: 0.83921568627451},
5505		{name: '332', r: 0.627450980392157, g: 0.898039215686275, b: 0.807843137254902},
5506		{name: '333', r: 0.368627450980392, g: 0.866666666666667, b: 0.756862745098039},
5507		{name: '334', r: 0, g: 0.6, b: 0.486274509803922},
5508		{name: '335', r: 0, g: 0.486274509803922, b: 0.4},
5509		{name: '336', r: 0, g: 0.407843137254902, b: 0.329411764705882},
5510		{name: '337', r: 0.607843137254902, g: 0.858823529411765, b: 0.756862745098039},
5511		{name: '3375', r: 0.556862745098039, g: 0.886274509803922, b: 0.737254901960784},
5512		{name: '338', r: 0.47843137254902, g: 0.819607843137255, b: 0.709803921568627},
5513		{name: '3385', r: 0.329411764705882, g: 0.847058823529412, b: 0.658823529411765},
5514		{name: '339', r: 0, g: 0.698039215686274, b: 0.549019607843137},
5515		{name: '3395', r: 0, g: 0.788235294117647, b: 0.576470588235294},
5516		{name: '340', r: 0, g: 0.6, b: 0.466666666666667},
5517		{name: '3405', r: 0, g: 0.698039215686274, b: 0.47843137254902},
5518		{name: '341', r: 0, g: 0.47843137254902, b: 0.368627450980392},
5519		{name: '3415', r: 0, g: 0.486274509803922, b: 0.349019607843137},
5520		{name: '342', r: 0, g: 0.419607843137255, b: 0.329411764705882},
5521		{name: '3425', r: 0, g: 0.407843137254902, b: 0.27843137254902},
5522		{name: '343', r: 0, g: 0.337254901960784, b: 0.247058823529412},
5523		{name: '3435', r: 0.00784313725490196, g: 0.286274509803922, b: 0.188235294117647},
5524		{name: '344', r: 0.709803921568627, g: 0.886274509803922, b: 0.749019607843137},
5525		{name: '345', r: 0.588235294117647, g: 0.847058823529412, b: 0.686274509803922},
5526		{name: '346', r: 0.43921568627451, g: 0.807843137254902, b: 0.607843137254902},
5527		{name: '347', r: 0, g: 0.619607843137255, b: 0.376470588235294},
5528		{name: '348', r: 0, g: 0.529411764705882, b: 0.317647058823529},
5529		{name: '349', r: 0, g: 0.419607843137255, b: 0.247058823529412},
5530		{name: '350', r: 0.137254901960784, g: 0.309803921568627, b: 0.2},
5531		{name: '351', r: 0.709803921568627, g: 0.909803921568627, b: 0.749019607843137},
5532		{name: '352', r: 0.6, g: 0.898039215686275, b: 0.698039215686274},
5533		{name: '353', r: 0.517647058823529, g: 0.886274509803922, b: 0.658823529411765},
5534		{name: '354', r: 0, g: 0.717647058823529, b: 0.376470588235294},
5535		{name: '355', r: 0, g: 0.619607843137255, b: 0.286274509803922},
5536		{name: '356', r: 0, g: 0.47843137254902, b: 0.23921568627451},
5537		{name: '357', r: 0.129411764705882, g: 0.356862745098039, b: 0.2},
5538		{name: '358', r: 0.666666666666667, g: 0.866666666666667, b: 0.588235294117647},
5539		{name: '359', r: 0.627450980392157, g: 0.858823529411765, b: 0.556862745098039},
5540		{name: '360', r: 0.376470588235294, g: 0.776470588235294, b: 0.349019607843137},
5541		{name: '361', r: 0.117647058823529, g: 0.709803921568627, b: 0.227450980392157},
5542		{name: '362', r: 0.2, g: 0.619607843137255, b: 0.207843137254902},
5543		{name: '363', r: 0.23921568627451, g: 0.556862745098039, b: 0.2},
5544		{name: '364', r: 0.227450980392157, g: 0.466666666666667, b: 0.156862745098039},
5545		{name: '365', r: 0.827450980392157, g: 0.909803921568627, b: 0.63921568627451},
5546		{name: '366', r: 0.768627450980392, g: 0.898039215686275, b: 0.556862745098039},
5547		{name: '367', r: 0.666666666666667, g: 0.866666666666667, b: 0.427450980392157},
5548		{name: '368', r: 0.356862745098039, g: 0.749019607843137, b: 0.129411764705882},
5549		{name: '368 2X', r: 0, g: 0.619607843137255, b: 0.0588235294117647},
5550		{name: '369', r: 0.337254901960784, g: 0.666666666666667, b: 0.109803921568627},
5551		{name: '370', r: 0.337254901960784, g: 0.556862745098039, b: 0.0784313725490196},
5552		{name: '371', r: 0.337254901960784, g: 0.419607843137255, b: 0.129411764705882},
5553		{name: '372', r: 0.847058823529412, g: 0.929411764705882, b: 0.588235294117647},
5554		{name: '373', r: 0.807843137254902, g: 0.917647058823529, b: 0.509803921568627},
5555		{name: '374', r: 0.729411764705882, g: 0.909803921568627, b: 0.376470588235294},
5556		{name: '375', r: 0.549019607843137, g: 0.83921568627451, b: 0},
5557		{name: '375 2X', r: 0.329411764705882, g: 0.737254901960784, b: 0},
5558		{name: '376', r: 0.498039215686275, g: 0.729411764705882, b: 0},
5559		{name: '377', r: 0.43921568627451, g: 0.576470588235294, b: 0.00784313725490196},
5560		{name: '378', r: 0.337254901960784, g: 0.388235294117647, b: 0.0784313725490196},
5561		{name: '379', r: 0.87843137254902, g: 0.917647058823529, b: 0.407843137254902},
5562		{name: '380', r: 0.83921568627451, g: 0.898039215686275, b: 0.258823529411765},
5563		{name: '381', r: 0.8, g: 0.886274509803922, b: 0.149019607843137},
5564		{name: '382', r: 0.729411764705882, g: 0.847058823529412, b: 0.0392156862745098},
5565		{name: '382 2X', r: 0.619607843137255, g: 0.768627450980392, b: 0},
5566		{name: '383', r: 0.63921568627451, g: 0.686274509803922, b: 0.0274509803921569},
5567		{name: '384', r: 0.576470588235294, g: 0.6, b: 0.0196078431372549},
5568		{name: '385', r: 0.43921568627451, g: 0.43921568627451, b: 0.0784313725490196},
5569		{name: '386', r: 0.909803921568627, g: 0.929411764705882, b: 0.376470588235294},
5570		{name: '387', r: 0.87843137254902, g: 0.929411764705882, b: 0.266666666666667},
5571		{name: '388', r: 0.83921568627451, g: 0.909803921568627, b: 0.0588235294117647},
5572		{name: '389', r: 0.807843137254902, g: 0.87843137254902, b: 0.0274509803921569},
5573		{name: '390', r: 0.729411764705882, g: 0.768627450980392, b: 0.0196078431372549},
5574		{name: '391', r: 0.619607843137255, g: 0.619607843137255, b: 0.0274509803921569},
5575		{name: '392', r: 0.517647058823529, g: 0.509803921568627, b: 0.0196078431372549},
5576		{name: '393', r: 0.949019607843137, g: 0.937254901960784, b: 0.529411764705882},
5577		{name: '3935', r: 0.949019607843137, g: 0.929411764705882, b: 0.427450980392157},
5578		{name: '394', r: 0.917647058823529, g: 0.929411764705882, b: 0.207843137254902},
5579		{name: '3945', r: 0.937254901960784, g: 0.917647058823529, b: 0.0274509803921569},
5580		{name: '395', r: 0.898039215686275, g: 0.909803921568627, b: 0.0666666666666667},
5581		{name: '3955', r: 0.929411764705882, g: 0.886274509803922, b: 0.0666666666666667},
5582		{name: '396', r: 0.87843137254902, g: 0.886274509803922, b: 0.0470588235294118},
5583		{name: '3965', r: 0.909803921568627, g: 0.866666666666667, b: 0.0666666666666667},
5584		{name: '397', r: 0.756862745098039, g: 0.749019607843137, b: 0.0392156862745098},
5585		{name: '3975', r: 0.709803921568627, g: 0.658823529411765, b: 0.0470588235294118},
5586		{name: '398', r: 0.686274509803922, g: 0.658823529411765, b: 0.0392156862745098},
5587		{name: '3985', r: 0.6, g: 0.549019607843137, b: 0.0392156862745098},
5588		{name: '399', r: 0.6, g: 0.556862745098039, b: 0.0274509803921569},
5589		{name: '3995', r: 0.427450980392157, g: 0.376470588235294, b: 0.00784313725490196},
5590		{name: '400', r: 0.819607843137255, g: 0.776470588235294, b: 0.709803921568627},
5591		{name: '401', r: 0.756862745098039, g: 0.709803921568627, b: 0.647058823529412},
5592		{name: '402', r: 0.686274509803922, g: 0.647058823529412, b: 0.576470588235294},
5593		{name: '403', r: 0.6, g: 0.549019607843137, b: 0.486274509803922},
5594		{name: '404', r: 0.509803921568627, g: 0.458823529411765, b: 0.4},
5595		{name: '405', r: 0.419607843137255, g: 0.368627450980392, b: 0.309803921568627},
5596		{name: '406', r: 0.807843137254902, g: 0.756862745098039, b: 0.709803921568627},
5597		{name: '408', r: 0.658823529411765, g: 0.6, b: 0.549019607843137},
5598		{name: '409', r: 0.6, g: 0.537254901960784, b: 0.486274509803922},
5599		{name: '410', r: 0.486274509803922, g: 0.427450980392157, b: 0.388235294117647},
5600		{name: '411', r: 0.4, g: 0.349019607843137, b: 0.298039215686275},
5601		{name: '412', r: 0.23921568627451, g: 0.188235294117647, b: 0.156862745098039},
5602		{name: '413', r: 0.776470588235294, g: 0.756862745098039, b: 0.698039215686274},
5603		{name: '414', r: 0.709803921568627, g: 0.686274509803922, b: 0.627450980392157},
5604		{name: '415', r: 0.63921568627451, g: 0.619607843137255, b: 0.549019607843137},
5605		{name: '416', r: 0.556862745098039, g: 0.549019607843137, b: 0.47843137254902},
5606		{name: '417', r: 0.466666666666667, g: 0.447058823529412, b: 0.388235294117647},
5607		{name: '418', r: 0.376470588235294, g: 0.368627450980392, b: 0.309803921568627},
5608		{name: '419', r: 0.156862745098039, g: 0.156862745098039, b: 0.129411764705882},
5609		{name: '420', r: 0.819607843137255, g: 0.8, b: 0.749019607843137},
5610		{name: '421', r: 0.749019607843137, g: 0.729411764705882, b: 0.686274509803922},
5611		{name: '422', r: 0.686274509803922, g: 0.666666666666667, b: 0.63921568627451},
5612		{name: '423', r: 0.588235294117647, g: 0.576470588235294, b: 0.556862745098039},
5613		{name: '424', r: 0.509803921568627, g: 0.498039215686275, b: 0.466666666666667},
5614		{name: '425', r: 0.376470588235294, g: 0.376470588235294, b: 0.356862745098039},
5615		{name: '426', r: 0.168627450980392, g: 0.168627450980392, b: 0.156862745098039},
5616		{name: '427', r: 0.866666666666667, g: 0.858823529411765, b: 0.819607843137255},
5617		{name: '428', r: 0.819607843137255, g: 0.807843137254902, b: 0.776470588235294},
5618		{name: '429', r: 0.67843137254902, g: 0.686274509803922, b: 0.666666666666667},
5619		{name: '430', r: 0.568627450980392, g: 0.588235294117647, b: 0.576470588235294},
5620		{name: '431', r: 0.4, g: 0.427450980392157, b: 0.43921568627451},
5621		{name: '432', r: 0.266666666666667, g: 0.309803921568627, b: 0.317647058823529},
5622		{name: '433', r: 0.188235294117647, g: 0.219607843137255, b: 0.227450980392157},
5623		{name: '433 2X', r: 0.0392156862745098, g: 0.0470588235294118, b: 0.0666666666666667},
5624		{name: '434', r: 0.87843137254902, g: 0.819607843137255, b: 0.776470588235294},
5625		{name: '435', r: 0.827450980392157, g: 0.749019607843137, b: 0.717647058823529},
5626		{name: '436', r: 0.737254901960784, g: 0.647058823529412, b: 0.619607843137255},
5627		{name: '437', r: 0.549019607843137, g: 0.43921568627451, b: 0.419607843137255},
5628		{name: '438', r: 0.349019607843137, g: 0.247058823529412, b: 0.23921568627451},
5629		{name: '439', r: 0.286274509803922, g: 0.207843137254902, b: 0.2},
5630		{name: '440', r: 0.247058823529412, g: 0.188235294117647, b: 0.168627450980392},
5631		{name: '441', r: 0.819607843137255, g: 0.819607843137255, b: 0.776470588235294},
5632		{name: '442', r: 0.729411764705882, g: 0.749019607843137, b: 0.717647058823529},
5633		{name: '443', r: 0.63921568627451, g: 0.658823529411765, b: 0.63921568627451},
5634		{name: '444', r: 0.537254901960784, g: 0.556862745098039, b: 0.549019607843137},
5635		{name: '445', r: 0.337254901960784, g: 0.349019607843137, b: 0.349019607843137},
5636		{name: '446', r: 0.286274509803922, g: 0.298039215686275, b: 0.286274509803922},
5637		{name: '447', r: 0.247058823529412, g: 0.247058823529412, b: 0.219607843137255},
5638		{name: '448', r: 0.329411764705882, g: 0.27843137254902, b: 0.176470588235294},
5639		{name: '4485', r: 0.376470588235294, g: 0.298039215686275, b: 0.0666666666666667},
5640		{name: '449', r: 0.329411764705882, g: 0.27843137254902, b: 0.149019607843137},
5641		{name: '4495', r: 0.529411764705882, g: 0.458823529411765, b: 0.188235294117647},
5642		{name: '450', r: 0.376470588235294, g: 0.329411764705882, b: 0.168627450980392},
5643		{name: '4505', r: 0.627450980392157, g: 0.568627450980392, b: 0.317647058823529},
5644		{name: '451', r: 0.67843137254902, g: 0.627450980392157, b: 0.47843137254902},
5645		{name: '4515', r: 0.737254901960784, g: 0.67843137254902, b: 0.458823529411765},
5646		{name: '452', r: 0.768627450980392, g: 0.717647058823529, b: 0.588235294117647},
5647		{name: '4525', r: 0.8, g: 0.749019607843137, b: 0.556862745098039},
5648		{name: '453', r: 0.83921568627451, g: 0.8, b: 0.686274509803922},
5649		{name: '4535', r: 0.858823529411765, g: 0.807843137254902, b: 0.647058823529412},
5650		{name: '454', r: 0.886274509803922, g: 0.847058823529412, b: 0.749019607843137},
5651		{name: '4545', r: 0.898039215686275, g: 0.858823529411765, b: 0.729411764705882},
5652		{name: '455', r: 0.4, g: 0.337254901960784, b: 0.0784313725490196},
5653		{name: '456', r: 0.6, g: 0.529411764705882, b: 0.0784313725490196},
5654		{name: '457', r: 0.709803921568627, g: 0.607843137254902, b: 0.0470588235294118},
5655		{name: '458', r: 0.866666666666667, g: 0.8, b: 0.419607843137255},
5656		{name: '459', r: 0.886274509803922, g: 0.83921568627451, b: 0.486274509803922},
5657		{name: '460', r: 0.917647058823529, g: 0.866666666666667, b: 0.588235294117647},
5658		{name: '461', r: 0.929411764705882, g: 0.898039215686275, b: 0.67843137254902},
5659		{name: '462', r: 0.356862745098039, g: 0.27843137254902, b: 0.137254901960784},
5660		{name: '4625', r: 0.27843137254902, g: 0.137254901960784, b: 0.0666666666666667},
5661		{name: '463', r: 0.458823529411765, g: 0.329411764705882, b: 0.149019607843137},
5662		{name: '4635', r: 0.549019607843137, g: 0.349019607843137, b: 0.2},
5663		{name: '464', r: 0.529411764705882, g: 0.376470588235294, b: 0.156862745098039},
5664		{name: '464 2X', r: 0.43921568627451, g: 0.258823529411765, b: 0.0784313725490196},
5665		{name: '4645', r: 0.698039215686274, g: 0.509803921568627, b: 0.376470588235294},
5666		{name: '465', r: 0.756862745098039, g: 0.658823529411765, b: 0.458823529411765},
5667		{name: '4655', r: 0.768627450980392, g: 0.6, b: 0.466666666666667},
5668		{name: '466', r: 0.819607843137255, g: 0.749019607843137, b: 0.568627450980392},
5669		{name: '4665', r: 0.847058823529412, g: 0.709803921568627, b: 0.588235294117647},
5670		{name: '467', r: 0.866666666666667, g: 0.8, b: 0.647058823529412},
5671		{name: '4675', r: 0.898039215686275, g: 0.776470588235294, b: 0.666666666666667},
5672		{name: '468', r: 0.886274509803922, g: 0.83921568627451, b: 0.709803921568627},
5673		{name: '4685', r: 0.929411764705882, g: 0.827450980392157, b: 0.737254901960784},
5674		{name: '469', r: 0.376470588235294, g: 0.2, b: 0.0666666666666667},
5675		{name: '4695', r: 0.317647058823529, g: 0.149019607843137, b: 0.109803921568627},
5676		{name: '470', r: 0.607843137254902, g: 0.309803921568627, b: 0.0980392156862745},
5677		{name: '4705', r: 0.486274509803922, g: 0.317647058823529, b: 0.23921568627451},
5678		{name: '471', r: 0.737254901960784, g: 0.368627450980392, b: 0.117647058823529},
5679		{name: '471 2X', r: 0.63921568627451, g: 0.266666666666667, b: 0.00784313725490196},
5680		{name: '4715', r: 0.6, g: 0.43921568627451, b: 0.356862745098039},
5681		{name: '472', r: 0.917647058823529, g: 0.666666666666667, b: 0.47843137254902},
5682		{name: '4725', r: 0.709803921568627, g: 0.568627450980392, b: 0.486274509803922},
5683		{name: '473', r: 0.956862745098039, g: 0.768627450980392, b: 0.627450980392157},
5684		{name: '4735', r: 0.8, g: 0.686274509803922, b: 0.607843137254902},
5685		{name: '474', r: 0.956862745098039, g: 0.8, b: 0.666666666666667},
5686		{name: '4745', r: 0.847058823529412, g: 0.749019607843137, b: 0.666666666666667},
5687		{name: '475', r: 0.968627450980392, g: 0.827450980392157, b: 0.709803921568627},
5688		{name: '4755', r: 0.886274509803922, g: 0.8, b: 0.729411764705882},
5689		{name: '476', r: 0.349019607843137, g: 0.23921568627451, b: 0.168627450980392},
5690		{name: '477', r: 0.388235294117647, g: 0.219607843137255, b: 0.149019607843137},
5691		{name: '478', r: 0.47843137254902, g: 0.247058823529412, b: 0.156862745098039},
5692		{name: '479', r: 0.686274509803922, g: 0.537254901960784, b: 0.43921568627451},
5693		{name: '480', r: 0.827450980392157, g: 0.717647058823529, b: 0.63921568627451},
5694		{name: '481', r: 0.87843137254902, g: 0.8, b: 0.729411764705882},
5695		{name: '482', r: 0.898039215686275, g: 0.827450980392157, b: 0.756862745098039},
5696		{name: '483', r: 0.419607843137255, g: 0.188235294117647, b: 0.129411764705882},
5697		{name: '484', r: 0.607843137254902, g: 0.188235294117647, b: 0.109803921568627},
5698		{name: '485', r: 0.847058823529412, g: 0.117647058823529, b: 0.0196078431372549},
5699		{name: '485 2X', r: 0.8, g: 0.0470588235294118, b: 0},
5700		{name: '486', r: 0.929411764705882, g: 0.619607843137255, b: 0.517647058823529},
5701		{name: '487', r: 0.937254901960784, g: 0.709803921568627, b: 0.627450980392157},
5702		{name: '488', r: 0.949019607843137, g: 0.768627450980392, b: 0.686274509803922},
5703		{name: '489', r: 0.949019607843137, g: 0.819607843137255, b: 0.749019607843137},
5704		{name: '490', r: 0.356862745098039, g: 0.149019607843137, b: 0.149019607843137},
5705		{name: '491', r: 0.458823529411765, g: 0.156862745098039, b: 0.156862745098039},
5706		{name: '492', r: 0.568627450980392, g: 0.2, b: 0.219607843137255},
5707		{name: '494', r: 0.949019607843137, g: 0.67843137254902, b: 0.698039215686274},
5708		{name: '495', r: 0.956862745098039, g: 0.737254901960784, b: 0.749019607843137},
5709		{name: '496', r: 0.968627450980392, g: 0.788235294117647, b: 0.776470588235294},
5710		{name: '497', r: 0.317647058823529, g: 0.156862745098039, b: 0.149019607843137},
5711		{name: '4975', r: 0.266666666666667, g: 0.117647058823529, b: 0.109803921568627},
5712		{name: '498', r: 0.427450980392157, g: 0.2, b: 0.168627450980392},
5713		{name: '4985', r: 0.517647058823529, g: 0.286274509803922, b: 0.286274509803922},
5714		{name: '499', r: 0.47843137254902, g: 0.219607843137255, b: 0.176470588235294},
5715		{name: '4995', r: 0.647058823529412, g: 0.419607843137255, b: 0.427450980392157},
5716		{name: '500', r: 0.807843137254902, g: 0.537254901960784, b: 0.549019607843137},
5717		{name: '5005', r: 0.737254901960784, g: 0.529411764705882, b: 0.529411764705882},
5718		{name: '501', r: 0.917647058823529, g: 0.698039215686274, b: 0.698039215686274},
5719		{name: '5015', r: 0.847058823529412, g: 0.67843137254902, b: 0.658823529411765},
5720		{name: '502', r: 0.949019607843137, g: 0.776470588235294, b: 0.768627450980392},
5721		{name: '5025', r: 0.886274509803922, g: 0.737254901960784, b: 0.717647058823529},
5722		{name: '503', r: 0.956862745098039, g: 0.819607843137255, b: 0.8},
5723		{name: '5035', r: 0.929411764705882, g: 0.807843137254902, b: 0.776470588235294},
5724		{name: '504', r: 0.317647058823529, g: 0.117647058823529, b: 0.149019607843137},
5725		{name: '505', r: 0.4, g: 0.117647058823529, b: 0.168627450980392},
5726		{name: '506', r: 0.47843137254902, g: 0.149019607843137, b: 0.219607843137255},
5727		{name: '507', r: 0.847058823529412, g: 0.537254901960784, b: 0.607843137254902},
5728		{name: '508', r: 0.909803921568627, g: 0.647058823529412, b: 0.686274509803922},
5729		{name: '509', r: 0.949019607843137, g: 0.729411764705882, b: 0.749019607843137},
5730		{name: '510', r: 0.956862745098039, g: 0.776470588235294, b: 0.788235294117647},
5731		{name: '511', r: 0.376470588235294, g: 0.129411764705882, b: 0.266666666666667},
5732		{name: '5115', r: 0.309803921568627, g: 0.129411764705882, b: 0.227450980392157},
5733		{name: '512', r: 0.517647058823529, g: 0.129411764705882, b: 0.419607843137255},
5734		{name: '5125', r: 0.458823529411765, g: 0.27843137254902, b: 0.376470588235294},
5735		{name: '513', r: 0.619607843137255, g: 0.137254901960784, b: 0.529411764705882},
5736		{name: '5135', r: 0.576470588235294, g: 0.419607843137255, b: 0.498039215686275},
5737		{name: '514', r: 0.847058823529412, g: 0.517647058823529, b: 0.737254901960784},
5738		{name: '5145', r: 0.67843137254902, g: 0.529411764705882, b: 0.6},
5739		{name: '515', r: 0.909803921568627, g: 0.63921568627451, b: 0.788235294117647},
5740		{name: '5155', r: 0.8, g: 0.686274509803922, b: 0.717647058823529},
5741		{name: '516', r: 0.949019607843137, g: 0.729411764705882, b: 0.827450980392157},
5742		{name: '5165', r: 0.87843137254902, g: 0.788235294117647, b: 0.8},
5743		{name: '517', r: 0.956862745098039, g: 0.8, b: 0.847058823529412},
5744		{name: '5175', r: 0.909803921568627, g: 0.83921568627451, b: 0.819607843137255},
5745		{name: '518', r: 0.317647058823529, g: 0.176470588235294, b: 0.266666666666667},
5746		{name: '5185', r: 0.27843137254902, g: 0.156862745098039, b: 0.207843137254902},
5747		{name: '519', r: 0.388235294117647, g: 0.188235294117647, b: 0.368627450980392},
5748		{name: '5195', r: 0.349019607843137, g: 0.2, b: 0.266666666666667},
5749		{name: '520', r: 0.43921568627451, g: 0.207843137254902, b: 0.447058823529412},
5750		{name: '5205', r: 0.556862745098039, g: 0.407843137254902, b: 0.466666666666667},
5751		{name: '521', r: 0.709803921568627, g: 0.549019607843137, b: 0.698039215686274},
5752		{name: '5215', r: 0.709803921568627, g: 0.576470588235294, b: 0.607843137254902},
5753		{name: '522', r: 0.776470588235294, g: 0.63921568627451, b: 0.756862745098039},
5754		{name: '5225', r: 0.8, g: 0.67843137254902, b: 0.686274509803922},
5755		{name: '523', r: 0.827450980392157, g: 0.717647058823529, b: 0.8},
5756		{name: '5235', r: 0.866666666666667, g: 0.776470588235294, b: 0.768627450980392},
5757		{name: '524', r: 0.886274509803922, g: 0.8, b: 0.827450980392157},
5758		{name: '5245', r: 0.898039215686275, g: 0.827450980392157, b: 0.8},
5759		{name: '525', r: 0.317647058823529, g: 0.149019607843137, b: 0.329411764705882},
5760		{name: '5255', r: 0.207843137254902, g: 0.149019607843137, b: 0.309803921568627},
5761		{name: '526', r: 0.407843137254902, g: 0.129411764705882, b: 0.47843137254902},
5762		{name: '5265', r: 0.286274509803922, g: 0.23921568627451, b: 0.388235294117647},
5763		{name: '527', r: 0.47843137254902, g: 0.117647058823529, b: 0.6},
5764		{name: '5275', r: 0.376470588235294, g: 0.337254901960784, b: 0.466666666666667},
5765		{name: '528', r: 0.686274509803922, g: 0.447058823529412, b: 0.756862745098039},
5766		{name: '5285', r: 0.549019607843137, g: 0.509803921568627, b: 0.6},
5767		{name: '529', r: 0.807843137254902, g: 0.63921568627451, b: 0.827450980392157},
5768		{name: '5295', r: 0.698039215686274, g: 0.658823529411765, b: 0.709803921568627},
5769		{name: '530', r: 0.83921568627451, g: 0.686274509803922, b: 0.83921568627451},
5770		{name: '5305', r: 0.8, g: 0.756862745098039, b: 0.776470588235294},
5771		{name: '531', r: 0.898039215686275, g: 0.776470588235294, b: 0.858823529411765},
5772		{name: '5315', r: 0.858823529411765, g: 0.827450980392157, b: 0.827450980392157},
5773		{name: '532', r: 0.207843137254902, g: 0.219607843137255, b: 0.258823529411765},
5774		{name: '533', r: 0.207843137254902, g: 0.247058823529412, b: 0.356862745098039},
5775		{name: '534', r: 0.227450980392157, g: 0.286274509803922, b: 0.447058823529412},
5776		{name: '535', r: 0.607843137254902, g: 0.63921568627451, b: 0.717647058823529},
5777		{name: '536', r: 0.67843137254902, g: 0.698039215686274, b: 0.756862745098039},
5778		{name: '537', r: 0.768627450980392, g: 0.776470588235294, b: 0.807843137254902},
5779		{name: '538', r: 0.83921568627451, g: 0.827450980392157, b: 0.83921568627451},
5780		{name: '539', r: 0, g: 0.188235294117647, b: 0.286274509803922},
5781		{name: '5395', r: 0.00784313725490196, g: 0.156862745098039, b: 0.227450980392157},
5782		{name: '540', r: 0, g: 0.2, b: 0.356862745098039},
5783		{name: '5405', r: 0.247058823529412, g: 0.376470588235294, b: 0.458823529411765},
5784		{name: '541', r: 0, g: 0.247058823529412, b: 0.466666666666667},
5785		{name: '5415', r: 0.376470588235294, g: 0.486274509803922, b: 0.549019607843137},
5786		{name: '542', r: 0.4, g: 0.576470588235294, b: 0.737254901960784},
5787		{name: '5425', r: 0.517647058823529, g: 0.6, b: 0.647058823529412},
5788		{name: '543', r: 0.576470588235294, g: 0.717647058823529, b: 0.819607843137255},
5789		{name: '5435', r: 0.686274509803922, g: 0.737254901960784, b: 0.749019607843137},
5790		{name: '544', r: 0.717647058823529, g: 0.8, b: 0.858823529411765},
5791		{name: '5445', r: 0.768627450980392, g: 0.8, b: 0.8},
5792		{name: '545', r: 0.768627450980392, g: 0.827450980392157, b: 0.866666666666667},
5793		{name: '5455', r: 0.83921568627451, g: 0.847058823529412, b: 0.827450980392157},
5794		{name: '546', r: 0.0470588235294118, g: 0.219607843137255, b: 0.266666666666667},
5795		{name: '5463', r: 0, g: 0.207843137254902, b: 0.227450980392157},
5796		{name: '5467', r: 0.0980392156862745, g: 0.219607843137255, b: 0.2},
5797		{name: '547', r: 0, g: 0.247058823529412, b: 0.329411764705882},
5798		{name: '5473', r: 0.149019607843137, g: 0.407843137254902, b: 0.427450980392157},
5799		{name: '5477', r: 0.227450980392157, g: 0.337254901960784, b: 0.309803921568627},
5800		{name: '548', r: 0, g: 0.266666666666667, b: 0.349019607843137},
5801		{name: '5483', r: 0.376470588235294, g: 0.568627450980392, b: 0.568627450980392},
5802		{name: '5487', r: 0.4, g: 0.486274509803922, b: 0.447058823529412},
5803		{name: '549', r: 0.368627450980392, g: 0.6, b: 0.666666666666667},
5804		{name: '5493', r: 0.549019607843137, g: 0.686274509803922, b: 0.67843137254902},
5805		{name: '5497', r: 0.568627450980392, g: 0.63921568627451, b: 0.6},
5806		{name: '550', r: 0.529411764705882, g: 0.686274509803922, b: 0.749019607843137},
5807		{name: '5503', r: 0.666666666666667, g: 0.768627450980392, b: 0.749019607843137},
5808		{name: '5507', r: 0.686274509803922, g: 0.729411764705882, b: 0.698039215686274},
5809		{name: '551', r: 0.63921568627451, g: 0.756862745098039, b: 0.788235294117647},
5810		{name: '5513', r: 0.807843137254902, g: 0.847058823529412, b: 0.819607843137255},
5811		{name: '5517', r: 0.788235294117647, g: 0.807843137254902, b: 0.768627450980392},
5812		{name: '552', r: 0.768627450980392, g: 0.83921568627451, b: 0.83921568627451},
5813		{name: '5523', r: 0.83921568627451, g: 0.866666666666667, b: 0.83921568627451},
5814		{name: '5527', r: 0.807843137254902, g: 0.819607843137255, b: 0.776470588235294},
5815		{name: '553', r: 0.137254901960784, g: 0.266666666666667, b: 0.207843137254902},
5816		{name: '5535', r: 0.129411764705882, g: 0.23921568627451, b: 0.188235294117647},
5817		{name: '554', r: 0.0980392156862745, g: 0.368627450980392, b: 0.27843137254902},
5818		{name: '5545', r: 0.309803921568627, g: 0.427450980392157, b: 0.368627450980392},
5819		{name: '555', r: 0.0274509803921569, g: 0.427450980392157, b: 0.329411764705882},
5820		{name: '5555', r: 0.466666666666667, g: 0.568627450980392, b: 0.509803921568627},
5821		{name: '556', r: 0.47843137254902, g: 0.658823529411765, b: 0.568627450980392},
5822		{name: '5565', r: 0.588235294117647, g: 0.666666666666667, b: 0.6},
5823		{name: '557', r: 0.63921568627451, g: 0.756862745098039, b: 0.67843137254902},
5824		{name: '5575', r: 0.686274509803922, g: 0.749019607843137, b: 0.67843137254902},
5825		{name: '558', r: 0.717647058823529, g: 0.807843137254902, b: 0.737254901960784},
5826		{name: '5585', r: 0.768627450980392, g: 0.807843137254902, b: 0.749019607843137},
5827		{name: '559', r: 0.776470588235294, g: 0.83921568627451, b: 0.768627450980392},
5828		{name: '5595', r: 0.847058823529412, g: 0.858823529411765, b: 0.8},
5829		{name: '560', r: 0.168627450980392, g: 0.298039215686275, b: 0.247058823529412},
5830		{name: '5605', r: 0.137254901960784, g: 0.227450980392157, b: 0.176470588235294},
5831		{name: '561', r: 0.149019607843137, g: 0.4, b: 0.349019607843137},
5832		{name: '5615', r: 0.329411764705882, g: 0.407843137254902, b: 0.337254901960784},
5833		{name: '562', r: 0.117647058823529, g: 0.47843137254902, b: 0.427450980392157},
5834		{name: '5625', r: 0.447058823529412, g: 0.517647058823529, b: 0.43921568627451},
5835		{name: '563', r: 0.498039215686275, g: 0.737254901960784, b: 0.666666666666667},
5836		{name: '5635', r: 0.619607843137255, g: 0.666666666666667, b: 0.6},
5837		{name: '564', r: 0.0196078431372549, g: 0.43921568627451, b: 0.368627450980392},
5838		{name: '5645', r: 0.737254901960784, g: 0.756862745098039, b: 0.698039215686274},
5839		{name: '565', r: 0.737254901960784, g: 0.858823529411765, b: 0.8},
5840		{name: '5655', r: 0.776470588235294, g: 0.8, b: 0.729411764705882},
5841		{name: '566', r: 0.819607843137255, g: 0.886274509803922, b: 0.827450980392157},
5842		{name: '5665', r: 0.83921568627451, g: 0.83921568627451, b: 0.776470588235294},
5843		{name: '567', r: 0.149019607843137, g: 0.317647058823529, b: 0.258823529411765},
5844		{name: '568', r: 0, g: 0.447058823529412, b: 0.388235294117647},
5845		{name: '569', r: 0, g: 0.529411764705882, b: 0.447058823529412},
5846		{name: '570', r: 0.498039215686275, g: 0.776470588235294, b: 0.698039215686274},
5847		{name: '571', r: 0.666666666666667, g: 0.858823529411765, b: 0.776470588235294},
5848		{name: '572', r: 0.737254901960784, g: 0.886274509803922, b: 0.807843137254902},
5849		{name: '573', r: 0.8, g: 0.898039215686275, b: 0.83921568627451},
5850		{name: '574', r: 0.286274509803922, g: 0.349019607843137, b: 0.156862745098039},
5851		{name: '5743', r: 0.247058823529412, g: 0.286274509803922, b: 0.149019607843137},
5852		{name: '5747', r: 0.258823529411765, g: 0.27843137254902, b: 0.0862745098039216},
5853		{name: '575', r: 0.329411764705882, g: 0.466666666666667, b: 0.188235294117647},
5854		{name: '5753', r: 0.368627450980392, g: 0.4, b: 0.227450980392157},
5855		{name: '5757', r: 0.419607843137255, g: 0.43921568627451, b: 0.168627450980392},
5856		{name: '576', r: 0.376470588235294, g: 0.556862745098039, b: 0.227450980392157},
5857		{name: '5763', r: 0.466666666666667, g: 0.486274509803922, b: 0.309803921568627},
5858		{name: '5767', r: 0.549019607843137, g: 0.568627450980392, b: 0.309803921568627},
5859		{name: '577', r: 0.709803921568627, g: 0.8, b: 0.556862745098039},
5860		{name: '5773', r: 0.607843137254902, g: 0.619607843137255, b: 0.447058823529412},
5861		{name: '5777', r: 0.666666666666667, g: 0.67843137254902, b: 0.458823529411765},
5862		{name: '578', r: 0.776470588235294, g: 0.83921568627451, b: 0.627450980392157},
5863		{name: '5783', r: 0.709803921568627, g: 0.709803921568627, b: 0.556862745098039},
5864		{name: '5787', r: 0.776470588235294, g: 0.776470588235294, b: 0.6},
5865		{name: '579', r: 0.788235294117647, g: 0.83921568627451, b: 0.63921568627451},
5866		{name: '5793', r: 0.776470588235294, g: 0.776470588235294, b: 0.647058823529412},
5867		{name: '5797', r: 0.827450980392157, g: 0.819607843137255, b: 0.666666666666667},
5868		{name: '580', r: 0.847058823529412, g: 0.866666666666667, b: 0.709803921568627},
5869		{name: '5803', r: 0.847058823529412, g: 0.83921568627451, b: 0.717647058823529},
5870		{name: '5807', r: 0.87843137254902, g: 0.866666666666667, b: 0.737254901960784},
5871		{name: '581', r: 0.376470588235294, g: 0.368627450980392, b: 0.0666666666666667},
5872		{name: '5815', r: 0.286274509803922, g: 0.266666666666667, b: 0.0666666666666667},
5873		{name: '582', r: 0.529411764705882, g: 0.537254901960784, b: 0.0196078431372549},
5874		{name: '5825', r: 0.458823529411765, g: 0.43921568627451, b: 0.168627450980392},
5875		{name: '583', r: 0.666666666666667, g: 0.729411764705882, b: 0.0392156862745098},
5876		{name: '5835', r: 0.619607843137255, g: 0.6, b: 0.349019607843137},
5877		{name: '584', r: 0.807843137254902, g: 0.83921568627451, b: 0.286274509803922},
5878		{name: '5845', r: 0.698039215686274, g: 0.666666666666667, b: 0.43921568627451},
5879		{name: '585', r: 0.858823529411765, g: 0.87843137254902, b: 0.419607843137255},
5880		{name: '5855', r: 0.8, g: 0.776470588235294, b: 0.576470588235294},
5881		{name: '586', r: 0.886274509803922, g: 0.898039215686275, b: 0.517647058823529},
5882		{name: '5865', r: 0.83921568627451, g: 0.807843137254902, b: 0.63921568627451},
5883		{name: '587', r: 0.909803921568627, g: 0.909803921568627, b: 0.607843137254902},
5884		{name: '5875', r: 0.87843137254902, g: 0.858823529411765, b: 0.709803921568627},
5885		{name: '600', r: 0.956862745098039, g: 0.929411764705882, b: 0.686274509803922},
5886		{name: '601', r: 0.949019607843137, g: 0.929411764705882, b: 0.619607843137255},
5887		{name: '602', r: 0.949019607843137, g: 0.917647058823529, b: 0.529411764705882},
5888		{name: '603', r: 0.929411764705882, g: 0.909803921568627, b: 0.356862745098039},
5889		{name: '604', r: 0.909803921568627, g: 0.866666666666667, b: 0.129411764705882},
5890		{name: '605', r: 0.866666666666667, g: 0.807843137254902, b: 0.0666666666666667},
5891		{name: '606', r: 0.827450980392157, g: 0.749019607843137, b: 0.0666666666666667},
5892		{name: '607', r: 0.949019607843137, g: 0.917647058823529, b: 0.737254901960784},
5893		{name: '608', r: 0.937254901960784, g: 0.909803921568627, b: 0.67843137254902},
5894		{name: '609', r: 0.917647058823529, g: 0.898039215686275, b: 0.588235294117647},
5895		{name: '610', r: 0.886274509803922, g: 0.858823529411765, b: 0.447058823529412},
5896		{name: '611', r: 0.83921568627451, g: 0.807843137254902, b: 0.286274509803922},
5897		{name: '612', r: 0.768627450980392, g: 0.729411764705882, b: 0},
5898		{name: '613', r: 0.686274509803922, g: 0.627450980392157, b: 0.0470588235294118},
5899		{name: '614', r: 0.917647058823529, g: 0.886274509803922, b: 0.717647058823529},
5900		{name: '615', r: 0.886274509803922, g: 0.858823529411765, b: 0.666666666666667},
5901		{name: '616', r: 0.866666666666667, g: 0.83921568627451, b: 0.607843137254902},
5902		{name: '617', r: 0.8, g: 0.768627450980392, b: 0.486274509803922},
5903		{name: '618', r: 0.709803921568627, g: 0.666666666666667, b: 0.349019607843137},
5904		{name: '619', r: 0.588235294117647, g: 0.549019607843137, b: 0.156862745098039},
5905		{name: '620', r: 0.517647058823529, g: 0.466666666666667, b: 0.0666666666666667},
5906		{name: '621', r: 0.847058823529412, g: 0.866666666666667, b: 0.807843137254902},
5907		{name: '622', r: 0.756862745098039, g: 0.819607843137255, b: 0.749019607843137},
5908		{name: '623', r: 0.647058823529412, g: 0.749019607843137, b: 0.666666666666667},
5909		{name: '624', r: 0.498039215686275, g: 0.627450980392157, b: 0.549019607843137},
5910		{name: '625', r: 0.356862745098039, g: 0.529411764705882, b: 0.447058823529412},
5911		{name: '626', r: 0.129411764705882, g: 0.329411764705882, b: 0.247058823529412},
5912		{name: '627', r: 0.0470588235294118, g: 0.188235294117647, b: 0.149019607843137},
5913		{name: '628', r: 0.8, g: 0.886274509803922, b: 0.866666666666667},
5914		{name: '629', r: 0.698039215686274, g: 0.847058823529412, b: 0.847058823529412},
5915		{name: '630', r: 0.549019607843137, g: 0.8, b: 0.827450980392157},
5916		{name: '631', r: 0.329411764705882, g: 0.717647058823529, b: 0.776470588235294},
5917		{name: '632', r: 0, g: 0.627450980392157, b: 0.729411764705882},
5918		{name: '633', r: 0, g: 0.498039215686275, b: 0.6},
5919		{name: '634', r: 0, g: 0.4, b: 0.498039215686275},
5920		{name: '635', r: 0.729411764705882, g: 0.87843137254902, b: 0.87843137254902},
5921		{name: '636', r: 0.6, g: 0.83921568627451, b: 0.866666666666667},
5922		{name: '637', r: 0.419607843137255, g: 0.788235294117647, b: 0.858823529411765},
5923		{name: '638', r: 0, g: 0.709803921568627, b: 0.83921568627451},
5924		{name: '639', r: 0, g: 0.627450980392157, b: 0.768627450980392},
5925		{name: '640', r: 0, g: 0.549019607843137, b: 0.698039215686274},
5926		{name: '641', r: 0, g: 0.47843137254902, b: 0.647058823529412},
5927		{name: '642', r: 0.819607843137255, g: 0.847058823529412, b: 0.847058823529412},
5928		{name: '643', r: 0.776470588235294, g: 0.819607843137255, b: 0.83921568627451},
5929		{name: '644', r: 0.607843137254902, g: 0.686274509803922, b: 0.768627450980392},
5930		{name: '645', r: 0.466666666666667, g: 0.588235294117647, b: 0.698039215686274},
5931		{name: '646', r: 0.368627450980392, g: 0.509803921568627, b: 0.63921568627451},
5932		{name: '647', r: 0.149019607843137, g: 0.329411764705882, b: 0.486274509803922},
5933		{name: '648', r: 0, g: 0.188235294117647, b: 0.368627450980392},
5934		{name: '649', r: 0.83921568627451, g: 0.83921568627451, b: 0.847058823529412},
5935		{name: '650', r: 0.749019607843137, g: 0.776470588235294, b: 0.819607843137255},
5936		{name: '651', r: 0.607843137254902, g: 0.666666666666667, b: 0.749019607843137},
5937		{name: '652', r: 0.427450980392157, g: 0.529411764705882, b: 0.658823529411765},
5938		{name: '653', r: 0.2, g: 0.337254901960784, b: 0.529411764705882},
5939		{name: '654', r: 0.0588235294117647, g: 0.168627450980392, b: 0.356862745098039},
5940		{name: '655', r: 0.0470588235294118, g: 0.109803921568627, b: 0.27843137254902},
5941		{name: '656', r: 0.83921568627451, g: 0.858823529411765, b: 0.87843137254902},
5942		{name: '657', r: 0.756862745098039, g: 0.788235294117647, b: 0.866666666666667},
5943		{name: '658', r: 0.647058823529412, g: 0.686274509803922, b: 0.83921568627451},
5944		{name: '659', r: 0.498039215686275, g: 0.549019607843137, b: 0.749
5944019607843137},
5945		{name: '660', r: 0.349019607843137, g: 0.376470588235294, b: 0.658823529411765},
5946		{name: '661', r: 0.176470588235294, g: 0.2, b: 0.556862745098039},
5947		{name: '662', r: 0.0470588235294118, g: 0.0980392156862745, b: 0.458823529411765},
5948		{name: '663', r: 0.886274509803922, g: 0.827450980392157, b: 0.83921568627451},
5949		{name: '664', r: 0.847058823529412, g: 0.8, b: 0.819607843137255},
5950		{name: '665', r: 0.776470588235294, g: 0.709803921568627, b: 0.768627450980392},
5951		{name: '666', r: 0.658823529411765, g: 0.576470588235294, b: 0.67843137254902},
5952		{name: '667', r: 0.498039215686275, g: 0.4, b: 0.537254901960784},
5953		{name: '668', r: 0.4, g: 0.286274509803922, b: 0.458823529411765},
5954		{name: '669', r: 0.27843137254902, g: 0.168627450980392, b: 0.349019607843137},
5955		{name: '670', r: 0.949019607843137, g: 0.83921568627451, b: 0.847058823529412},
5956		{name: '671', r: 0.937254901960784, g: 0.776470588235294, b: 0.827450980392157},
5957		{name: '672', r: 0.917647058823529, g: 0.666666666666667, b: 0.768627450980392},
5958		{name: '673', r: 0.87843137254902, g: 0.549019607843137, b: 0.698039215686274},
5959		{name: '674', r: 0.827450980392157, g: 0.419607843137255, b: 0.619607843137255},
5960		{name: '675', r: 0.737254901960784, g: 0.219607843137255, b: 0.466666666666667},
5961		{name: '676', r: 0.627450980392157, g: 0, b: 0.329411764705882},
5962		{name: '677', r: 0.929411764705882, g: 0.83921568627451, b: 0.83921568627451},
5963		{name: '678', r: 0.917647058823529, g: 0.8, b: 0.807843137254902},
5964		{name: '679', r: 0.898039215686275, g: 0.749019607843137, b: 0.776470588235294},
5965		{name: '680', r: 0.827450980392157, g: 0.619607843137255, b: 0.686274509803922},
5966		{name: '681', r: 0.717647058823529, g: 0.447058823529412, b: 0.556862745098039},
5967		{name: '682', r: 0.627450980392157, g: 0.317647058823529, b: 0.458823529411765},
5968		{name: '683', r: 0.498039215686275, g: 0.156862745098039, b: 0.309803921568627},
5969		{name: '684', r: 0.937254901960784, g: 0.8, b: 0.807843137254902},
5970		{name: '685', r: 0.917647058823529, g: 0.749019607843137, b: 0.768627450980392},
5971		{name: '686', r: 0.87843137254902, g: 0.666666666666667, b: 0.729411764705882},
5972		{name: '687', r: 0.788235294117647, g: 0.537254901960784, b: 0.619607843137255},
5973		{name: '688', r: 0.698039215686274, g: 0.4, b: 0.517647058823529},
5974		{name: '689', r: 0.576470588235294, g: 0.258823529411765, b: 0.4},
5975		{name: '690', r: 0.43921568627451, g: 0.137254901960784, b: 0.258823529411765},
5976		{name: '691', r: 0.937254901960784, g: 0.819607843137255, b: 0.788235294117647},
5977		{name: '692', r: 0.909803921568627, g: 0.749019607843137, b: 0.729411764705882},
5978		{name: '693', r: 0.858823529411765, g: 0.658823529411765, b: 0.647058823529412},
5979		{name: '694', r: 0.788235294117647, g: 0.549019607843137, b: 0.549019607843137},
5980		{name: '695', r: 0.698039215686274, g: 0.419607843137255, b: 0.43921568627451},
5981		{name: '696', r: 0.556862745098039, g: 0.27843137254902, b: 0.286274509803922},
5982		{name: '697', r: 0.498039215686275, g: 0.219607843137255, b: 0.227450980392157},
5983		{name: '698', r: 0.968627450980392, g: 0.819607843137255, b: 0.8},
5984		{name: '699', r: 0.968627450980392, g: 0.749019607843137, b: 0.749019607843137},
5985		{name: '700', r: 0.949019607843137, g: 0.647058823529412, b: 0.666666666666667},
5986		{name: '701', r: 0.909803921568627, g: 0.529411764705882, b: 0.556862745098039},
5987		{name: '702', r: 0.83921568627451, g: 0.376470588235294, b: 0.427450980392157},
5988		{name: '703', r: 0.717647058823529, g: 0.219607843137255, b: 0.266666666666667},
5989		{name: '704', r: 0.619607843137255, g: 0.156862745098039, b: 0.156862745098039},
5990		{name: '705', r: 0.976470588235294, g: 0.866666666666667, b: 0.83921568627451},
5991		{name: '706', r: 0.988235294117647, g: 0.788235294117647, b: 0.776470588235294},
5992		{name: '707', r: 0.988235294117647, g: 0.67843137254902, b: 0.686274509803922},
5993		{name: '708', r: 0.976470588235294, g: 0.556862745098039, b: 0.6},
5994		{name: '709', r: 0.949019607843137, g: 0.407843137254902, b: 0.466666666666667},
5995		{name: '710', r: 0.87843137254902, g: 0.258823529411765, b: 0.317647058823529},
5996		{name: '711', r: 0.819607843137255, g: 0.176470588235294, b: 0.2},
5997		{name: '712', r: 1, g: 0.827450980392157, b: 0.666666666666667},
5998		{name: '713', r: 0.976470588235294, g: 0.788235294117647, b: 0.63921568627451},
5999		{name: '714', r: 0.976470588235294, g: 0.729411764705882, b: 0.509803921568627},
6000		{name: '715', r: 0.988235294117647, g: 0.619607843137255, b: 0.286274509803922},
6001		{name: '716', r: 0.949019607843137, g: 0.517647058823529, b: 0.0666666666666667},
6002		{name: '717', r: 0.827450980392157, g: 0.427450980392157, b: 0},
6003		{name: '718', r: 0.749019607843137, g: 0.356862745098039, b: 0},
6004		{name: '719', r: 0.956862745098039, g: 0.819607843137255, b: 0.686274509803922},
6005		{name: '720', r: 0.937254901960784, g: 0.768627450980392, b: 0.619607843137255},
6006		{name: '721', r: 0.909803921568627, g: 0.698039215686274, b: 0.509803921568627},
6007		{name: '722', r: 0.819607843137255, g: 0.556862745098039, b: 0.329411764705882},
6008		{name: '723', r: 0.729411764705882, g: 0.458823529411765, b: 0.188235294117647},
6009		{name: '724', r: 0.556862745098039, g: 0.286274509803922, b: 0.0196078431372549},
6010		{name: '725', r: 0.458823529411765, g: 0.219607843137255, b: 0.00784313725490196},
6011		{name: '726', r: 0.929411764705882, g: 0.827450980392157, b: 0.709803921568627},
6012		{name: '727', r: 0.886274509803922, g: 0.749019607843137, b: 0.607843137254902},
6013		{name: '728', r: 0.827450980392157, g: 0.658823529411765, b: 0.486274509803922},
6014		{name: '729', r: 0.756862745098039, g: 0.556862745098039, b: 0.376470588235294},
6015		{name: '730', r: 0.666666666666667, g: 0.458823529411765, b: 0.247058823529412},
6016		{name: '731', r: 0.447058823529412, g: 0.247058823529412, b: 0.0392156862745098},
6017		{name: '732', r: 0.376470588235294, g: 0.2, b: 0.0392156862745098},
6018		{name: '801', r: 0, g: 0.666666666666667, b: 0.8},
6019		{name: '801 2X', r: 0, g: 0.537254901960784, b: 0.686274509803922},
6020		{name: '802', r: 0.376470588235294, g: 0.866666666666667, b: 0.286274509803922},
6021		{name: '802 2X', r: 0.109803921568627, g: 0.807843137254902, b: 0.156862745098039},
6022		{name: '803', r: 1, g: 0.929411764705882, b: 0.219607843137255},
6023		{name: '803 2X', r: 1, g: 0.847058823529412, b: 0.0862745098039216},
6024		{name: '804', r: 1, g: 0.576470588235294, b: 0.219607843137255},
6025		{name: '804 2X', r: 1, g: 0.498039215686275, b: 0.117647058823529},
6026		{name: '805', r: 0.976470588235294, g: 0.349019607843137, b: 0.317647058823529},
6027		{name: '805 2X', r: 0.976470588235294, g: 0.227450980392157, b: 0.168627450980392},
6028		{name: '806', r: 1, g: 0, b: 0.576470588235294},
6029		{name: '806 2X', r: 0.968627450980392, g: 0.00784313725490196, b: 0.486274509803922},
6030		{name: '807', r: 0.83921568627451, g: 0, b: 0.619607843137255},
6031		{name: '807 2X', r: 0.749019607843137, g: 0, b: 0.549019607843137},
6032		{name: '808', r: 0, g: 0.709803921568627, b: 0.607843137254902},
6033		{name: '808 2X', r: 0, g: 0.627450980392157, b: 0.529411764705882},
6034		{name: '809', r: 0.866666666666667, g: 0.87843137254902, b: 0.0588235294117647},
6035		{name: '809 2X', r: 0.83921568627451, g: 0.83921568627451, b: 0.0470588235294118},
6036		{name: '810', r: 1, g: 0.8, b: 0.117647058823529},
6037		{name: '810 2X', r: 1, g: 0.737254901960784, b: 0.129411764705882},
6038		{name: '811', r: 1, g: 0.447058823529412, b: 0.27843137254902},
6039		{name: '811 2X', r: 1, g: 0.329411764705882, b: 0.0862745098039216},
6040		{name: '812', r: 0.988235294117647, g: 0.137254901960784, b: 0.4},
6041		{name: '812 2X', r: 0.988235294117647, g: 0.0274509803921569, b: 0.309803921568627},
6042		{name: '813', r: 0.898039215686275, g: 0, b: 0.6},
6043		{name: '813 2X', r: 0.819607843137255, g: 0, b: 0.517647058823529},
6044		{name: '814', r: 0.549019607843137, g: 0.376470588235294, b: 0.756862745098039},
6045		{name: '814 2X', r: 0.43921568627451, g: 0.247058823529412, b: 0.686274509803922 },
6046        { name: 'Black', r: 0, g: 0, b: 0 },
6047        { name: 'White', r: 1, g: 1, b: 1 }
6048	];
6049});;
6050/** Class representing the Product Grid. */
6051var ProductGrid = (function () {
6052    //Current indexes within quantity ranges
6053    var indexes = {};
6054    //The product price grids
6055    var priceGrids = {};
6056    //The product quantity ranges
6057    var quantityRanges = {};
6058    //The selected quantity range
6059    var selectedQuantityRange = {};
6060
6061    /**
6062     * Initialize the product grids and quantities in order to better extract grid data in a predicable format
6063     * @param {object} grids - The product pricing grids
6064     * @param {object} quantities - The product quantity ranges
6065     */
6066    function initGrids(grids, quantities) {
6067        if (typeof (grids) !== 'undefined' && typeof (quantities) !== 'undefined') {
6068            priceGrids = grids;
6069            quantityRanges = quantities;
6070
6071            for (grid in priceGrids) {
6072                indexes[grid] = null;
6073            }
6074        }
6075    }
6076
6077    /**
6078     * Update the grid with a highlighted cell corresponding to the correct quantity range
6079     * @param {object} table - The target table to highlight cell
6080     * @param {number} decorationMethodId - The decoration method ID
6081     * @param {number} totalQuantityCount - The total quantity count 
6082     */
6083    function updateGrid(table, decorationMethodId, totalQuantityCount) {
6084        if (totalQuantityCount > 0) {
6085            //Filter passed in quantity ranges in an attempt to match the range with passed in quantity count
6086            selectedQuantityRange = quantityRanges[decorationMethodId].filter(function (value) {
6087                if ((totalQuantityCount >= value.QuantityFrom) && ((totalQuantityCount <= value.QuantityTo) || (!value.QuantityTo)))
6088                    return value;
6089            })[0];
6090
6091            if (typeof (selectedQuantityRange) !== 'undefined') {
6092                $(table).find('td').removeClass('bolded');
6093
6094                if (table.hasClass('table-horizontal')) {
6095                    $(table).find('td:nth-of-type(' + selectedQuantityRange.Index + ')').addClass('bolded');
6096                } else if (table.hasClass('table-vertical')) {
6097                    $(table).find('tr:nth-of-type(' + (selectedQuantityRange.Index + 1) + ') td').addClass('bolded');
6098                }
6099
6100                if (indexes[decorationMethodId] != selectedQuantityRange.Index) {
6101                    //Set the global index to the new index 
6102                    indexes[decorationMethodId] = selectedQuantityRange.Index;
6103
6104                    if (table.hasClass('table-horizontal')) {
6105                        //Highlight the cell once when the range changes
6106                        $(table).find('td:nth-of-type(' + selectedQuantityRange.Index + ')').effect("highlight", { c
6106olor: '#333' }, 500);
6107                    } else if (table.hasClass('table-vertical')) {
6108                        //Highlight the cell once when the range changes
6109                        $(table).find('tr:nth-of-type(' + (selectedQuantityRange.Index + 1) + ') td').effect("highlight", { color: '#333' }, 500);
6110                    }
6111                }
6112            } else {
6113                indexes[decorationMethodId] = null;
6114                $(table).find('td').removeClass('bolded');
6115            }
6116        }
6117    }
6118
6119    /**
6120     * Get the current price of the selected quantity range
6121     * @param {number} decorationMethodId - The decoration method ID
6122     * @param {array} gridIdArray - The grid ID array
6123     * @param {object} colourElement - The colour object
6124     * @return {number} The current price
6125     */
6126    function getPrice(decorationMethodId, gridIdArray, colourElement) {
6127        var price = null;
6128
6129        if (typeof (selectedQuantityRange) !== 'undefined') {
6130            if (typeof (priceGrids[decorationMethodId]) !== 'undefined') {
6131                //Get possible grids for a chosen colour
6132                var colourGridIdArray = colourElement.length ? colourElement.data('grid-id').toString().split(',') : [];
6133                //Find intersecting grids between the colour and quantity, if any. There should only be one.
6134                var intersectingGrids = gridIdArray.filter(function (n) {
6135                    return colourGridIdArray.indexOf(n) > -1;
6136                });
6137                var gridId = intersectingGrids.length ? intersectingGrids[0] : 0;
6138
6139                if (typeof (priceGrids[decorationMethodId][gridId]) !== 'undefined') {
6140                    //If the grid id key is defined, set the price to match the ListPrice of the selected quantity range index in a given decoration method
6141                    price = priceGrids[decorationMethodId][gridId][0].Prices.filter(function (value) {
6142                        if (selectedQuantityRange.Index == value.Index)
6143                            return value;
6144                    })[0].ListPrice;
6145                } else {
6146                    //If not, simply look in the first price grid available in a given decoration method
6147                    price = priceGrids[decorationMethodId][gridIdArray[0]][0].Prices.filter(function (value) {
6148                        if (selectedQuantityRange.Index == value.Index)
6149                            return value;
6150                    })[0].ListPrice;
6151                }
6152            }
6153        }
6154
6155        return price;
6156    }
6157
6158    /**
6159     * Get the current setup charge of the selected quantity range
6160     * @param {number} decorationMethodId - The decoration method ID
6161     * @return {number} The current price
6162     */
6163    function getSetupCharge(decorationMethodId) {
6164        var price = null;
6165
6166        if (typeof (selectedQuantityRange) !== 'undefined') {
6167            if (typeof (priceGrids[decorationMethodId]) !== 'undefined') {
6168                //Get the setup charge of the first grid available in a given decoration method
6169                price = priceGrids[decorationMethodId][Object.keys(priceGrids[decorationMethodId])[0]][0].SetupCharge;
6170            }
6171        }
6172
6173        return price;
6174    }
6175
6176    /**
6177     * Get maximum number of decoration colours for a given decoration method
6178     * @param {number} decorationMethodId - The decoration method ID
6179     * @return {number} The current price
6180     */
6181    function getDecorationColourMax(decorationMethodId) {
6182        var max = 1;
6183
6184        if (typeof (selectedQuantityRange) !== 'undefined') {
6185            if (typeof (priceGrids[decorationMethodId][Object.keys(priceGrids[decorationMethodId])[0]]) !== 'undefined') {
6186                //Look in the first price grid available in a given decoration method to match the AdditionalCost of the selected quantity range index
6187                var additionalCost = (priceGrids[decorationMethodId][Object.keys(priceGrids[decorationMethodId])[0]][0].Prices.filter(function (value) {
6188                    if (selectedQuantityRange.Index == value.Index)
6189                        return value;
6190                })[0].AdditionalCost);
6191
6192                //Get the maximum CostIndex and set it as the max
6193                if (additionalCost.length)
6194                    max = Math.max.apply(Math, additionalCost.map(function (value) { return value.CostIndex; }))
6195            }
6196        }
6197
6198        return max;
6199    }
6200
6201    /**
6202     * Get the decoration colour cost of a given colour in a given decoration method
6203     * @param {number} decorationMethodId - The decoration method ID
6204     * @param {number} index - The cost index
6205     * @return {number} The decoration colour cost
6206     */
6207    function getDecorationColourCost(decorationMethodId, index) {
6208        var cost = null;
6209
6210        if (typeof (selectedQuantityRange) !== 'undefined') {
6211            if (typeof (priceGrids[decorationMethodId][Object.keys(priceGrids[decorationMethodId])[0]]) !== 'undefined') {
6212                //Look in the first price grid available in a given decoration method to match the Prices of the selected quantity range index
6213                var priceGrid = priceGrids[decorationMethodId][Object.keys(priceGrids[decorationMethodId])[0]][0].Prices.filter(function (value) {
6214                    if (selectedQuantityRange.Index == value.Index)
6215                        return value;
6216                });
6217
6218                if (priceGrid.length) {
6219                    //Find the cost index in the AdditionalCost array matching the index received
6220                    var colourArr = priceGrid[0].AdditionalCost.filter(function (colour) {
6221                        if (colour.CostIndex == index)
6222                            return colour;
6223                    });
6224
6225                    if (colourArr.length)
6226                        cost = colourArr[0].Sell;
6227                }
6228            }
6229        }
6230
6231        return cost;
6232    }
6233
6234    /**
6235     * Get the decoration colour setup charge in a given decoration method
6236     * @param {number} decorationMethodId - The decoration method ID
6237     * @param {number} index - The cost index
6238     * @return {number} The decoration colour setup charge
6239     */
6240    function getDecorationColourSetupCharge(decorationMethodId, index) {
6241        var setupCharge = null;
6242
6243        if (typeof (selectedQuantityRange) !== 'undefined') {
6244            if (typeof (priceGrids[decorationMethodId][Object.keys(priceGrids[decorationMethodId])[0]]) !== 'undefined') {
6245                //Look in the first price grid available in a given decoration method to match the Prices of the selected quantity range index
6246                var priceGrid = priceGrids[decorationMethodId][Object.keys(priceGrids[decorationMethodId])[0]][0].Prices.filter(function (value) {
6247                    if (selectedQuantityRange.Index == value.Index)
6248                        return value;
6249                });
6250
6251                if (priceGrid.length) {
6252                    //Find the cost index in the AdditionalCost array matching the index received
6253                    var colourArr = priceGrid[0].AdditionalCost.filter(function (colour) {
6254                        if (colour.CostIndex == index)
6255                            return colour;
6256                    });
6257
6258                    //If there is a match, set setupCharge to the SetupCharge property
6259                    if (colourArr.length)
6260                        setupCharge = colourArr[0].SetupCharge;
6261                }
6262            }
6263        }
6264
6265        return setupCharge;
6266    }
6267
6268    /**
6269     * Get the grid ID of a given colour in a given decoration method
6270     * @param {number} decorationMethodId - The decoration method ID
6271     * @param {number} colourId - The colour id
6272     * @return {number} The grid id of the colour
6273     */
6274    function getColourGridId(decorationMethodId, colourId) {
6275        var id = null;
6276
6277        if (typeof (selectedQuantityRange) !== 'undefined') {
6278            if (typeof (priceGrids[decorationMethodId]) !== 'undefined') {
6279                if (typeof (colourId) !== 'undefined') {
6280                    for (grid in priceGrids[decorationMethodId]) {
6281                        //Look through all grids in a given decoration method
6282                        priceGrids[decorationMethodId][grid].forEach(function (gridValue) {
6283                            //If the colour is found in the grid that matches the passed in colour id
6284                            var match = gridValue.Colors.filter(function (color) {
6285                                if (color.Id == colourId)
6286                                    return color.Id;
6287                            })[0];
6288
6289                            //Set the matched grid id
6290                            if (match) {
6291                                id = match['GridId'];
6292                            }
6293                        });
6294                    }
6295                } else {
6296                    //If not, simply get the first grid ID available in a given decoration method
6297                    id = priceGrids[decorationMethodId][Object.keys(priceGrids[decorationMethodId])[0]][0].GridId;
6298                }
6299            }
6300        }
6301
6302        return id;
6303    }
6304
6305    /**
6306     * Get the supported colours of a given decoration method
6307     * @param {number} decorationMethodId - The decoration method ID
6308     * @return {array} The array of colours
6309     */
6310    function getDecorationMethodColours(decorationMethodId) {
6311        var colours = [];
6312
6313        if (typeof (selectedQuantityRange) !== 'undefined') {
6314            if (typeof (priceGrids[decorationMethodId]) !== 'undefined') {
6315                for (grid in priceGrids[decorationMethodId]) {
6316                    //Look through all grids in a given decoration method and return all enabled colours
6317                    var enabledColors = priceGrids[decorationMethodId][grid][0].Colors.filter(function (value) {
6318                        if (value.Enabled)
6319                            return value;
6320                    });
6321
6322                    enabledColors.forEach(function (value) {
6323                        var colour = [];
6324
6325                        //If the colour is already in the colours array, get a reference to it
6326                        colour = colours.filter(function (color) {
6327                            return color.Id == value.Id;
6328                        });
6329
6330                        //If we are editing a colour, make sure to append all possible grid IDs
6331                        if (colour.length) {
6332                            var gridIds = colour[0]['GridId'].toString().split();
6333                            gridIds.push(grid);
6334                            colour[0]['GridId'] = gridIds.join(',');
6335                        } else {
6336                            //Set the grid id for each enabled colour to the current grid
6337                            value['GridId'] = parseInt(grid);
6338                            colours.push(value);
6339                        }
6340                    });
6341                }
6342            }
6343        }
6344
6345        return colours;
6346    }
6347
6348    /**
6349     * Get the price grid HTML of a given decoration method
6350     * @param {number} decorationMethodId - The decoration method ID
6351     * @return {string} The price grid HTML
6352     */
6353    function getGridHtml(decorationMethodId) {
6354        var source = $('#productPricingGrid').html();
6355        var template = Handlebars.compile(source);
6356        var html = '';
6357
6358        if (typeof (decorationMethodId) !== 'undefined') {
6359            //Pass in the selected grid and selected quantity range to the template
6360            var selectedMethodGrids = priceGrids[decorationMethodId];
6361            var selectedQuantityRanges = quantityRanges[decorationMethodId];
6362
6363            //Get the first grid available in the decoration method grids
6364            var firstGrid = Object.keys(selectedMethodGrids)[0];
6365            var price = 0;
6366            //Get first list price in prices array
6367            if (selectedMethodGrids[firstGrid][0].hasOwnProperty('Prices') && selectedMethodGrids[firstGrid][0].Prices.length > 0)
6368                price = numeral().unformat(selectedMethodGrids[firstGrid][0].Prices[0].ListPrice);
6369
6370            html = template({
6371                quantities: selectedQuantityRanges,
6372                comment: selectedMethodGrids[firstGrid][0].Comment,
6373                grids: selectedMethodGrids,
6374                hidePrice: (price > 0) ? false : true
6375            });
6376        }
6377
6378        return html;
6379    }
6380
6381    /**
6382     * Get the vertical price grid HTML of a given decoration method
6383     * @param {number} decorationMethodId - The decoration method ID
6384     * @return {string} The price grid HTML
6385     */
6386    function getVerticalGridHtml(decorationMethodId) {
6387        var source = $('#productPricingVerticalGrid').html();
6388        var template = Handlebars.compile(source);
6389        var html = '';
6390
6391        if (typeof (decorationMethodId) !== 'undefined') {
6392            //Pass in the selected grid and selected quantity range to the template
6393            var selectedMethodGrids = priceGrids[decorationMethodId];
6394            var selectedQuantityRanges = quantityRanges[decorationMethodId];
6395
6396            //Get the first grid available in the decoration method grids
6397            var firstGrid = Object.keys(selectedMethodGrids)[0];
6398            var price = 0;
6399            //Get first list price in prices array
6400            if (selectedMethodGrids[firstGrid][0].hasOwnProperty('Prices') && selectedMethodGrids[firstGrid][0].Prices.length > 0)
6401                price = numeral().unformat(selectedMethodGrids[firstGrid][0].Prices[0].ListPrice);
6402
6403            //Return a custom data format for selected quantity ranges and leave price as an empty array
6404            var quantitiesPrices = selectedQuantityRanges.map(function (value) {
6405                return {
6406                    QuantityFrom: value.QuantityFrom,
6407                    QuantityTo: value.QuantityTo,
6408                    Prices: []
6409                }
6410            });
6411
6412            //Loop through selected method grids and push the grid prices
6413            for (grid in selectedMethodGrids) {
6414                for (var i = 0; i < selectedMethodGrids[grid][0].Prices.length; i++) {
6415                    if (typeof (quantitiesPrices[i]) !== 'undefined') {
6416                        quantitiesPrices[i].Prices.push(selectedMethodGrids[grid][0].Prices[i]);
6417                    }
6418                }
6419            }
6420
6421            html = template({
6422                quantities: quantitiesPrices,
6423                comment: selectedMethodGrids[firstGrid][0].Comment,
6424                grids: selectedMethodGrids,
6425                hidePrice: (price > 0) ? false : true
6426            });
6427        }
6428
6429        return html;
6430    }
6431
6432    /**
6433     * Get quantity range of a given selected decoration method
6434     * @param {number} decorationMethodId - The decoration method ID
6435     * @return {array} The quantity range array
6436     */
6437    function getQuantityRange(decorationMethodId) {
6438        if (typeof (decorationMethodId) !== 'undefined') {
6439            return quantityRanges[decorationMethodId];
6440        }
6441
6442        return [];
6443    }
6444
6445    /**
6446     * Get valid grid ids for selected colour
6447     * @param {number} decorationMethodId - The decoration method ID
6448     * @param {number} colourId - The colour ID
6449     * @return {object} The grid ids object
6450     */
6451    function getColorEnabledGrids(decorationMethodId, colourId) {
6452        var gridIds = {};
6453
6454        if (decorationMethodId && colourId) {
6455            for (grid in priceGrids[decorationMethodId]) {
6456                priceGrids[decorationMethodId][grid].forEach(function (gridValue) {
6457                    //Loop through all grids in a given decoration method and attempt to match passed in colour in the Colors array
6458                    var match = gridValue.Colors.filter(function (color) {
6459                        if (color.Id == colourId)
6460                            return color.Id;
6461                    })[0];
6462
6463                    //If there is a match, create a new grid ID key with array as value and push the color 
6464                    if (match) {
6465                        if (typeof (gridIds[grid]) === 'undefined') {
6466                            gridIds[grid] = [];
6467                        }
6468
6469                        gridIds[grid].push(match['Id'])
6470                    }
6471                });
6472            }
6473        }
6474        return gridIds;
6475    }
6476
6477    return {
6478        InitGrids: function (grids, quantities) {
6479            initGrids(grids, quantities);
6480        },
6481        GetQuantityRange: function (decorationMethodId) {
6482            return getQuantityRange(decorationMethodId);
6483        },
6484        UpdateGrid: function (table, decorationMethodId, totalQuantityCount) {
6485            updateGrid(table, decorationMethodId, totalQuantityCount)
6486        },
6487        GetPrice: function (decorationMethodId, gridIdArray, colourElement) {
6488            return getPrice(decorationMethodId, gridIdArray, colourElement);
6489        },
6490        GetSetupCharge: function (decorationMethodId) {
6491            return getSetupCharge(decorationMethodId);
6492        },
6493        GetGridHtml: function (decorationMethodId) {
6494            return getGridHtml(decorationMethodId);
6495        },
6496        GetVerticalGridHtml: function (decorationMethodId) {
6497            return getVerticalGridHtml(decorationMethodId);
6498        },
6499        GetDecorationColourMax: function (decorationMethodId) {
6500            return getDecorationColourMax(decorationMethodId);
6501        },
6502        GetDecorationColourCost: function (decorationMethodId, index) {
6503            return getDecorationColourCost(decorationMethodId, index);
6504        },
6505        GetDecorationColourSetupCharge: function (decorationMethodId, index) {
6506            return getDecorationColourSetupCharge(decorationMethodId, index);
6507        },
6508        GetColourGridId: function (decorationMethodId, colourGridId) {
6509            return getColourGridId(decorationMethodId, colourGridId);
6510        },
6511        GetDecorationMethodColours: function (decorationMethodId) {
6512            return getDecorationMethodColours(decorationMethodId);
6513        },
6514        GetColorEnabledGrids: function (decorationMethodId, colourId) {
6515            return getColorEnabledGrids(decorationMethodId, colourId);
6516        }
6517    }
6518}());;
6519/** Class representing the Product Configure pages. */
6520var ProductConfigure = (function () {
6521    //Reference to the product configure quantity grids
6522    var quantityGrids = [];
6523    //Reference to the product configure quantity count
6524    var currentQuantityCount = 0;
6525    //Reference to the selected colour label
6526    var selectedColourLabel = null;
6527    //Table representing the product configure total data
6528    var table = $("#product-config-total table");
6529    //Reference to the current product config page
6530    var currentPage = 1;
6531    //Used to hold product parameters
6532    var productParams = {};
6533    //Used to hold Product configure parameters 
6534    var productConfigureParams = {};
6535    //Used to load cart line items when editing an order item
6536    var productDisplayParams = {};
6537    //Used to hold product price grids
6538    var priceGrids = {};
6539    //Used to hold all validation rules for product config areas
6540    var validateRules = {};
6541    //Used to hold references to all valid forms on product config pages
6542    var validForms = {};
6543    //Used to hold product colours
6544    var coloursArr = [];
6545    //Default list of PMS colours
6546    var filteredDecorationColours = [{ "HexCode": "#000000", "Pms": "Black" }, { "HexCode": "#0038a8", "Pms": "286" }, { "HexCode": "#876028", "Pms": "464" }, { "HexCode": "#8c2633", "Pms": "202" }, { "HexCode": "#00bce2", "Pms": "306" }, { "HexCode": "#603311", "Pms": "469" }, { "HexCode": "#a09151", "Pms": "4505" }, { "HexCode": "#006847", "Pms": "3425" }, { "HexCode": "#ddcc6b", "Pms": "458" }, { "HexCode": "#009e49", "Pms": "355" }, { "HexCode": "#60dd49", "Pms": "802" }, { "HexCode": "#666d70", "Pms": "431" }, { "HexCode": "#75aadb", "Pms": "284" }, { "HexCode": "#afaaa3", "Pms": "422" }, { "HexCode": "#00386b", "Pms": "295" }, { "HexCode": "#f47c00", "Pms": "1505" }, { "HexCode": "#F3B6D2", "Pms": "165 2X" }, { "HexCode": "#ff0093", "Pms": "806" }, { "HexCode": "#fc8c99", "Pms": "183" }, { "HexCode": "#72166b", "Pms": "259" }, { "HexCode": "#8c60c1", "Pms": "814" }, { "HexCode": "#e8112d", "Pms": "185" }, { "HexCode": "#d81e05", "Pms": "485" }, { "HexCode": "#005bbf", "Pms": "2935" }, { "HexCode": "#e0dbb5", "Pms": "5875" }, { "HexCode": "#008789", "Pms": "321" }, { "HexCode": "#FFFFFF", "Pms": "White" }, { "HexCode": "#f9e814", "Pms": "102" }];
6547
6548    /**
6549     * Initialize the product configure page
6550     * @param {object} params - The parameters passed in to properly initialize Product Configure
6551     */
6552    function init(params) {
6553        if (typeof (params) !== 'undefined') {
6554            productParams = params;
6555            productConfigureParams.CurrentQuantity = [];
6556
6557            priceGrids = Main.GetFormattedPriceGrid(productParams.PriceGrids, productParams.Sizes);
6558
6559            if (productParams.hasOwnProperty('ProductId') && !isNaN(productParams.ProductId))
6560                productConfigureParams.CurrentProductId = productParams.ProductId;
6561            //If product has sizes, populate current quantity array with correct size ids
6562            if ((productParams.hasOwnProperty('SizeId')) && (productParams.SizeId > 0) && (productParams.hasOwnProperty('Quantity')) && (productParams.Quantity > 0)) {
6563                productConfigureParams.CurrentQuantity.push(
6564                    {
6565                        Id: productParams.SizeId,
6566                        Quantity: productParams.Quantity,
6567                        GridId: productParams.GridId
6568                    }
6569                );
6570            //If no sizes available, size ID will be null
6571            } else if (productParams.hasOwnProperty('Quantity') && (productParams.Quantity > 0)) {
6572                productConfigureParams.CurrentQuantity.push(
6573                    {
6574                        Id: null,
6575                        Quantity: productParams.Quantity,
6576                        GridId: productParams.GridId
6577                    }
6578                );
6579            }
6580            if (productParams.hasOwnProperty('DecorationMethodId') && (productParams.DecorationMethodId > 0))
6581                productConfigureParams.CurrentMethod = productParams.DecorationMethodId;
6582            if (productParams.hasOwnProperty('ColourId') && (productParams.ColourId > 0))
6583                productConfigureParams.CurrentColour = productParams.ColourId;
6584            //If editing and cart items already found, display them
6585            if (productParams.hasOwnProperty('CartItems') && productParams.CartItems !== null) {
6586                initCartItems(productParams.CartItems);
6587                displayCartItems(productParams.CartItems);
6588            }
6589            if (productParams.hasOwnProperty('Colors') && productParam
6589s.Colors.length)
6590                coloursArr = productParams.Colors;
6591        }
6592
6593        //Output the HTML for the current page
6594        displayPageTemplate(currentPage);
6595        //Update the breadcrumb to save search parameters
6596        updateBreadcrumb();
6597
6598        //If JS media queries are supported
6599        if (matchMedia) {
6600            //Match all viewports below 992px in width
6601            var smallMediaMatch = window.matchMedia("(max-width: 991px)");
6602            //Subscribe smallWidthChange to match media
6603            smallMediaMatch.addListener(smallWidthChange);
6604            //Immediately call small width change with active settings
6605            smallWidthChange(smallMediaMatch);
6606        }
6607    }
6608
6609    /**
6610     * If user hasn't already selected the same product with same colour, get selected value of the custom control for product colour (which is not in a form) and set the original control with this value to ensure that it can be serialized in a form
6611     * @param {object} listItem - The link in the list item of the unordered list that was created for the custom control
6612     */
6613    function selectProductSwatch(listItem) {
6614        if (typeof ($(listItem).data('id')) !== 'undefined' && Number.isInteger($(listItem).data('id'))) {
6615            var options = {
6616                callback: {
6617                    done: function (response) {
6618                        //If another product was found with the same colour 
6619                        if ((response.ItemId > 0) && (response.ItemId != productParams.ItemId)) {
6620                            swal({
6621                                title: 'Duplicate Product',
6622                                text: "There is a duplicate product in cart with the same colour. Would you like to load this product and edit it instead?",
6623                                type: 'warning',
6624                                showCancelButton: true,
6625                                confirmButtonClass: 'swal2-confirm swal2-styled',
6626                                cancelButtonClass: 'swal2-cancel swal2-styled'
6627                            }).then(function () {
6628                                //If the user clicks OK, edit the product that was found with the same colour
6629                                location.href = '/product/configure/?productid=' + productConfigureParams.CurrentProductId + '&itemid=' + response.ItemId + '&edit=true';
6630                            }, function (dismiss) {
6631                            });
6632                        } else {
6633                            $(listItem).closest('ul').find('li > a').removeClass('active');
6634                            //Get the original form-friendly element that this custom control corresponds to
6635                            var select = $(listItem).closest('ul').data('original-element');
6636                            //Set its value to the value that was clicked on the custom control
6637                            $(select).val($(listItem).data('id')).change();
6638
6639                            //If the current product we are configuring only has a single quantity
6640                            if ($('#product-config-quantity-form #single-quantity').length) {
6641                                //Set the grid id to the passed in value
6642                                $('#product-config-quantity-form #single-quantity').data('grid-id', $(listItem).data('grid-id'));
6643
6644                                //Update quantity and totals
6645                                ProductConfigure.SetQuantity($('#product-config-quantity-form input'));
6646                                ProductConfigure.UpdateTotalColumn(ProductConfigureSteps.GetPageObj(1));
6647                            }
6648
6649                            //Add colour to global product colour
6650                            setColour($(listItem));
6651                        }
6652                    },
6653                    fail: function () {
6654                        // Throw an exception if the request failed.
6655                        AsyncException({
6656                            ErrorType: 2,
6657                            Message: 'An error occurred while attempting to check if product exists in selected swatch.'
6658                        });
6659                    }
6660                },
6661                params: {
6662                    colourId: $(listItem).data('id'),
6663                    productId: productConfigureParams.CurrentProductId
6664                },
6665                method: 'GET'
6666            }
6667
6668            DataManager.process('Product', 'CheckColour', options);
6669        }
6670    }
6671
6672    /**
6673     * Sets the quantity values of the given product based on the quantity input group 
6674     * @param {object} quantitiesObj - The input(s) corresponding to the entered quantities
6675     */
6676    function setQuantity(quantitiesObj) {
6677        quantityGrids = [];
6678        currentQuantityCount = 0;
6679
6680        if ($(quantitiesObj).length) {
6681            $(quantitiesObj).each(function () {
6682                if (!isNaN($(this).val()) && $(this).val() > 0) {
6683                    //Get the current grid id of given input
6684                    var gridId = $(this).data('grid-id');
6685                        quantityGrids.push({
6686                            Id: parseInt($(this).attr('id')),
6687                            Quantity: parseInt($(this).val()),
6688                            GridId: parseInt(gridId)
6689                        });
6690
6691                    //Add the value to current quantity count
6692                    currentQuantityCount += parseInt($(this).val());
6693                }
6694            });
6695        }
6696
6697        var quantityRanges = ProductGrid.GetQuantityRange(getSelectedDecorationMethodId());
6698        productConfigureParams.CurrentQuantity = quantityGrids;
6699
6700        if (quantityRanges.length > 0) {
6701            //Get the difference between the lowest quantity and the current quantity
6702            var difference = quantityRanges[0].QuantityFrom - currentQuantityCount;
6703            //If the user has exceeded the minimum quantity count, display the current quantity
6704            if (difference <= 0)
6705                $('#product-selected-quantity').html(currentQuantityCount + " item(s)");
6706        }
6707
6708        //Redraw the product config pricing table
6709        $('#product-config-pricing-table').empty()
6710            .append(ProductGrid.GetGridHtml(getSelectedDecorationMethodId()))
6711            .append(ProductGrid.GetVerticalGridHtml(getSelectedDecorationMethodId()));
6712        //If there are any discount prices available, cross out the regular prices
6713        if (Main.GetDiscount() > 0)
6714            $('#product-config-pricing-table td span.regular-price').css('text-decoration', 'line-through');
6715
6716        //Update the product grid with the current quantity
6717        ProductGrid.UpdateGrid($('#product-config-pricing-table table:visible'), getSelectedDecorationMethodId(), currentQuantityCount);
6718    }
6719
6720    /**
6721     * Sets the colour of the given product
6722     * @param {object} colourObj - Reference to the colour element
6723     */
6724    function setColour(colourObj) {
6725        if (colourObj && colourObj.length) {
6726            selectedColourLabel = $(colourObj).data('label');
6727            $('#product-config-colour-form select').val($(colourObj).data('id'));
6728            $(colourObj).addClass('active');
6729
6730            productConfigureParams.CurrentColour = parseInt($(colourObj).data('id'));
6731
6732            var colors = $(colourObj).data('bg-color').split(",");
6733            var swatch;
6734
6735            //If more than 2 colours in list, display a dual tone swatch. If not, simply display a solid color
6736            if (colors.length > 1) {
6737                swatch = $('<span />', { 'class': 'swatch' }).css('background', 'linear-gradient(to bottom right, ' + colors[0] + ' 49%, ' + colors[1] + ' 51%)');
6738            } else {
6739                swatch = $('<span />', { 'class': 'swatch' }).css('background-color', colors[0]);
6740            }
6741
6742            //Set the display text to the current colour
6743            $('#product-selected-colour').html(
6744                $('<div />', { 'class': 'product-colours' }).append(
6745                    swatch,
6746                    $('<span />', { 'class': 'swatch-title', 'text': selectedColourLabel })
6747                )
6748            );
6749
6750            //Set the display text to the current colour in the total column
6751            $('.item-colour').each(function () {
6752                $(this).text(' ('+selectedColourLabel+')');
6753            });
6754        } else {
6755            //Reset all colour settings
6756            selectedColourLabel = null;
6757            $('#product-config-colour-form select').val('');
6758            productConfigureParams.CurrentColour = null;
6759
6760            $('#product-selected-colour').empty();
6761            $('.item-colour').empty();
6762        }
6763
6764        //Sets the quantity values of the given product based on the quantity input group 
6765        setQuantity($('#product-config-quantity-form input'));
6766        //Validate form passed in based on custom predefined validation rules on a given page
6767        validateForms(['product-config-quantity-form']);
6768        //Update total column on product configure with current page config object
6769        updateTotalColumn(ProductConfigureSteps.GetPageObj(1));
6770    }
6771
6772    /**
6773     * Update decoration colour based on serialized decoration colour form
6774     */
6775    function updateDecorationColour() {
6776        productConfigureParams.CurrentDecorationColours = [];
6777
6778        //Get reference to checked values in product configure decoration colour form
6779        var decorationColoursArr = $('#product-config-decoration-colour-form input');
6780        $(decorationColoursArr).each(function () {
6781            if ($(this).val()) {
6782                var selectedColour = ($(this).val() == 'White' || $(this).val() == 'Black') ? $(this).val() : 'PMS ' + $(this).val();
6783                var selectedColourValue = $(this).val();
6784                var selectedBgColour = $(this).data('bg-color');
6785
6786                productConfigureParams.CurrentDecorationColours.push(
6787                    {
6788                        Pms: selectedColourValue,
6789                        Color: selectedColour,
6790                        BgColor: selectedBgColour
6791                    }
6792                );
6793            }
6794        });
6795
6796        //If no product colours are present, re-append product colours container. If not, simply empty it
6797        if (!$('#product-decoration-selected-colour .product-colours').length) {
6798            $('#product-decoration-selected-colour').empty();
6799            $('<div />', { 'class': 'product-colours' }).appendTo($('#product-decoration-selected-colour'));
6800        } else {
6801            $('#product-decoration-selected-colour .product-colours').empty();
6802        }
6803
6804        //Append swatches to current decoration colour list
6805        productConfigureParams.CurrentDecorationColours.forEach(function (value) {
6806            $('<span />', { 'class': 'swatch' }).css('background-color',value.BgColor).appendTo($('#product-decoration-selected-colour .product-colours'));
6807            $('<span />', { 'class': 'swatch-title', 'text': value.Color }).appendTo('#product-decoration-selected-colour .product-colours');
6808        });
6809    }
6810
6811    /**
6812     * Add a decoration method for a given product
6813     * @param {object} methodObj - Reference to the method element
6814     */
6815    function setMethod(methodObj) {
6816        if (typeof (methodObj) !== 'undefined') {
6817            var currentMethod = methodObj.data('label');
6818            //Manually trigger the check event on the method element
6819            $(methodObj).iCheck('check');
6820            productConfigureParams.CurrentMethod = parseInt(methodObj.val());
6821
6822            //Hide the old decoration method image
6823            $('#product-config-decoration-method .decoration-method').hide();
6824            //Show the new decoration method image
6825            $('#product-config-decoration-method .decoration-method[data-decoration-id="' + methodObj.val() + '"]').show();
6826
6827            if (typeof (currentMethod) !== 'undefined')
6828                $('#product-selected-method').text(currentMethod);
6829            else
6830                $('#product-selected-method').empty();
6831        }
6832    }
6833
6834    /**
6835     * Add a decoration location for a given product
6836     * @param {object} locationObj - Reference to the location element
6837     */
6838    function setLocation(locationObj) {
6839        if (typeof (locationObj) !== 'undefined') {
6840            var selectedLocation = locationObj.data('label');
6841            //Manually trigger the check event on the location element
6842            $(locationObj).iCheck('check');
6843            productConfigureParams.CurrentLocation = parseInt(locationObj.val());
6844
6845            if (typeof (selectedLocation) !== 'undefined')
6846                $('#product-selected-location').text(selectedLocation);
6847            else
6848                $('#product-selected-location').empty();
6849        }
6850    }
6851
6852    /**
6853     * Add decoration artwork for a given product
6854     * @param {object} artworkObj - Reference to the artwork element
6855     */
6856    function setArtwork(artworkObj) {
6857        if (typeof (artworkObj) !== 'undefined') {
6858            var currentArtwork = artworkObj.data('label');
6859            //Manually trigger the check event on the artwork element
6860            $(artworkObj).iCheck('check');
6861            productConfigureParams.CurrentArtwork = parseInt(artworkObj.val());
6862
6863            if (typeof (currentArtwork) !== 'undefined')
6864                $('#product-selected-artwork').text(currentArtwork);
6865            else
6866                $('#product-selected-artwork').empty();
6867        }
6868    }
6869
6870    /**
6871     * Set artwork file based on passed in file options object
6872     * @param {object} fileObj - A object with file options
6873     */
6874    function setArtworkFile(fileObj) {
6875        if (typeof (fileObj) !== 'undefined') {
6876            productConfigureParams.CurrentArtworkFile = fileObj;
6877        }
6878    }
6879
6880    /**
6881     * Add comments for artwork options
6882     * @param {string} comments - A comment string
6883     */
6884    function setComments(comments) {
6885        if (comments.length) {
6886            $('#product-config-comments-form #comments').val(comments);
6887
6888            productConfigureParams.CurrentComments = comments;
6889        }
6890    }
6891
6892    /**
6893     * Update and display a given product configuration page
6894     * @param {number} page - Page number
6895     */
6896    function changePage(page) {
6897        currentPage = page;
6898        //Set all pages beyond next page to disabled
6899        updateDisabledPages();
6900   
6901        //Remove done class on each list item in progress bar
6902        $('.product-config-progress > li').each(function () {
6903            $(this).removeClass('done');
6904        });
6905        //If we are on any page besides the first...
6906        if (currentPage > 1) {
6907            //Add done class to all steps prior to the previous page on the progress bar
6908            for (var i = currentPage; i > 0; i--)
6909                $('.product-config-progress > li').eq(i - 1).attr('class', 'done');
6910        }
6911
6912        $('.product-config-progress > li').each(function () {
6913            //Remove active class from all steps in progress bar
6914            if ($(this).hasClass('active'))
6915                $(this).removeClass('active');
6916        });
6917        //Set the active class on the current step in progress bar
6918        $('.product-config-progress > li').eq((currentPage - 1)).attr('class', 'active');
6919        //Set the product config content to the HTML response
6920        displayPageTemplate(currentPage);
6921
6922        //Call the JavaScript for this specific page
6923        ProductConfigureSteps.UpdateJS(page);
6924    }
6925
6926    /**
6927     * Update the product config progress bar and add disabled class to all pages beyond the next page
6928     */
6929    function updateDisabledPages() {
6930        $('.product-config-progress li a').each(function () {
6931            if ($(this).data('page') >= (currentPage + 2)) {
6932                $(this).parent().addClass('disabled');
6933            } else {
6934                $(this).parent().removeClass('disabled');
6935            }
6936        });
6937    }
6938
6939    /**
6940     * Update product configure total column
6941     * @param {object} obj - Object that includes form groups with page number and titles
6942     */
6943    function updateTotalColumn(obj) {
6944        if (typeof (obj) !== 'undefined') {
6945            if (obj.forms.length) {
6946                var elements = [];
6947                var rowHtml = "";
6948                var validForms = getValidForms(obj);
6949
6950                if (validForms.length) {
6951                    for (var i = 0; i < obj.forms.length; i++) {
6952                        //Loop through all the forms and set elements variable to serialized key/values pairs that are not empty
6953                        elements = $('#' + obj.forms[i].name).serializeArray().filter(function (input) {
6954                            if (input.value != '')
6955                                return input;
6956                        });
6957
6958                        //Return validated status of a particular form name
6959                        if (getFormStatus(obj.forms[i].name)) {
6960                            if (elements.length) {
6961                                //Create a title and total row for a given form
6962                                createTitleRow(obj);
6963                                //Concatenate all total row HTML strings for the given form
6964                                rowHtml += getTotalRows(obj.forms[i].name, elements, obj.page);
6965                            }
6966                        }
6967                    }
6968                } else {
6969                    //If current quantity is below minimum...remove both title row and data
6970                    var nextTitle = $('#' + obj.rowTitle).nextAll('tr[id]').first();
6971
6972                    //Remove all titles and rows  for the given form
6973                    if ($('#' + obj.rowTitle).nextUntil(nextTitle, 'tr').andSelf().length)
6974                        $('#' + obj.rowTitle).nextUntil(nextTitle, 'tr').andSelf().remove();
6975                }
6976
6977                $(rowHtml).insertAfter('#' + obj.rowTitle);
6978
6979                //Update the total price of the order
6980                updateTotalPrice();
6981                //Localize all relevant elements
6982                Main.LocalizePage();
6983            }
6984        }
6985    }
6986
6987    /**
6988     * Update product configure total column
6989     */
6990    function loadTotalColumn() {
6991        var rowHtml = "";
6992
6993        for (var page in productDisplayParams) {
6994            //Get the given page object and create a title row
6995            var pageObj = ProductConfigureSteps.GetPageObj(page);
6996            //Create a title row for a given form
6997            createTitleRow(pageObj);
6998
6999            //Loop through all forms of the given page
7000            productDisplayParams[page].forEach(function (value) {
7001                //Concatenate all HTML for total rows for the given elements in a given form
7002                rowHtml += getTotalRows(value.form, value.elements, page);
7003            });
7004
7005            //Insert total rows after the row title
7006            $(rowHtml).insertAfter('#' + pageObj.rowTitle);
7007            rowHtml = "";
7008        }
7009
7010        //Display updated total price
7011        updateTotalPrice();
7012    }
7013
7014    /**
7015     * Update total price
7016     */
7017    function updateTotalPrice() {
7018        var currentTotal = 0;
7019        //Get all price values in the total column and unformat them into floating numbers
7020        var totalPrice = $(table).find('.product-price').map(function (key, value) {
7021            if (parseInt(numeral().unformat($(this).html())))
7022                return numeral().unformat($(this).html());
7023        });
7024        if (totalPrice.length)
7025            //Add all those numbers together
7026            var currentTotal = $.makeArray(totalPrice).reduce(function (previousValue, currentValue) {
7027                return previousValue + currentValue;
7028            });
7029
7030        //If the total is greater than 0, display it
7031        if (currentTotal > 0)
7032            $('#product-total').text(numeral(currentTotal).format('$0,0.00'));
7033        else
7034            $('#product-total').html("&mdash;");
7035    }
7036
7037    /**
7038     * Get the size input HTML of a given decoration method 
7039     * @param {number} decorationMethodId - The decoration method ID
7040     * @return {string} The HTML for the size input controls
7041     */
7042    function getSizesHtml(decorationMethodId) {
7043        var source = $('#productSizes').html();
7044        var template = Handlebars.compile(source);
7045        var html = '';
7046        var singleGridId = '';
7047        
7048        if (typeof (decorationMethodId) !== 'undefined') {
7049            var sizeArr = [];
7050
7051            for (var grid in priceGrids[decorationMethodId]) {
7052                priceGrids[decorationMethodId][grid][0].Sizes.forEac
7052h(function (value) {
7053                    //For each size, if not already present in the size array, push it
7054                    if (!sizeArr.some(function(size) {
7055                        return size.Id == value.Id 
7056                    })) {
7057                        sizeArr.push(value);
7058                    } else {
7059                        //If the current size was found in the size array
7060                        var record = sizeArr.find(function (size) {
7061                            return size.Id == value.Id
7062                        });
7063                        if (record) {
7064                            //Get the grid IDs of the current size
7065                            var currentGrids = record.GridId.toString();
7066                            //Get a grid ID array if more than one ID was found
7067                            var gridIdArr = currentGrids.split(',');
7068                            
7069                            //If the grid ID wasn't found in the grid ID array, push it
7070                            if (gridIdArr.indexOf(value.GridId.toString()) == -1)
7071                                gridIdArr.push(value.GridId);
7072                            
7073                            //Set the grid ID in record to joined elements in gridIdArr
7074                            record.GridId = gridIdArr.join(',');
7075                        }
7076                    }
7077                });
7078            }
7079
7080            //If a current colour is present, set the single grid ID to that particular colour grid ID
7081            if (typeof (productConfigureParams.CurrentColour) !== 'undefined')
7082                singleGridId = ProductGrid.GetColourGridId(decorationMethodId, productConfigureParams.CurrentColour);
7083            else
7084                singleGridId = ProductGrid.GetColourGridId(decorationMethodId);
7085
7086            var html = template({
7087                sizes: sizeArr,
7088                gridId: singleGridId
7089            });
7090        }
7091
7092        return html;
7093    }
7094
7095    /**
7096     * Get the selected decoration method ID
7097     * @return {number} The selected decoration method ID
7098     */
7099    function getSelectedDecorationMethodId() {
7100        var selectedDecorationMethod;
7101
7102        if (productParams.PriceGrids.length > 0) {
7103            //Search the available price grids and return the decoration method ID if default grid is set to true
7104            var defaultDecorationMethodId = productParams.PriceGrids.filter(function (value) {
7105                if (value.DefaultGrid)
7106                    return value;
7107            })[0].DecorationMethodId;
7108        }
7109
7110        //If a current method is specified, set it. If not, get the default decoration method ID.
7111        selectedDecorationMethod = ((productConfigureParams.hasOwnProperty('CurrentMethod') && productConfigureParams.CurrentMethod > 0) ? productConfigureParams.CurrentMethod : defaultDecorationMethodId);
7112
7113        return selectedDecorationMethod;
7114    }
7115
7116    /**
7117     * Display the given product configure page template
7118     * @param {number} page - The page number
7119     */
7120    function displayPageTemplate(page) {
7121        if (productParams.Decorations.Methods.length > 0) {
7122            //Get product config page template
7123            var source = (page != 1) ? $('#productConfigPage' + page).html() : $('#productConfigPage').html();
7124            var template = Handlebars.compile(source);
7125
7126            var htmlObj = {
7127                colors: productParams.Colors,
7128                decorations: productParams.Decorations.Methods,
7129                edit: productParams.Edit
7130            };
7131
7132            var selectedDecorationMethod = [];
7133            if (!isNaN(productConfigureParams.CurrentMethod)) {
7134                //Search passed in decoration methods and return the current method value
7135                selectedDecorationMethod = productParams.Decorations.Methods.filter(function (value) {
7136                    if (value.Id == productConfigureParams.CurrentMethod)
7137                        return value;
7138                });
7139
7140                //Set the decoration colours to a sorted array of colours
7141                selectedDecorationMethod[0].decorationColours = sortColorsByHue(getFormattedColors(filteredDecorationColours));
7142                //Set method to the returned decoration method
7143                htmlObj['method'] = selectedDecorationMethod
7144            }
7145
7146            var html = template(htmlObj);
7147
7148            $('#product-config-container').empty().append(html).hide().fadeIn(600);
7149            //Localize all relevant elements
7150            Main.LocalizePage();
7151            //Remove all down states for total columns
7152            resetLineItemDownstates();
7153        }
7154    }
7155
7156    /**
7157     * Populate the input values after the template has been output
7158     * @param {number} page - The page number
7159     */
7160    function updatePageValues(page) {
7161        switch (page) {
7162            //Page 1
7163            case 1:
7164                //If current quantity exists, populate all quantity inputs
7165                if (productConfigureParams.hasOwnProperty('CurrentQuantity') && productConfigureParams.CurrentQuantity.length) {
7166                    populateQuantityInputs();
7167                }
7168                //If current colour exists, trigger a click event on that given colour
7169                if (productConfigureParams.hasOwnProperty('CurrentColour') && typeof(productConfigureParams.CurrentColour) !== 'undefined') {
7170                    $('#product-colours ul li a[data-id=' + productConfigureParams.CurrentColour + ']').trigger('click');
7171                }
7172                //If current method exists, set the method radio button to the current method
7173                if (productConfigureParams.hasOwnProperty('CurrentMethod') && typeof (productConfigureParams.CurrentMethod) !== 'undefined') {
7174                    setMethod($('#product-config-method-form input[value=' + productConfigureParams.CurrentMethod + ']'));
7175                }
7176                break;
7177            //Page 2
7178            case 2:
7179                //If location exists, set the location radio button to the current location
7180                if (productConfigureParams.hasOwnProperty('CurrentLocation') && typeof (productConfigureParams.CurrentLocation) !== 'undefined') {
7181                    setLocation($('#product-config-location-form input[value=' + productConfigureParams.CurrentLocation + ']'));
7182                }
7183                //If decoration colours exist, loop through them and set colours in the colour picker
7184                if (productConfigureParams.hasOwnProperty('CurrentDecorationColours') && productConfigureParams.CurrentDecorationColours.length) {
7185                    var currentDecorationColours = productConfigureParams.CurrentDecorationColours;
7186
7187                    for (var i = 0; i < currentDecorationColours.length; i++) {
7188                        $('#product-config-decoration-colour-form input#color-picker-' + (i + 1)).colorpicker('setColor', currentDecorationColours[i].Pms);
7189                    }
7190                }
7191                break;
7192            //Page 3
7193            case 3:
7194                //If artwork option exists, set the artwork radio button to the current artwork option
7195                if (productConfigureParams.hasOwnProperty('CurrentArtwork') && typeof (productConfigureParams.CurrentArtwork) !== 'undefined') {
7196                    setArtwork($('#product-config-artwork-form input[value=' + productConfigureParams.CurrentArtwork + ']'));
7197                } else {
7198                    setArtwork($('#product-config-artwork-form input:checked'));
7199                }
7200                if (productConfigureParams.hasOwnProperty('CurrentArtworkFile') && typeof (productConfigureParams.CurrentArtworkFile) !== 'undefined') {
7201                    setArtworkFile(productConfigureParams.CurrentArtworkFile);
7202
7203                    //Initialize existing file object
7204                    var file = {
7205                        name: productConfigureParams.CurrentArtworkFile.Filename,
7206                        size: productConfigureParams.CurrentArtworkFile.Size,
7207                        path: productParams.ArtworkPath
7208                    };
7209
7210                    if ($("#upload-artwork").length) {
7211                        //Create a dropzone for upload artwork element
7212                        var thisDropzone = Dropzone.forElement("#upload-artwork");
7213
7214                        //Call the default addedfile event handler
7215                        thisDropzone.emit("addedfile", file);
7216                        //And optionally show the thumbnail of the file:
7217                        thisDropzone.emit("thumbnail", file, file.path + file.name);
7218                        thisDropzone.createThumbnailFromUrl(file, file.path + file.name, function () { }, true);
7219                        //Call the complete event
7220                        thisDropzone.emit("complete", file);
7221                    }
7222                }
7223                //If the comment exists, set it
7224                if (productConfigureParams.hasOwnProperty('CurrentComments') && productConfigureParams.CurrentComments.length > 0) {
7225                    setComments(productConfigureParams.CurrentComments);
7226                }
7227                break;
7228        }
7229    }
7230
7231    /**
7232     * When editing a product in your cart, ensure that product configure parameters are initialized with values from the cart item
7233     * @param {cartItems} cartItems - The items in your cart
7234     */
7235    function initCartItems(cartItems) {
7236        var currentItem = cartItems[0];
7237
7238        if (currentItem.hasOwnProperty('Id'))
7239            productConfigureParams.CurrentItemId = currentItem.Id;
7240        if (currentItem.hasOwnProperty('Quantities')) {
7241            productConfigureParams.CurrentQuantity = [];
7242            //Loop through all cart item quantities and push quantity objects
7243            for (var i = 0; i < currentItem.Quantities.length; i++) {
7244                productConfigureParams.CurrentQuantity.push(
7245                    {
7246                        Id: currentItem.Quantities[i].SizeId,
7247                        Quantity: currentItem.Quantities[i].Quantity,
7248                        GridId: currentItem.Quantities[i].GridId
7249                    }
7250                );
7251            }
7252        }
7253        if(currentItem.hasOwnProperty('ColourId'))
7254            productConfigureParams.CurrentColour = currentItem.ColourId;
7255        if (currentItem.hasOwnProperty('DecorationMethodId'))
7256            productConfigureParams.CurrentMethod = currentItem.DecorationMethodId;
7257        if (currentItem.hasOwnProperty('DecorationLocation'))
7258            productConfigureParams.CurrentLocation = currentItem.DecorationLocation.Id;
7259        if (currentItem.hasOwnProperty('DecorationColours')) {
7260            if (!productConfigureParams.hasOwnProperty('CurrentDecorationColours'))
7261                productConfigureParams.CurrentDecorationColours = [];
7262                
7263                //Loop through all decoration colours and push decoration colour objects
7264                currentItem.DecorationColours.forEach(function (value) {
7265                    productConfigureParams.CurrentDecorationColours.push(
7266                        {
7267                            Pms: value.Color.Pms,
7268                            Color: 'PMS ' + value.Color.Label
7269                        }
7270                    );
7271                });
7272        }
7273        if (currentItem.hasOwnProperty('Comment'))
7274            productConfigureParams.CurrentComments = currentItem.Comment;
7275        //If the current item has an artwork file, set it and ensure that current artwork option is set to 1 (upload art) or 2 (contact me)
7276        if (currentItem.hasOwnProperty('ArtworkFile') && currentItem.ArtworkFile.Filename) {
7277            productConfigureParams.CurrentArtworkFile = currentItem.ArtworkFile;
7278            productConfigureParams.CurrentArtwork = 1;
7279        } else {
7280            productConfigureParams.CurrentArtwork = 2;
7281        }
7282    }
7283
7284    /**
7285     * Display total rows based on passed in values from cart item. Each display configuration object will follow a consistent pattern so that total rows can properly handle the values. 
7286     * @param {cartItems} cartItems - The items in your cart
7287     */
7288    function displayCartItems(cartItems) {
7289        var currentItem = cartItems[0];
7290        //Get the number of pages available
7291        var pageCount = ProductConfigureSteps.GetPageCount();
7292
7293        for (var i = 0; i < pageCount; i++)
7294            productDisplayParams[i + 1] = [];
7295
7296        if (currentItem.hasOwnProperty('Quantities')) {
7297            var elements = [];
7298            for (var i = 0; i < currentItem.Quantities.length; i++) {
7299                //Push a quantity object with name and value into elements
7300                elements.push({
7301                    "name": currentItem.Quantities[i].Size.Title.length ? currentItem.Quantities[i].Size.Title : "Quantity",
7302                    "value": currentItem.Quantities[i].Quantity
7303                });
7304            }
7305
7306            //For quantities, push an object with populated form class and form elements to display title and total rows
7307            productDisplayParams[1].push(
7308            {
7309                'form': 'product-config-quantity-form',
7310                'elements': elements
7311            });
7312        }
7313
7314        if (currentItem.hasOwnProperty('DecorationMethodId')) {
7315            //For decoration method, push an object with populated form class and form elements to display title and total rows
7316            productDisplayParams[1].push({
7317                'form': 'product-config-method-form',
7318                'elements': [
7319                    {
7320                        "name": "Decoration Method",
7321                        "value": currentItem.DecorationMethod.Title
7322                    }
7323                ]
7324            });
7325        }
7326
7327        if (currentItem.hasOwnProperty('DecorationLocation')) {
7328            //For decoration location, push an object with populated form class and form elements to display title and total rows
7329            productDisplayParams[2].push({
7330                'form': 'product-config-location-form',
7331                'elements': [
7332                    {
7333                        'name': "Location",
7334                        'value': currentItem.DecorationLocation.Title
7335                    }
7336                ]
7337            });
7338        }
7339
7340        if (currentItem.hasOwnProperty('DecorationColours')) {
7341            var elements = [];
7342            //For each decoration colour, push an object with name and value
7343            for (var i = 0; i < currentItem.DecorationColours.length; i++) {
7344                elements.push({
7345                    "name": "decorationColours[]",
7346                    "value": currentItem.DecorationColours[i].Colour.Title
7347                });
7348            }
7349
7350            //For decoration colours, push an object with populated form class and form elements to display title and total rows
7351            productDisplayParams[2].push({
7352                'form': 'product-config-decoration-colour-form',
7353                'elements': elements
7354            });
7355        }
7356
7357        if (currentItem.hasOwnProperty('ArtworkFile')) {
7358            //For artwork file, push an object with populated form class and form elements to display title and total rows
7359            productDisplayParams[3].push({
7360                'form': 'product-config-artwork-form',
7361                'elements': [
7362                    {
7363                        'name': "Product Artwork",
7364                        'value': (currentItem.ArtworkFile.Filename.length > 0) ? "Upload Art" : "Contact Me"
7365                    }
7366                ]
7367            });
7368        }
7369    }
7370
7371    /**
7372     * Populate product quantity inputs with existing values
7373     */
7374    function populateQuantityInputs() {
7375        if (productConfigureParams.hasOwnProperty('CurrentQuantity') && typeof(productConfigureParams.CurrentQuantity) !== 'undefined') {
7376            if ($('#product-config-quantity-form input').length > 1) {
7377                if (productConfigureParams.CurrentQuantity.length) {
7378                    //Loop through all quantities and set the value of each populated quantity
7379                    for (var quantity in productConfigureParams.CurrentQuantity) {
7380                        $('#product-config-quantity-form input[id="' + productConfigureParams.CurrentQuantity[quantity].Id + '"]').val(productConfigureParams.CurrentQuantity[quantity].Quantity);
7381                    }
7382                }
7383            } else if ($('#product-config-quantity-form input').length == 1) {
7384                if (productConfigureParams.CurrentQuantity.length) {
7385                    //Simply set the single input to the populated quantity
7386                    $('#product-config-quantity-form input').val(productConfigureParams.CurrentQuantity[0].Quantity);
7387                    //Used to revert value to a legal number (higher than the minimum quantity)
7388                    $('#product-config-quantity-form input').data('current-value', productConfigureParams.CurrentQuantity[0].Quantity);
7389                } else {
7390                    //If there are no populated quantities, get the minimum quantity from the price grid and set it 
7391                    if (typeof (Main.GetQuantityRanges(priceGrids)[getSelectedDecorationMethodId()] !== 'undefined')) {
7392                        //Get the quantity range for the selected decoration method id
7393                        var quantityRanges = Main.GetQuantityRanges(priceGrids)[getSelectedDecorationMethodId()];
7394
7395                        if(quantityRanges.length) {
7396                            $('#product-config-quantity-form input').val(quantityRanges[0].QuantityFrom);
7397                            //Used to revert value to a legal number (higher than the minimum quantity)
7398                            $('#product-config-quantity-form input').data('current-value', quantityRanges[0].QuantityFrom);
7399                        }
7400                    }
7401                }
7402            }
7403        }
7404    }
7405
7406    /**
7407     * Validate all the forms passed in based on custom predefined validation rules on a given page
7408     * @param {array} formArr - The array of form elements
7409     * @param {number} page - The page number
7410     */
7411    function validateForms(formArr, page) {
7412        //If validForms doesn't have a key for current page, create one
7413        if (typeof (validForms[currentPage]) === 'undefined')
7414            validForms[currentPage] = {};
7415
7416        //Set local forms variable to passed in form array, or keys of validate rules object
7417        var forms = (typeof (page) === 'undefined') ? formArr : Object.keys(vali
7417dateRules[page]);
7418
7419        for (var i = 0; i < forms.length; i++) {
7420            //If a validation rules exist for this form
7421            if (typeof (validateRules[currentPage][forms[i]]) !== 'undefined') {
7422                //If there is no valid form key in valid forms, create one and initialize it to an empty array
7423                if (typeof (validForms[currentPage][forms[i]]) === 'undefined')
7424                    validForms[currentPage][forms[i]] = [];
7425
7426                if ($('#' + forms[i]).length) {
7427                    //Get a reference to the form to get at the validation rules
7428                    var form = validateRules[currentPage][forms[i]];
7429                    for (var rule in form) {
7430                        //If the given validation function returns false
7431                        if (!form[rule].validateFunc()) {
7432                            //Display the custom error for that rule
7433                            form[rule].displayValidateError();
7434                            //Set the valid status to false
7435                            validForms[currentPage][forms[i]] = false;
7436                            break;
7437                        } else {
7438                            //Set the valid status to true
7439                            validForms[currentPage][forms[i]] = true;
7440                            //Run the success function
7441                            form[rule].displayValidateSuccess();
7442                        }
7443                    }
7444                }
7445            }
7446        }
7447    }
7448
7449    /**
7450     * Get the status for a given form
7451     * @param {string} form - The name of a given form
7452     * @return {bool} The form status
7453     */
7454    function getFormStatus(form) {
7455        if (form.length > 0) {
7456            if (typeof (validForms[currentPage]) === 'undefined')
7457                //Set a new key for the current page if it doesn't exist
7458                validForms[currentPage] = {};
7459            if (typeof (validForms[currentPage][form]) !== 'undefined') {
7460                //Return the status for passed in form name
7461                return validForms[currentPage][form];
7462            }
7463        }
7464    }
7465
7466    /**
7467     * Get the name of the failing form in a given page
7468     * @param {number} page - The page number
7469     * @return {string} The form name
7470     */
7471    function getFailedForm(page) {
7472        if (typeof (validForms[page]) === 'undefined')
7473            //Set a new key for the passed in page if it doesn't exist
7474            validForms[page] = {};
7475
7476        if (Object.keys(validForms[page]).length) {
7477            //Loop through all forms in valid forms
7478            for (var form in validForms[page]) {
7479                //If not valid, return form name
7480                if (!validForms[page][form]) {
7481                    return form;
7482                }
7483            }
7484        }
7485
7486        return '';
7487    }
7488
7489    /**
7490     * Add validation rules for all forms on a given page
7491     * @param {object} obj - The object containing all pages and forms
7492     */
7493    function addValidationRules(obj) {
7494        if (typeof (validateRules[obj.page]) === 'undefined')
7495            //Create a key for given page
7496            validateRules[obj.page] = {};
7497
7498        if (obj.hasOwnProperty('forms')) {
7499            if (typeof (validateRules[obj.page]) !== 'undefined') {
7500
7501                obj.forms.forEach(function (value) {
7502                    if (typeof (validateRules[obj.page][value.name]) === 'undefined')
7503                        //Create a key for given form
7504                        validateRules[obj.page][value.name] = {};
7505
7506                    //Loop through all validation rules
7507                    for (var rule in value.validateRulesList) {
7508                        if (typeof (validateRules[obj.page][value.name][rule]) === 'undefined')
7509                            //Create a key for given rule
7510                            validateRules[obj.page][value.name][rule] = {};
7511
7512                        //For each rule, populate a rule object with the validation methods
7513                        validateRules[obj.page][value.name][rule] = {
7514                            validateFunc: value.validateRulesList[rule],
7515                            displayValidateError: value.displayValidateErrorList[rule],
7516                            displayValidateSuccess: value.displayValidateSuccessList[rule]
7517                        };
7518                    }
7519                });
7520            }
7521        }
7522    }
7523
7524    /**
7525     * Remove current artwork file key from product configure parameters
7526     */
7527    function removeArtworkFile() {
7528        if(productConfigureParams.hasOwnProperty('CurrentArtworkFile'))
7529            delete productConfigureParams['CurrentArtworkFile'];
7530    }
7531
7532    /**
7533     * Update breadcrumb to previously saved product search parameters
7534     */
7535    function updateBreadcrumb() {
7536        //If cookies are enabled
7537        if (navigator.cookieEnabled) {
7538            //If storage API supported
7539            if (typeof (Storage) !== "undefined") {
7540                //If session storage key exists
7541                if (sessionStorage.hasOwnProperty('breadcrumbSearchParams')) {
7542                    //Parse JSON for breadcrumb search params
7543                    var breadcrumbString = JSON.parse(sessionStorage.breadcrumbSearchParams);
7544                    //If breadcrumbString has current product id as key
7545                    if (breadcrumbString.hasOwnProperty(productConfigureParams.CurrentProductId)) {
7546                        //If product key is defined
7547                        if (breadcrumbString[productConfigureParams.CurrentProductId])
7548                            //Set breadcrumb link to saved search parameters
7549                            $('.breadcrumb li a[data-type="search"]').attr('href', '/search/' + breadcrumbString[productConfigureParams.CurrentProductId]);
7550                    }
7551                }
7552            }
7553        }
7554    }
7555
7556    /**
7557     * Reset all states for total rows and remove down class
7558     */
7559    function resetLineItemDownstates() {
7560        $('#product-config-total table td').each(function () {
7561            if ($(this).hasClass('down'))
7562                $(this).removeClass('down');
7563        });
7564    }
7565
7566    /**
7567     * Get total rows HTML based on passed in form, elements, and page
7568     * @param {string} form - The name of the form
7569     * @param {array} elements - The key/value pairs of that form
7570     * @param {number} page - The page number
7571     * @return {string} The total rows HTML
7572     */
7573    function getTotalRows(form, elements, page) {
7574        var rowHtml = "";
7575
7576        //Get the page object for passed in page
7577        var pageObj = ProductConfigureSteps.GetPageObj(page);
7578        //Get a reference to the form configuration of the form name that was passed in
7579        var selectedForm = pageObj.forms.filter(function (value) {
7580            if (value.name == form)
7581                return value;
7582        })[0];
7583
7584        if (selectedForm !== 'undefined' && elements.length) {
7585            for (var k = 0; k < elements.length; k++) {
7586                if (typeof (selectedForm.writeLineTotal) !== 'undefined') {
7587                    //Build the row HTML based on the custom function passed in
7588                    rowHtml += selectedForm.writeLineTotal(elements[k].name, elements[k].value, pageObj.page, selectedColourLabel, (k + 1));
7589                }
7590            }
7591        }
7592
7593        return rowHtml;
7594    }
7595
7596    /**
7597     * Get total rows HTML based on passed in form, elements, and page
7598     * @param {object} obj - The object containing row titles and pages
7599     */
7600    function createTitleRow(obj) {
7601        //Create HTML for a new title row in product config total
7602        var titleRow = $('<tr id="' + obj.rowTitle + '" data-page="' + obj.page + '"><th>' + obj.title + '</th><th>&nbsp;</th></tr>');
7603        //Get a reference to the next title row, if any
7604        var nextTitle = $('#' + obj.rowTitle).nextAll('tr[id]').first();
7605        //If we have a title row that comes after the current title row, remove all data (not title) rows between them. This will allow the key/value forms to constantly "refresh", but keep the title row as reference
7606        if ($('#' + obj.rowTitle).nextUntil(nextTitle, 'tr').length)
7607            $('#' + obj.rowTitle).nextUntil(nextTitle, 'tr').remove();
7608
7609        //If the current title row doesn't exist...
7610        if (!$('#' + obj.rowTitle).length) {
7611            //...and there is a title row that should come after it on the next page (eg. you are editing a page by going back a step)
7612            if (($("tr[data-page=" + (obj.page + 1) + "]").length))
7613                //Insert the title row before that row
7614                $(titleRow).insertBefore($("tr[data-page=" + (obj.page + 1) + "]"));
7615            else
7616                //If not, insert it before the total row (after that row)
7617                $(titleRow).insertBefore($("tr#total-row"));
7618        }
7619    }
7620
7621    /**
7622     * Get a list of all valid forms
7623     * @param {object} obj - The object containing all pages and forms
7624     * @return {array} An array of all valid form names
7625     */
7626    function getValidForms(obj) {
7627        //Get all form names where the inner key/value pairs of those forms are not empty
7628        var validForms = obj.forms.map(function (value) {
7629            var elements = ($('#' + value.name).serializeArray().filter(function (input) {
7630                if (input.value != '')
7631                    return input;
7632            }));
7633            if (elements.length)
7634                return value.name;
7635            else
7636                return '';
7637        }).filter(function (value) {
7638            if (value != '')
7639                return value;
7640        });
7641
7642        return validForms;
7643    }
7644
7645    /**
7646     * Toggle visibility of elements based value of media queries
7647     * @param {object} match - A media query list that matches a pre-determined breakpoint
7648     */
7649    function smallWidthChange(match) {
7650        //Match all viewports below 992px in width
7651        if (match.matches) {
7652            $('#rep-window').hide();
7653        } else {
7654            $('#rep-window').show();
7655        }
7656
7657        //Update the grid as required
7658        ProductGrid.UpdateGrid($('#product-config-pricing-table table:visible'), getSelectedDecorationMethodId(), currentQuantityCount);
7659    }
7660
7661    return {
7662        Init: function (params) {
7663            init(params);
7664        },
7665        ChangePage: function (page) {
7666            changePage(page);
7667        },
7668        UpdateTotalColumn: function (obj) {
7669            updateTotalColumn(obj);
7670        },
7671        SetColour: function (colour) {
7672            setColour(colour);
7673        },
7674        GetColour: function() {
7675            return productConfigureParams.CurrentColour;
7676        },
7677        GetProductColours: function() {
7678            return coloursArr;
7679        },
7680        SetQuantity: function (quantitiesObj) {
7681            setQuantity(quantitiesObj);
7682        },
7683        GetQuantity: function () {
7684            return currentQuantity;
7685        },
7686        GetQuantityCount: function () {
7687            return currentQuantityCount;
7688        },
7689        SetMethod: function (methodObj) {
7690            setMethod(methodObj);
7691        },
7692        SetLocation: function (locationObj) {
7693            setLocation(locationObj);
7694        },
7695        SetArtwork: function (artworkObj) {
7696            setArtwork(artworkObj);
7697        },
7698        SetComments: function (comments) {
7699            setComments(comments);
7700        },
7701        UpdatePageValues: function (page) {
7702            updatePageValues(page);
7703        },
7704        GetPriceGrids: function() {
7705            return priceGrids;
7706        },
7707        GetProductConfigureParams: function () {
7708            return productConfigureParams;
7709        },
7710        GetSizesHtml: function (decorationMethodId) {
7711            return getSizesHtml(decorationMethodId);
7712        },
7713        GetSelectedDecorationMethodId: function() {
7714            return getSelectedDecorationMethodId();
7715        },
7716        GetCurrentPage: function() {
7717            return currentPage;
7718        },
7719        PopulateQuantityInputs: function () {
7720            populateQuantityInputs();
7721        },
7722        SetArtworkFile: function (fileObj) {
7723            setArtworkFile(fileObj);
7724        },
7725        GetArtworkFile: function () {
7726            return productConfigureParams.CurrentArtworkFile;
7727        },
7728        RemoveArtworkFile: function () {
7729            removeArtworkFile();
7730        },
7731        ValidateForms: function (formArr, page) {
7732            validateForms(formArr, page);
7733        },
7734        AddValidationRules: function(forms) {
7735            addValidationRules(forms);
7736        },
7737        GetProductId: function() {
7738            return productConfigureParams.CurrentProductId;
7739        },
7740        GetFormStatus: function(form) {
7741            return getFormStatus(form);
7742        },
7743        GetFailedForm: function (page) {
7744            return getFailedForm(page);
7745        },
7746        UpdateDisabledPages: function () {
7747            updateDisabledPages();
7748        },
7749        ResetLineItemDownstates: function () {
7750            resetLineItemDownstates();
7751        },
7752        SelectProductSwatch: function (listItem) {
7753            selectProductSwatch(listItem);
7754        },
7755        LoadTotalColumn: function() {
7756            loadTotalColumn();
7757        },
7758        UpdateDecorationColour: function () {
7759            updateDecorationColour();
7760        }
7761    }
7762
7763}());;
7764/** Class representing the Product Configure steps. This class holds all of the unique configuration data for all 3 product config pages */
7765var ProductConfigureSteps = (function () {
7766    //Used to hold all configuration objects for product configure pages
7767    var productConfigPages = {};
7768    //Used to hold dropzone instance
7769    var myDropzone = {};
7770
7771    /**
7772     * Initialize the product configure steps with the appropriate page data
7773     */
7774    function init() {
7775        //Set options for page 1
7776        productConfigPages[1] = {
7777            //Page numbre
7778            page: 1,
7779            //Row title ID
7780            rowTitle: 'product-quantity-title',
7781            //List of forms to group under title
7782            forms: [
7783                {
7784                    //Set the form name
7785                    name: 'product-config-quantity-form',
7786                    //Custom function that handles how an HTML string is constructed for key/value pairs in this particular form
7787                    writeLineTotal: function (name, value, page, colour) {
7788                        //Get the quantity ranges from the product grid
7789                        var quantityRanges = ProductGrid.GetQuantityRange(ProductConfigure.GetSelectedDecorationMethodId());
7790                        //Get the product configure quantity count
7791                        var updatedQuantity = ProductConfigure.GetQuantityCount();
7792
7793                        //If an integer is successfully parsed and quantity is greater than minimum allowed quantity
7794                        if ((parseInt(value)) && (updatedQuantity >= quantityRanges[0].QuantityFrom)) {
7795                            //Get grid array from form
7796                            var gridArray = $('form input[name="' + name + '"]').length ? $('form input[name="' + name + '"]').data('grid-id').toString().split(',') : [];
7797
7798                            //Get the current price of the selected quantity range
7799                            var price = ProductGrid.GetPrice(ProductConfigure.GetSelectedDecorationMethodId(), gridArray, $('#product-colours ul li a.active'));
7800
7801                            //If there are any discount prices available, cross out the regular prices
7802                            if (Main.GetDiscount() > 0)
7803                                var priceUnformatted = numeral().unformat(price) * (1 - Main.GetDiscount());
7804                            else
7805                                var priceUnformatted = numeral().unformat(price);
7806
7807                            //Calculate price based on price and value passed in
7808                            var calculatedPrice = priceUnformatted * value;
7809                            //Format price into dollar value
7810                            var parsedPrice = numeral(calculatedPrice).format('$0,0.00');
7811                            //If a colour is defined, create a span with the colour label
7812                            var colour = (colour != null) ? " <span class='item-colour'>(" + colour + ")</span>" : "<span class='item-colour'></span>";
7813
7814                            //Return row HTML for display in product config total
7815                            return "<tr class='line-item' data-type='quantity' data-name='" + name + "' data-next-page=" + page + "><td>" + value + " " + name + colour + "</td><td style='text-align:right;'><span class='product-price'>" + parsedPrice + "</span></td></tr>";
7816                        } else {
7817                            return "";
7818                        }
7819                    },
7820                    //Object that holds all the validation rules for this form
7821                    validateRulesList: {
7822                        //Sizes rule - if there are no sizes inputted, validation check will fail
7823                        'sizes': function () {
7824                            var sizes = $('#product-config-quantity-form input').filter(function () {
7825                                return $(this).val() != "";
7826                            });
7827
7828                            if (!sizes.length)
7829                                return false;
7830                            else
7831                                return true;
7832                        },
7833                        //Invalid chars rule - if the inputted entries are not integers, validation check will fail
7834                        'invalidchars': function () {
7835                            var invalidEntries = $('#product-config-quantity-form input').filter(function () {
7836                                if (!Number.isInteger(parseInt($(this).val())))
7837                                    return $(this).val();
7838                            });
7839
7840                            if (invalidEntries.length)
7841                                return false;
7842                            else
7843                                return true;
7844                        },
7845                        //Minimum quantity rule - if the total quantity falls below the minimum, validation check will fail 
7846                        'minimumquantity': function () {
7847                            var quantityRanges = ProductGrid.GetQuantityRange(ProductConfigure.GetSelectedDecorationMethodId());
7848                            if ((ProductConfigure.GetQuantityCount() < quantityRanges[0].QuantityFrom))
7849                                return false;
7850                            else
7851                                return true;
7852                        }
7853                    },
7854                    displayValidateErrorList: {
7855                        //For sizes rule fail, output the following
7856                        'sizes': function () {
7857                            $('#product-selected-quantity').html('<span class="validation-error"><i class="fa fa-exclamation-circle"></i> Enter a quantity below your required sizes.</span>');
7858                            $('#product-config-quantity-form input').css('border', '1px solid #a94442');
7859                        },
7860                        //For invalidchars rule fail, output the following
7861                        'invalidchars': function () {
7862                            $('#product-selected-quantity').html('<span class="validation-error"><i class="fa fa-exclamation-circle"></i> Please ensure that only numbers are entered.</span>');
7863                            $('#product-config-quantity-form input').css('border', '1px solid #a94442');
7864                        },
7865                        //For minimumquantity rule fail, output the following
7866                        'minimumquantity': function () {
7867                            var quantityRanges = ProductGrid.GetQuantityRange(ProductConfigure.GetSelectedDecorationMethodId());
7868                            //If this is a positive number, that means that we haven't reached the minimum yet
7869                            var difference = quantityRanges[0].QuantityFrom - ProductConfigure.GetQuantityCount();
7870
7871                            if (difference > 0)
7872                                $('#product-selected-quantity').html("<span class='validation-error'><i class='fa fa-exclamation-circle'></i> " + ProductConfigure.GetQuantityCount() + " item(s), add " + difference + " more to meet minimum.</span>");
7873
7874                            $('#product-config-quantity-form input').css('border', '1px solid #a94442');
7875                        }
7876                    },
7877                    displayValidateSuccessList: {
7878                        //For sizes rule success, output the following
7879                        'sizes': function () {
7880                            $('#product-config-quantity-form input').css('border', '1px solid #e7e7e7');
7881                        },
7882                        //For invalidchars rule success, output the following
7883                        'invalidchars': function () {
7884                            $('#product-config-quantity-form input').css('border', '1px solid #e7e7e7');
7885                        },
7886                        //For minimumquantity rule success, output the following
7887                        'minimumquantity': function () {
7888                            $('#product-config-quantity-form input').css('border', '1px solid #e7e7e7');
7889                        }
7890                    }
7891                },
7892                {
7893                    name: 'product-config-colour-form',
7894                    //Object that holds all the validation rules for this form
7895                    validateRulesList: {
7896                        //Required rule - if there are no colours checked, validation check will fail
7897                        'required': function () {
7898                            var checkedColours = $('#product-colours ul li a.active');
7899
7900                            if (!checkedColours.length)
7901                                return false;
7902                            else
7903                                return true;
7904                        }
7905                    },
7906                    displayValidateErrorList: {
7907                        //For required rule fail, output the following
7908                        'required': function () {
7909                            $('#product-selected-colour').html('<span class="validation-error"><i class="fa fa-exclamation-circle"></i>
7909 Please select a ' + Localize.GetLocaleString('color') + '.</span>');
7910                        }
7911                    },
7912                    displayValidateSuccessList: {
7913                        'required': function () { }
7914                    },
7915                    writeLineTotal: function () {
7916                        return "";
7917                    }
7918                },
7919                {
7920                    //Set the form name
7921                    name: 'product-config-method-form',
7922                    //Custom function that handles how an HTML string is constructed for key/value pairs in this particular form
7923                    writeLineTotal: function (name, value, page, colour) {
7924                        //Get name value to display in total grid column
7925                        var totalName = $('form input[name="' + name + '"]:checked').length ? $('form input[name="' + name + '"]:checked').data('name') : name;
7926                        //Get the setup charge of passed in decoration method ID
7927                        var price = ProductGrid.GetSetupCharge(ProductConfigure.GetSelectedDecorationMethodId());
7928                        //Get custom label in checkbox data attribute
7929                        var label = $('form input[name="' + name + '"]:checked').length ? $('form input[name="' + name + '"]:checked').data('label') : value;
7930                        //Format price into dollar value
7931                        var parsedPrice = numeral(price).format('$0,0.00');
7932                        //Return row HTML for display in product config total
7933                        return "<tr class='line-item' data-type='method' data-name='DecorationMethod' data-next-page=" + page + "><td>" + totalName + " - " + label + "</td><td style='text-align:right;'><span class='product-price'>" + parsedPrice + "</span></td></tr>";
7934                    },
7935                    //Object that holds all the validation rules for this form
7936                    validateRulesList: {
7937                        //Required rule - if there are no methods checked, validation check will fail
7938                        'required': function () {
7939                            var checkedMethods = $('#product-config-method-form input[name="DecorationMethod"]:checked');
7940
7941                            if (!checkedMethods.length)
7942                                return false;
7943                            else
7944                                return true;
7945                        }
7946                    },
7947                    displayValidateErrorList: {
7948                        //For required rule fail, output the following
7949                        'required': function () {
7950                            $('#product-selected-method').html('<span class="validation-error"><i class="fa fa-exclamation-circle"></i> Please select a decoration method.</span>');
7951                        }
7952                    },
7953                    displayValidateSuccessList: {
7954                        'required': function () {
7955                        }
7956                    }
7957                }
7958            ],
7959            //Row title label
7960            title: 'Product Quantity'
7961        };
7962        //Set options for page 2
7963        productConfigPages[2] = {
7964            //Page number
7965            page: 2,
7966            //Row title ID
7967            rowTitle: 'product-decoration-title',
7968            //List of forms to group under title
7969            forms: [
7970                {
7971                    //Set the form name
7972                    name: 'product-config-location-form',
7973                    //Custom function that handles how an HTML string is constructed for key/value pairs in this particular form
7974                    writeLineTotal: function (name, value, page) {
7975                        var label = $('form input[name="' + name + '"]').length ? $('form input[name="' + name + '"]:checked').data('label') : value;
7976                        return "<tr class='line-item' data-type='location' data-name='" + name + "' data-next-page=" + page + "><td colspan='2'>" + name + " - " + label + "</td></tr>";
7977                    },
7978                    //Object that holds all the validation rules for this form
7979                    validateRulesList: {
7980                        //Required rule - if there are no locations checked, validation check will fail
7981                        'required': function () {
7982                            var checkedMethods = $('#product-config-location-form input[name="location"]:checked');
7983
7984                            if (!checkedMethods.length)
7985                                return false;
7986                            else
7987                                return true;
7988                        }
7989                    },
7990                    displayValidateErrorList: {
7991                        //For required rule fail, output the following
7992                        'required': function () {
7993                            $('#product-selected-location').html('<span class="validation-error"><i class="fa fa-exclamation-circle"></i> Please select a location.</span>');
7994                        }
7995                    },
7996                    displayValidateSuccessList: {
7997                        'required': function () {
7998                        }
7999                    }
8000                },
8001                {
8002                    //Set the form name
8003                    name: 'product-config-decoration-colour-form',
8004                    //Custom function that handles how an HTML string is constructed for key/value pairs in this particular form
8005                    writeLineTotal: function (name, value, page, colour, count) {
8006                        var parsedPrice = "";
8007                        var parsedSetupCharge = "";
8008
8009                        //All other colours should automatically calculate cost
8010                        if (count != 1) {
8011                            //Get decoration colour cost based on passed in decoration method ID and count
8012                            var price = ProductGrid.GetDecorationColourCost(ProductConfigure.GetSelectedDecorationMethodId(), count);
8013                            //Get decoration colour setup charge based on passed in decoration method ID and count
8014                            var setupCharge = ProductGrid.GetDecorationColourSetupCharge(ProductConfigure.GetSelectedDecorationMethodId(), count);
8015                            //Multiply price by quantity count
8016                            var calculatedPrice = price * ProductConfigure.GetQuantityCount();
8017                            //Format the price into currency 
8018                            parsedPrice = numeral(calculatedPrice).format('$0,0.00');
8019
8020                            var calculatedPrice = setupCharge;
8021                            //Format the setup charge into currency
8022                            parsedSetupCharge = numeral(calculatedPrice).format('$0,0.00');
8023                        } else {
8024                            //The first colour is free
8025                            parsedPrice = "INCLUDED";
8026                            parsedSetupCharge = "INCLUDED";
8027                        }
8028
8029                        //Get unique colour label from data attribute of colour radio button
8030                        var label = $('form input[name="' + name + '"][value="' + value + '"]:checked').length ? $('form input[name="' + name + '"][value="' + value + '"]:checked').data('label') : value;
8031                        //If label is black or white, don't prepend PMS
8032                        var updatedLabel = ((label == 'Black') || (label == 'White')) ? label : 'PMS ' + label;
8033
8034                        //Return row HTML for display in product config total
8035                        return "<tr class='line-item' data-type='decoration-colour' data-name='" + name + "' data-next-page=" + page + "><td>" + getOrdinal(count) + " <span class='localized'>Color</span> - <strong> " + updatedLabel + "</strong></td><td style='text-align:right;'><span class='product-price'>" + parsedPrice + "</span></td></tr>" +
8036                                "<tr class='line-item' data-type='decoration-colour' data-name='" + name + "' data-next-page=" + page + "><td>Setup Charge</td><td style='text-align:right;'><span class='product-price'>" + parsedSetupCharge + "</span></td></tr>";
8037                    },
8038                    //Object that holds all the validation rules for this form
8039                    validateRulesList: {
8040                        'required': function () {
8041                            //Required rule - if there are no product decoration colours checked, validation check will fail
8042                            var checkedColours = $('#product-config-input input.colorpicker-popup').map(function (value) {
8043                                if ($(this).val()) {
8044                                    return $(this).val();
8045                                }
8046                            });
8047
8048                            if (!checkedColours.length)
8049                                return false;
8050                            else
8051                                return true;
8052                        }
8053                    },
8054                    displayValidateErrorList: {
8055                        //For required rule fail, output the following
8056                        'required': function () {
8057                            $('#product-decoration-selected-colour').html('<span class="validation-error"><i class="fa fa-exclamation-circle"></i>
8057 Please select a ' + Localize.GetLocaleString('color') + '.</span>');
8058                        }
8059                    },
8060                    displayValidateSuccessList: {
8061                        'required': function () { }
8062                    }
8063                }
8064            ],
8065            //Row title label
8066            title: 'Decorating Options'
8067        };
8068        //Set options for page 3
8069        productConfigPages[3] = {
8070            //Page number
8071            page: 3,
8072            //Row title ID
8073            rowTitle: 'product-artwork-location-title',
8074            //List of forms to group under title
8075            forms: [
8076                {
8077                    //Set the form name
8078                    name: 'product-config-artwork-form',
8079                    //Custom function that handles how an HTML string is constructed for key/value pairs in this particular form
8080                    writeLineTotal: function (name, value, page) {
8081                        //Get name value to display in total grid column
8082                        var newName = $('form input[name="' + name + '"]:checked').length ? $('form input[name="' + name + '"]:checked').data('name') : name;
8083                        //Get custom label in radio input
8084                        var label = $('form input[name="' + name + '"]:checked').length ? $('form input[name="' + name + '"]:checked').data('label') : value;
8085
8086                        return "<tr class='line-item' data-type='artwork' data-name='" + name + "' data-next-page=" + page + "><td>" + newName + " - " + label + "</td><td style='text-align:right;'>N/A</td></tr>";
8087                    },
8088                    //Object that holds all the validation rules for this form
8089                    validateRulesList: {
8090                        //Required rule - if there are no artwork options checked, validation check will fail
8091                        'required': function () {
8092                            var checkedArtwork = $('#product-config-artwork-form input[name="artwork"]:checked');
8093
8094                            if (!checkedArtwork.length)
8095                                return false;
8096                            else
8097                                return true;
8098                        }
8099                    },
8100                    displayValidateErrorList: {
8101                        //For required rule fail, output the following
8102                        'required': function () {
8103                            $('#product-selected-artwork').html('<span class="validation-error"><i class="fa fa-exclamation-circle"></i> Please select a method.</span>');
8104                        }
8105                    },
8106                    displayValidateSuccessList: {
8107                        'required': function () {
8108                        }
8109                    }
8110                }
8111            ],
8112            //Row title label
8113            title: 'Product Artwork'
8114        };
8115    }
8116
8117    /**
8118     * Retrieve the configuration options for a specific product configure page
8119     * @param {number} page - The page number
8120     * @return {object} The product configure page object
8121     */
8122    function getPageObj(page) {
8123        if (typeof (productConfigPages[page]) !== 'undefined') {
8124            return productConfigPages[page];
8125        }
8126    }
8127
8128    /**
8129     * Get the product configure page count
8130     * @return {number} The number of product configure pages
8131     */
8132    function getPageCount() {
8133        return Object.keys(productConfigPages).length;
8134    }
8135
8136    /**
8137     * Enable or disable dropzone based on current value of decoration method
8138     * @param {number} artworkType - The artwork radio value
8139     */
8140    function checkMethodStatus(artworkType) {
8141        if (artworkType == 1) {
8142            myDropzone.enable();
8143            $('#upload-artwork').fadeTo("fast", 1);
8144        } else {
8145            myDropzone.disable();
8146            $('#upload-artwork').fadeTo("fast", 0.3);
8147        }
8148    };
8149
8150    /**
8151     * Update the JavaScript based on the page that is loaded in product configure
8152     * @param {number} page - Page number
8153     */
8154    function updateJS(page) {
8155        //Get the page number
8156        switch (page) {
8157            case 1:
8158                //Initialize product grid with product config pricing table
8159                ProductGrid.InitGrids(ProductConfigure.GetPriceGrids(), Main.GetQuantityRanges(ProductConfigure.GetPriceGrids()));
8160                //Append the sizes HTML to the quantity container for the selected decoration method
8161                $('#product-config-quantity-container').empty().append(ProductConfigure.GetSizesHtml(ProductConfigure.GetSelectedDecorationMethodId()));
8162                //Redraw the product config pricing table
8163                $('#product-config-pricing-table').empty()
8164                    .append(ProductGrid.GetGridHtml(ProductConfigure.GetSelectedDecorationMethodId()))
8165                    .append(ProductGrid.GetVerticalGridHtml(ProductConfigure.GetSelectedDecorationMethodId()));
8166                //Add validation rules found in page configuration object
8167                ProductConfigure.AddValidationRules(getPageObj(page));
8168
8169                //If a decoration method is already defined, set it in form
8170                if (typeof (ProductConfigure.GetSelectedDecorationMethodId()) !== 'undefined') {
8171                    $('#product-config-method-form input[value=' + ProductConfigure.GetSelectedDecorationMethodId() + ']').iCheck('check');
8172                }
8173
8174                //Update inputs with selected data, if any
8175                ProductConfigure.UpdatePageValues(page);
8176
8177                //Set quantity in product grid
8178                ProductConfigure.SetQuantity($('#product-config-quantity-form input'));
8179                //If there are any discount prices available, cross out the regular prices
8180                if(Main.GetDiscount() > 0)
8181                    $('#product-config-pricing-table td span.regular-price').css('text-decoration', 'line-through');
8182
8183                //When in edit mode, if any of the parameters below are present, validate forms on first page
8184                if ((typeof (getUrlParameter('Edit')) !== 'undefined' && getUrlParameter('Edit')) ||
8185                        (
8186                            (typeof (getUrlParameter('Quantity')) !== 'undefined') &&
8187                            (typeof (getUrlParameter('SizeId')) !== 'undefined')
8188                        ) ||
8189                        (typeof (getUrlParameter('ColourId')) !== 'undefined') ||
8190                        (typeof (getUrlParameter('DecorationMethodId')) !== 'undefined')
8191                    )
8192                    //Validate the forms for page 1 and save the results of those tests
8193                    ProductConfigure.ValidateForms([], 1);
8194
8195                //Update total column on product configure with custom object
8196                if($('#product-config-quantity-form input#single-quantity').length)
8197                    ProductConfigure.ValidateForms(['product-config-quantity-form']);
8198                //Update total column on product configure with current page config object
8199                ProductConfigure.UpdateTotalColumn(getPageObj(page));
8200
8201                // Set the NumericInput plugin on quantity inputs to prevent decimal inputs.
8202                $('.quantity-input').numericInput({ min: 0, allowNegative: false, allowFloat: false });
8203
8204                break;
8205            case 2:
8206                //Add validation rules found in page configuration object
8207                ProductConfigure.AddValidationRules(getPageObj(page));
8208
8209                //If there is only one location, simply check it
8210                if ($('#product-config-location-form input').length == 1) {
8211                    $('#product-config-location-form input').iCheck('check');
8212                }
8213
8214                //Get reference to product decoration colour checkboxes
8215                var $checkboxesObj = $('#product-config-colours');
8216                //Hide original product decoration colour checkboxes
8217                $checkboxesObj.find('div.checkbox').hide();
8218                //Parse the checkboxes and return an object representing its values
8219                var listObject = Main.ParseElement($checkboxesObj);
8220                //Create an unordered list with custom colour swatches and add a custom validation function
8221                var swatchSelector = Main.CreateSwatches(listObject);
8222
8223                //Prepend the HTML for the custom colour swatches in the correct location
8224                $('#product-decoration-colours').prepend(swatchSelector);
8225
8226                //Get the maximum amount of decoration colours
8227                var maxInputs = ProductGrid.GetDecorationColourMax(ProductConfigure.GetSelectedDecorationMethodId());
8228
8229                //Output the number of max input controls for decoration colours
8230                for (i = 0; i < maxInputs; i++) {
8231                    $('#product-config-input').append(
8232                        $('<div />', { 'class': 'color-line' }).append(
8233                            $('<label />', { 'for': 'color-picker-' + (i + 1), 'html': getOrdinal(i + 1) + ' <span class="localized">color</span>' }),
8234                            $('<input />', { 'type': 'text', 'class': 'form-control colorpicker-popup', 'id': 'color-picker-' + (i + 1), 'name': 'color-picker-' + (i + 1) }),
8235                            $('<button />', { 'type': 'button', 'class': 'color-picker-remove', 'data-id': (i + 1) }).append(
8236                                $('<i />', { 'class': 'fa fa-close' })
8237                            )
8238                        )
8239                    );
8240                }
8241
8242                //Localize all relevant elements
8243                Main.LocalizePage();
8244
8245
8246                //Intialize colour picker for decoration colours
8247                $('.colorpicker-popup').colorpicker({
8248                    showOn: 'both',
8249                    buttonColorize: true,
8250                    parts: ['map', 'swatches', 'footer'],
8251                    swatches: 'pantone',
8252                    colorFormat: 'NAME',
8253                    swatchesWidth: 170,
8254                    limit: 'name',
8255                    select: function (e, color) {
8256                        $(e.target).data('bg-color', color.colorPicker.options.color);
8257
8258                        //Update decoration colour based on serialized decoration colour form
8259                        ProductConfigure.UpdateDecorationColour();
8260                        //Validate the form and save the results of those tests
8261                        ProductConfigure.ValidateForms(['product-config-decoration-colour-form']);
8262                        //Update total column on product configure with current page config object
8263                        ProductConfigure.UpdateTotalColumn(getPageObj(page));
8264                    }
8265                });
8266
8267                //Update inputs with selected data, if any
8268                ProductConfigure.UpdatePageValues(page);
8269                //If editing a product
8270                if (typeof (getUrlParameter('Edit')) !== 'undefined' && getUrlParameter('Edit'))
8271                    //Validate the forms for page 2 and save the results of those tests
8272                    ProductConfigure.ValidateForms([], 2);
8273                //Update total column on product configure with custom object
8274                ProductConfigure.UpdateTotalColumn(getPageObj(page));
8275                break;
8276            case 3:
8277                //Add validation rules found in page configuration object
8278                ProductConfigure.AddValidationRules(getPageObj(page));
8279
8280                //Get HTML for "Dropzone" template
8281                var template = $('#dropzoneTemplate').html();
8282                //Compile template using handlebars
8283                var handlebarsTemplate = Handlebars.compile(template);
8284                var html = handlebarsTemplate();
8285                //Empty and append dropzone html
8286                $('#artwork-tab').empty().append(html);
8287
8288                //Disabling autoDiscover, otherwise Dropzone will try to attach twice.
8289                Dropzone.autoDiscover = false;
8290                //Initialize Dropzone for drag-and-drop file uploading
8291                myDropzone = new Dropzone("#upload-artwork",
8292                {
8293                    //Target URL
8294                    url: "/Cart/AsyncAddArtwork",
8295                    //Only accept the file types below for decoration artwork
8296                    acceptedFiles: ".jpg,.jpeg,.png,.gif,.tif,.eps,.dst,.ai,.pdf,.svg",
8297                    addRemoveLinks: true,
8298                    maxFiles: 1,
8299                    paramName: "file",
8300                    init: function () {
8301                        this.on("addedfile", function (file) {
8302                            //Check if svg image is clean
8303                            sanitizeSVG(file).then(cleanResult => {
8304                                if (!cleanResult) {
8305                                    this.cancelUpload(file);
8306                                    swal('Upload Error', "Invalid file upload", 'error');
8307                                }
8308                            })
8309                            
8310                            //If there are 2 files, remove the first one in array
8311                            if (this.files[1] != null) {
8312                                this.removeFile(this.files[0]);
8313                            }
8314                        });
8315                        this.on("success", function (file) {
8316                            //If upload was successful, set the artwork file
8317                            if (typeof (file) !== 'undefined') {
8318                                var newDataObj = {
8319                                    'Filename': file.name,
8320                                    'Size': file.size
8321                                };
8322
8323                                ProductConfigure.SetArtworkFile(newDataObj);
8324                            }
8325                        });
8326                        this.on("error", function (file, errorMessage) {
8327                            //If upload not successful, remove file and show alert
8328                            this.removeFile(file);
8329
8330                            swal('Error', errorMessage, 'error');
8331                        });
8332                        this.on("removedfile", function (file) {
8333                            //When a file is removed, get artwork file
8334                            var currentFileObj = ProductConfigure.GetArtworkFile();
8335
8336                            if (typeof (currentFileObj) !== 'undefined') {
8337                                //Is the saved file equal to the file attempting to be removed?
8338                                if (file.name == currentFileObj.Filename) {
8339                                    if (!myDropzone.files.length) {
8340                                        var options = {
8341                                            callback: {
8342                                                done: function (response) {
8343                                                    swal({
8344                                                        title: 'File deleted.',
8345                                                        text: "Your file " + file.name + " was deleted.",
8346                                                        type: 'success',
8347                                                        showCancelButton: fal
8347se,
8348                                                        confirmButtonClass: 'swal2-confirm swal2-styled',
8349                                                        cancelButtonClass: 'swal2-cancel swal2-styled'
8350                                                    }).then(function () {
8351                                                        //Remove file and check the appropriate artwork option
8352                                                        ProductConfigure.RemoveArtworkFile();
8353
8354                                                        $('#product-config-artwork-form input[value=2]').iCheck('check');
8355                                                    });
8356                                                },
8357                                                fail: function () {
8358                                                    // Throw an exception if the request failed.
8359                                                    AsyncException({
8360                                                        ErrorType: 2,
8361                                                        Message: 'An error occurred while attempting to remove file.'
8362                                                    });
8363                                                }
8364                                            },
8365                                            params: {
8366                                                fileName: file.name
8367                                            }
8368                                        }
8369
8370                                        DataManager.process('Cart', 'RemoveArtwork', options);
8371                                    }
8372                                }
8373                            }
8374                        });
8375                    }
8376                });
8377
8378                //Check the appropriate artwork option
8379                $('#product-config-artwork-form input[value=2]').iCheck('check');
8380
8381                //Update inputs with selected data, if any
8382                ProductConfigure.UpdatePageValues(page);
8383                //If editing a product
8384                if (typeof (getUrlParameter('Edit')) !== 'undefined' && getUrlParameter('Edit'))
8385                    //Validate the forms for page 1 and save the results of those tests
8386                    ProductConfigure.ValidateForms([], 3);
8387                //Update total column on product configure with current page config object
8388                ProductConfigure.UpdateTotalColumn(getPageObj(page));
8389
8390                //Enable or disable dropzone based on current value of decoration method
8391                checkMethodStatus(parseInt($('#product-config-artwork-form input[name="artwork"]:checked').val()));
8392                break;
8393        }
8394
8395        //Convert all radios to iCheck display
8396        $('#product-config-content input[type=radio]').iCheck({
8397            radioClass: 'iradio_minimal'
8398        });
8399    }
8400
8401    return {
8402        Init: function() {
8403            init();
8404        },
8405        UpdateJS: function (page) {
8406            updateJS(page);
8407        },
8408        GetPageObj: function (page) {
8409            return getPageObj(page);
8410        },
8411        GetPageCount: function () {
8412            return getPageCount();
8413        },
8414        CheckMethodStatus: function (artworkType) {
8415            checkMethodStatus(artworkType);
8416        }
8417    }
8418}());;
8419//When an AJAX call is made, block the product config content
8420$(document).ajaxStart(function () {
8421    $('section#product-config-content').block({
8422        message: null,
8423        overlayCSS: {
8424            backgroundColor: '#ffffff'
8425        }
8426    });
8427});
8428
8429//When an AJAX call is done, unblock it
8430$(document).ajaxStop(function () {
8431    $('section#product-config-content').unblock();
8432});
8433
8434//When the next button is clicked, change the page
8435$('body').on('click', '#product-config-next', function () {
8436    //Validate the forms for the current page and save the results of those tests
8437    ProductConfigure.ValidateForms([], ProductConfigure.GetCurrentPage());
8438
8439    //If there is no failing form
8440    if (!ProductConfigure.GetFailedForm(ProductConfigure.GetCurrentPage()).length) {
8441        //Get the next page number and update the page display
8442        if (parseInt($(this).data('next-page'))) {
8443            ProductConfigure.ChangePage($(this).data('next-page'));
8444        }
8445    } else {
8446        //If there is a failing form, automatically scroll to it
8447        if ($("#" + ProductConfigure.GetFailedForm(ProductConfigure.GetCurrentPage())).parents('section').first().length)
8448            $('html,body').animate({ scrollTop: $("#" + ProductConfigure.GetFailedForm(ProductConfigure.GetCurrentPage())).parents('section').first().offset().top - $('#top-wrapper').height() }, 'slow');
8449    }
8450});
8451
8452//When the previous button is clicked, change the page
8453$('body').on('click', '#product-config-previous', function () {
8454    if (parseInt($(this).data('previous-page'))) {
8455        ProductConfigure.ChangePage($(this).data('previous-page'));
8456    }
8457});
8458
8459//Update the product config progress bar and add disabled class to all pages beyond the next page
8460ProductConfigure.UpdateDisabledPages();
8461
8462//When any numbers on the progress bar are clicked, move to that page
8463$('body').on('click', '.product-config-progress li a', function () {
8464    //If the page number can be parsed successfully
8465    if (parseInt($(this).data('page'))) {
8466        //Do not proceed if the button is disabled
8467        if ($(this).parent().hasClass('disabled')) {
8468            return;
8469        }
8470        
8471        //Validate the forms for the current page and save the results of those tests
8472        ProductConfigure.ValidateForms([],ProductConfigure.GetCurrentPage());
8473
8474        //If there is no failing form, the page clicked is equal to this page plus 1 OR the page clicked is the current page or a page preceeding it
8475        if (!(ProductConfigure.GetFailedForm(ProductConfigure.GetCurrentPage()).length && ($(this).data('page') == (ProductConfigure.GetCurrentPage() + 1))) || (parseInt($(this).data('page')) <= ProductConfigure.GetCurrentPage())) {
8476            ProductConfigure.ChangePage($(this).data('page'));
8477        } else {
8478            //If there is a failing form, automatically scroll to it
8479            if($("#" + ProductConfigure.GetFailedForm(ProductConfigure.GetCurrentPage())).parents('section').first().length)
8480                $('html,body').animate({ scrollTop: $("#" + ProductConfigure.GetFailedForm(ProductConfigure.GetCurrentPage())).parents('section').first().offset().top - $('#top-wrapper').height() }, 'slow');
8481        }
8482    }
8483});
8484
8485//Whenever a total row is moused over, add active class
8486$(document).on('mouseover', '#product-config-total tr.line-item', function () {
8487    $(this).find('td').addClass('active')
8488});
8489
8490//Whenever a total row is moused out, remove active class
8491$('body').on('mouseleave', '#product-config-total tr.line-item', function () {
8492    $(this).find('td').removeClass('active')
8493});
8494
8495//When a total row is clicked
8496$('body').on('click', '#product-config-total tr.line-item', function () {
8497    //Remove all down states for total columns
8498    ProductConfigure.ResetLineItemDownstates();
8499    //Add down state to line item
8500    $(this).find('td').addClass('down')
8501
8502    if (typeof ($(this).data('next-page')) !== 'undefined') {
8503        //If next page number is defined and it's not equal to the current page, go to that page
8504        if (ProductConfigure.GetCurrentPage() != parseInt($(this).data('next-page'))) {
8505            ProductConfigure.ChangePage($(this).data('next-page'));
8506        }
8507    }
8508
8509    if (typeof ($(this).data('type')) !== 'undefined') {
8510        //Depending on the total row clicked, go to corresponding form
8511        switch ($(this).data('type')) {
8512            //If quantity row clicked, scroll to quantity form
8513            case 'quantity':
8514                if (typeof ($(this).data('name')) !== 'undefined') {
8515                    $('html,body').animate({ scrollTop: $("#product-config-container").offset().top }, 'slow');
8516                    $('#product-config-quantity-form input[name="' + $(this).data('name') + '"]').focus()
8517                }
8518                break;
8519            //If method row clicked, scroll to method form
8520            case 'method':
8521                if (typeof ($(this).data('name')) !== 'undefined') {
8522                    $('html,body').animate({ scrollTop: $("#product-config-decoration-method").offset().top }, 'slow');
8523                    $('#product-config-method-form input[name="' + $(this).data('name') + '"]').focus()
8524                }
8525                break;
8526            //If location row clicked, scroll to location form
8527            case 'location':
8528                if (typeof ($(this).data('name')) !== 'undefined') {
8529                    $('html,body').animate({ scrollTop: $("#product-config-decoration-location").offset().top }, 'slow');
8530                    $('#product-config-location-form input[name="' + $(this).data('name') + '"]').focus()
8531                }
8532                break;
8533            //If decoration colour row clicked, scroll to decoration colour form
8534            case 'decoration-colour':
8535                if (typeof ($(this).data('name')) !== 'undefined') {
8536                    $('html,body').animate({ scrollTop: $("#product-config-colour").offset().top }, 'slow');
8537                }
8538                break;
8539            //If artwork row clicked, scroll to artwork form
8540            case 'artwork':
8541                if (typeof ($(this).data('name')) !== 'undefined') {
8542                    $('html,body').animate({ scrollTop: $("#product-config-artwork-options").offset().top }, 'slow');
8543                    $('#product-config-artwork-form input[name="' + $(this).data('name') + '"]').focus()
8544                }
8545                break;
8546        }
8547    }
8548});
8549
8550//When back to cart button is clicked, get product configure parameters and submit via form POST
8551$('#back-to-cart').on('click', function () {
8552    let params = TpcUtility.stripHtmlTags(ProductConfigure.GetProductConfigureParam
8552s());
8553    $.form('/cart/additem/', params, 'POST').submit();
8554});
8555
8556//When product config quantity form is submitted, cancel event
8557$('body').on('submit', '#product-config-quantity-form', function (e) {
8558    e.preventDefault();
8559});
8560
8561//Add click handler to handle changing of quantity manually
8562$('body').on('change', '#product-config-quantity-form input', function () {
8563    var page = 1;
8564
8565    //Get quantity range from product grid
8566    var quantityRange = ProductGrid.GetQuantityRange(ProductConfigure.GetSelectedDecorationMethodId());
8567
8568    if (quantityRange.length) {
8569        //If quantity is greater or equal to the lowest possible value...
8570        if (parseInt($('.product-quantity').val()) >= quantityRange[0].QuantityFrom)
8571            //Update newly decremented value in input
8572            $('.product-quantity-container .product-quantity').val($(this).val());
8573
8574        //Set quantity in product configure class
8575        ProductConfigure.SetQuantity($('#product-config-quantity-form input'));
8576        //If there are any discount prices available, cross out the regular prices
8577        if (Main.GetDiscount() > 0)
8578            $('#product-config-pricing-table td span.regular-price').css('text-decoration', 'line-through');
8579        //Validate the form and save the results of those tests
8580        ProductConfigure.ValidateForms(['product-config-quantity-form']);
8581        //Update total column on product configure with current page config object
8582        ProductConfigure.UpdateTotalColumn(ProductConfigureSteps.GetPageObj(page));
8583    }
8584});
8585
8586//Add click handler to add quantity button
8587$('body').on('click', '.product-quantity-container .add-quantity', function () {
8588    var page = 1;
8589
8590    //Parse integer from quantity input values
8591    var newValue = parseInt($('.product-quantity-container input').val()) + 1;
8592    //Update newly incremented value in input
8593    $('.product-quantity-container input').val(newValue);
8594    //Used to revert value to a legal number (higher than the minimum quantity)
8595    $('.product-quantity-container input').data('current-value', $('.product-quantity-container input').val());
8596    //Set quantity in product configure class
8597    ProductConfigure.SetQuantity($('#product-config-quantity-form input'));
8598    //If there are any discount prices available, cross out the regular prices
8599    if (Main.GetDiscount() > 0)
8600        $('#product-config-pricing-table td span.regular-price').css('text-decoration', 'line-through');
8601    //Validate the forms for the current page and save the results of those tests
8602    ProductConfigure.ValidateForms(['product-config-quantity-form']);
8603    //Update total column on product configure with current page config object
8604    ProductConfigure.UpdateTotalColumn(ProductConfigureSteps.GetPageObj(page));
8605});
8606
8607//Add click handler to the remove quantity button
8608$('body').on('click', '.product-quantity-container .remove-quantity', function () {
8609    var page = 1;
8610
8611    //Parse integer from quantity input values
8612    var newValue = parseInt($('.product-quantity-container input').val()) - 1;
8613    //Get quantity range from product grid
8614    var quantityRange = ProductGrid.GetQuantityRange(ProductConfigure.GetSelectedDecorationMethodId());
8615
8616    if (quantityRange.length) {
8617        //If quantity is greater than the lowest possible value...
8618        if (parseInt($('.product-quantity-container input').val()) > quantityRange[0].QuantityFrom) {
8619            //Update newly decremented value in input
8620            $('.product-quantity-container input').val(newValue);
8621            $('.product-quantity-container input').data('current-value', $('.product-quantity-container input').val());
8622        }
8623        //Set quantity in product configure class
8624        ProductConfigure.SetQuantity($('#product-config-quantity-form input'));
8625        //If there are any discount prices available, cross out the regular prices
8626        if (Main.GetDiscount() > 0)
8627            $('#product-config-pricing-table td span.regular-price').css('text-decoration', 'line-through');
8628        //Validate the forms for the current page and save the results of those tests
8629        ProductConfigure.ValidateForms(['product-config-quantity-form']);
8630        //Update total column on product configure with current page config object
8631        ProductConfigure.UpdateTotalColumn(ProductConfigureSteps.GetPageObj(page));
8632    }
8633});
8634
8635$('body').on('ifChecked', '#product-config-method-form input', function () {
8636    var page = 1;
8637
8638    //Set decoration method to the selected radio button
8639    ProductConfigure.SetMethod($(this));
8640
8641    //Append the sizes HTML to the quantity container for the selected decoration method
8642    $('#product-config-quantity-container').empty().append(ProductConfigure.GetSizesHtml(ProductConfigure.GetSelectedDecorationMethodId()));
8643    //Redraw the product config pricing table
8644    $('#product-config-pricing-table').empty()
8645        .append(ProductGrid.GetGridHtml(ProductConfigure.GetSelectedDecorationMethodId()))
8646        .append(ProductGrid.GetVerticalGridHtml(ProductConfigure.GetSelectedDecorationMethodId()));
8647    
8648    //Get the quantity range for selected decoration method
8649    var quantityRange = ProductGrid.GetQuantityRange(ProductConfigure.GetSelectedDecorationMethodId());
8650    if (quantityRange.length) {
8651        //If the current quantity is out of bounds...
8652        if ((ProductConfigure.GetQuantityCount() > 0) && (ProductConfigure.GetQuantityCount() < quantityRange[0].QuantityFrom)) {
8653            //Set the product quantity to the lowest possible value
8654            $('.product-quantity-container .product-quantity').val(quantityRange[0].QuantityFrom);
8655            $('.product-quantity-container .product-quantity').data('current-value', quantityRange[0].QuantityFrom);
8656
8657            //Display a notification when user selects a different decoration method
8658            swal('Price updated!', 'You have selected a different decoration method. <strong>Minimum quantity</strong> and <strong>pricing</strong> have been updated.', 'info');
8659
8660            //Scroll back up to the product grid after user selects a different method
8661            $('html,body').animate({ scrollTop: $("#product-config-steps").offset().top }, 'slow');
8662        }
8663    }
8664
8665    if ($('#colour-selector').length) {
8666        //Empty the colour selector and get colours based on passed in decoration method
8667        $('#colour-selector').empty();
8668        var colours = (ProductGrid.GetDecorationMethodColours(ProductConfigure.GetSelectedDecorationMethodId()).length) ? ProductGrid.GetDecorationMethodColours(ProductConfigure.GetSelectedDecorationMethodId()) : ProductConfigure.GetProductColours();
8669        //Get all colour IDs from the colours array
8670        var colourIds = colours.map(function (value) {
8671            return value.Id;
8672        });
8673
8674        //Append a default option
8675        $('#colour-selector').append($('<option />').val("").text(''));
8676        colours.forEach(function (value) {
8677            //For each returned colour, build options and append relevant attributes
8678            var listItem = $('<option />').val(value.Id);
8679            listItem.text(value.Label);
8680            listItem.data('bg-color', value.HexCodes.map(function (value) {
8681                return value.HexCode;
8682            }));
8683            listItem.data('grid-id', value.GridId);
8684
8685            $('#colour-selector').append(listItem);
8686        });
8687
8688        //Get reference to product colour select
8689        var $selectObj = $('#colour-selector').hide();
8690        //Parse the select and return an object representing its values
8691        var listObject = Main.ParseElement($selectObj);
8692        //Create an unordered list with custom colour swatches
8693        var swatchSelector = Main.CreateSwatches(listObject);
8694        //Prepend the HTML for the custom colour swatches in the correct location
8695        $('#product-colours > ul').remove();
8696        $('#product-colours').prepend(swatchSelector);
8697
8698        //If only one colour returned, trigger a click event
8699        if ($('#product-config-colour #product-colours ul li').length == 1) {
8700            $('#product-config-colour #product-colours ul li a').trigger('click');
8701        } else {
8702            //Otherwise, set colour to null
8703            if($.inArray(ProductConfigure.GetColour(),colourIds) == -1)
8704                ProductConfigure.SetColour(null);
8705        }
8706    }
8707
8708    //Populate product quantity inputs with existing values
8709    ProductConfigure.PopulateQuantityInputs();
8710    //Sets the quantity values of the given product based on the quantity input group 
8711    ProductConfigure.SetQuantity($('#product-config-quantity-form input'));
8712    //Validate form passed in based on custom predefined validation rules on a given page
8713    ProductConfigure.ValidateForms(['product-config-method-form']);
8714    //Update total column on product configure with current page config object
8715    ProductConfigure.UpdateTotalColumn(ProductConfigureSteps.GetPageObj(page));
8716});
8717
8718$('body').on('click touchstart', '#product-config-colour #product-colours ul li a', function (e) {
8719    e.stopPropagation();
8720    e.preventDefault();
8721
8722    var page = 1;
8723
8724    // Get valid grids for selected colour and enable only relevant sizes
8725    var gridIds = Object.keys(ProductGrid.GetColorEnabledGrids(ProductConfigure.GetSelectedDecorationMethodId(), $(e.target).data('id')));
8726
8727    if (gridIds.length && $('#product-config-quantity-form input').length > 1) {
8728        // Disable all sizes by default
8729        $('#product-config-quantity-form input').prop('disabled', true);
8730
8731        //Loop through colour enabled grid ids
8732        gridIds.forEach(function (value) {
8733            $('#product-config-quantity-form input').each(function () {
8734                //For each quantity input, get the grid ID and split it on the comma
8735                var ids = $(this).data('grid-id').toString().split(',');
8736
8737                //If the current grid ID isn't found in the ids array
8738                if ($.inArray(value, ids) != -1) {
8739                    //Disable the current colour
8740                    $(this).prop('disabled', false);
8741                }
8742            });
8743        });
8744
8745        // If there are still disabled inputs, clear the values
8746        $('#product-config-quantity-form input:disabled').each(function () {
8747            $(this).val('');
8748        });
8749    }
8750
8751    ProductConfigure.SelectProductSwatch(e.target);
8752});
8753
8754//Add click handler when location is checked
8755$('body').on('ifChecked', '#product-config-location-form input', function () {
8756    var page = 2;
8757
8758    //Set quantity in product configure class
8759    if ($(this).data('src').length)
8760        $('#decoration-location-img').empty().append($('<img />', { 'src': "/Images/" + $(this).data('src'), 'class': 'img-responsive', 'alt': '' }));
8761    else
8762        $('#decoration-location-img').empty();
8763
8764    //Set a decoration location for a given product
8765    ProductConfigure.SetLocation($(this));
8766    //Validate form passed in based on custom predefined validation rules on a given page
8767    ProductConfigure.ValidateForms(['product-config-location-form']);
8768    //Update total column on product configure with current page config object
8769    ProductConfigure.UpdateTotalColumn(ProductConfigureSteps.GetPageObj(page));
8770});
8771
8772//When product config decoration colour form is submitted, cancel event
8773$('body').on('submit', '#product-config-decoration-colour-form', function (e) {
8774    e.preventDefault();
8775});
8776
8777//When add to cart button is clicked
8778$('body').on('click', '#add-to-cart', function (e) {
8779    var page = 3;
8780
8781    e.preventDefault();
8782
8783    //Validate form passed in based on custom predefined validation rules on selected page
8784    ProductConfigure.ValidateForms([], page);
8785    //If there are no failed forms on given page, get product configure parameters and submit via form POST
8786    if (!ProductConfigure.GetFailedForm(page).length) {
8787        let params = TpcUtility.stripHtmlTags(ProductConfigure.GetProductConfigureParam
8787s());
8788        $.form('/cart/additem/', params, 'POST').submit();
8789    } else {
8790        //If there is a failing form, automatically scroll to it
8791        $('html,body').animate({ scrollTop: $("#" + ProductConfigure.GetFailedForm(ProductConfigure.GetCurrentPage())).parents('section').first().offset().top - $('#top-wrapper').height() }, 'slow');
8792    }
8793});
8794
8795//When artwork radio button is checked
8796$('body').on('ifChecked', '#product-config-artwork-form input', function () {
8797    var page = 3;
8798
8799    //Set artwork to value of input that was clicked in product configure class
8800    ProductConfigure.SetArtwork($(this));
8801    //Validate the passed in form and save the results of those tests
8802    ProductConfigure.ValidateForms(['product-config-artwork-form']);
8803    //Update total column on product configure with current page config object
8804    ProductConfigure.UpdateTotalColumn(ProductConfigureSteps.GetPageObj(page));
8805    //Fade upload artwork control in or out depending on passed in value
8806    ProductConfigureSteps.CheckMethodStatus(parseInt($(this).val()))
8807});
8808
8809//Set value of comments on every key press
8810$('body').on('keyup', '#product-config-comments-form #comments', function () {
8811    ProductConfigure.SetComments($(this).val());
8812});
8813
8814//When a decoration colour swatch is clicked
8815$('body').on('click touchstart', '#product-decoration-colours ul li a', function (e) {
8816    e.stopPropagation();
8817    e.preventDefault();
8818
8819    //Get PMS value
8820    if ($(e.target).data('pms')) {
8821        $('.colorpicker-popup').each(function () {
8822            if (!$(this).colorpicker('getColor')) {
8823                //Set the colour in colour picker with passed in PMS value
8824                $(this).colorpicker('setColor', $(e.target).data('pms'));
8825                return false;
8826            }
8827        });
8828    }
8829});
8830
8831//When remove button next to decoration colour is clicked
8832$('body').on('click', 'button.color-picker-remove', function (e) {
8833    if ($('#color-picker-' + $(e.currentTarget).data('id')).length) {
8834        //Set colour to null 
8835        $('#color-picker-' + $(e.currentTarget).data('id')).colorpicker('setColor', null);
8836    }
8837});
8838
8839//Initialize the main slick carousel with predetermined options
8840$('.product-slider').slick({
8841    slidesToShow: 1,
8842    slidesToScroll: 1,
8843    prevArrow: "<i class='fa fa-chevron-left slick-prev'></i>",
8844    nextArrow: "<i class='fa fa-chevron-right slick-next'></i>",
8845    fade: true
8846});;

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.