PageSourceSearch

https://idimex.de/modules/formmaker/views/js/spectrum.js

js idimex.de collected 2026-10-09 23:30:31 UTC 82,450 bytes, 2,352 lines download raw bytes

vendor: 37,262 bytes, lines 1-1021
1/**
2 * Formmaker
3 *
4 * @author    silbersaiten <[email protected]>
5 * @copyright 2022 silbersaiten
6 * @license   See joined file licence.txt
7 * @version   1.4.0
8 * @website   https://www.silbersaiten.de
9 * @category  Module
10 * @support   silbersaiten <[email protected]>
11 */
12// Spectrum Colorpicker v1.8.0
13// https://github.com/bgrins/spectrum
14// Author: Brian Grinstead
15// License: MIT
16
17(function (factory) {
18    "use strict";
19
20    if (typeof define === 'function' && define.amd) { // AMD
21        define(['jquery'], factory);
22    }
23    else if (typeof exports == "object" && typeof module == "object") { // CommonJS
24        module.exports = factory(require('jquery'));
25    }
26    else { // Browser
27        factory(jQuery);
28    }
29})(function($, undefined) {
30    "use strict";
31
32    var defaultOpts = {
33
34        // Callbacks
35        beforeShow: noop,
36        move: noop,
37        change: noop,
38        show: noop,
39        hide: noop,
40
41        // Options
42        color: false,
43        flat: false,
44        showInput: false,
45        allowEmpty: false,
46        showButtons: true,
47        clickoutFiresChange: true,
48        showInitial: false,
49        showPalette: false,
50        showPaletteOnly: false,
51        hideAfterPaletteSelect: false,
52        togglePaletteOnly: false,
53        showSelectionPalette: true,
54        localStorageKey: false,
55        appendTo: "body",
56        maxSelectionSize: 7,
57        cancelText: "cancel",
58        chooseText: "choose",
59        togglePaletteMoreText: "more",
60        togglePaletteLessText: "less",
61        clearText: "Clear Color Selection",
62        noColorSelectedText: "No Color Selected",
63        preferredFormat: false,
64        className: "", // Deprecated - use containerClassName and replacerClassName instead.
65        containerClassName: "",
66        replacerClassName: "",
67        showAlpha: false,
68        theme: "sp-light",
69        palette: [["#ffffff", "#000000", "#ff0000", "#ff8000", "#ffff00", "#008000", "#0000ff", "#4b0082", "#9400d3"]],
70        selectionPalette: [],
71        disabled: false,
72        offset: null
73    },
74    spectrums = [],
75    IE = !!/msie/i.exec( window.navigator.userAgent ),
76    rgbaSupport = (function() {
77        function contains( str, substr ) {
78            return !!~('' + str).indexOf(substr);
79        }
80
81        var elem = document.createElement('div');
82        var style = elem.style;
83        style.cssText = 'background-color:rgba(0,0,0,.5)';
84        return contains(style.backgroundColor, 'rgba') || contains(style.backgroundColor, 'hsla');
85    })(),
86    replaceInput = [
87        "<div class='sp-replacer'>",
88            "<div class='sp-preview'><div class='sp-preview-inner'></div></div>",
89            "<div class='sp-dd'>&#9660;</div>",
90        "</div>"
91    ].join(''),
92    markup = (function () {
93
94        // IE does not support gradients with multiple stops, so we need to simulate
95        //  that for the rainbow slider with 8 divs that each have a single gradient
96        var gradientFix = "";
97        if (IE) {
98            for (var i = 1; i <= 6; i++) {
99                gradientFix += "<div class='sp-" + i + "'></div>";
100            }
101        }
102
103        return [
104            "<div class='sp-container sp-hidden'>",
105                "<div class='sp-palette-container'>",
106                    "<div class='sp-palette sp-thumb sp-cf'></div>",
107                    "<div class='sp-palette-button-container sp-cf'>",
108                        "<button type='button' class='sp-palette-toggle'></button>",
109                    "</div>",
110                "</div>",
111                "<div class='sp-picker-container'>",
112                    "<div class='sp-top sp-cf'>",
113                        "<div class='sp-fill'></div>",
114                        "<div class='sp-top-inner'>",
115                            "<div class='sp-color'>",
116                                "<div class='sp-sat'>",
117                                    "<div class='sp-val'>",
118                                        "<div class='sp-dragger'></div>",
119                                    "</div>",
120                                "</div>",
121                            "</div>",
122                            "<div class='sp-clear sp-clear-display'>",
123                            "</div>",
124                            "<div class='sp-hue'>",
125                                "<div class='sp-slider'></div>",
126                                gradientFix,
127                            "</div>",
128                        "</div>",
129                        "<div class='sp-alpha'><div class='sp-alpha-inner'><div class='sp-alpha-handle'></div></div></div>",
130                    "</div>",
131                    "<div class='sp-input-container sp-cf'>",
132                        "<input class='sp-input' type='text' spellcheck='false'  />",
133                    "</div>",
134                    "<div class='sp-initial sp-thumb sp-cf'></div>",
135                    "<div class='sp-button-container sp-cf'>",
136                        "<a class='sp-cancel' href='#'></a>",
137                        "<button type='button' class='sp-choose'></button>",
138                    "</div>",
139                "</div>",
140            "</div>"
141        ].join("");
142    })();
143
144    function paletteTemplate (p, color, className, opts) {
145        var html = [];
146        for (var i = 0; i < p.length; i++) {
147            var current = p[i];
148            if(current) {
149                var tiny = tinycolor(current);
150                var c = tiny.toHsl().l < 0.5 ? "sp-thumb-el sp-thumb-dark" : "sp-thumb-el sp-thumb-light";
151                c += (tinycolor.equals(color, current)) ? " sp-thumb-active" : "";
152                var formattedString = tiny.toString(opts.preferredFormat || "rgb");
153                var swatchStyle = rgbaSupport ? ("background-color:" + tiny.toRgbString()) : "filter:" + tiny.toFilter();
154                html.push('<span title="' + formattedString + '" data-color="' + tiny.toRgbString() + '" class="' + c + '"><span class="sp-thumb-inner" style="' + swatchStyle + ';" /></span>');
155            } else {
156                var cls = 'sp-clear-display';
157                html.push($('<div />')
158                    .append($('<span data-color="" style="background-color:transparent;" class="' + cls + '"></span>')
159                        .attr('title', opts.noColorSelectedText)
160                    )
161                    .html()
162                );
163            }
164        }
165        return "<div class='sp-cf " + className + "'>" + html.join('') + "</div>";
166    }
167
168    function hideAll() {
169        for (var i = 0; i < spectrums.length; i++) {
170            if (spectrums[i]) {
171                spectrums[i].hide();
172            }
173        }
174    }
175
176    function instanceOptions(o, callbackContext) {
177        var opts = $.extend({}, defaultOpts, o);
178        opts.callbacks = {
179            'move': bind(opts.move, callbackContext),
180            'change': bind(opts.change, callbackContext),
181            'show': bind(opts.show, callbackContext),
182            'hide': bind(opts.hide, callbackContext),
183            'beforeShow': bind(opts.beforeShow, callbackContext)
184        };
185
186        return opts;
187    }
188
189    function spectrum(element, o) {
190
191        var opts = instanceOptions(o, element),
192            flat = opts.flat,
193            showSelectionPalette = opts.showSelectionPalette,
194            localStorageKey = opts.localStorageKey,
195            theme = opts.theme,
196            callbacks = opts.callbacks,
197            resize = throttle(reflow, 10),
198            visible = false,
199            isDragging = false,
200            dragWidth = 0,
201            dragHeight = 0,
202            dragHelperHeight = 0,
203            slideHeight = 0,
204            slideWidth = 0,
205            alphaWidth = 0,
206            alphaSlideHelperWidth = 0,
207            slideHelperHeight = 0,
208            currentHue = 0,
209            currentSaturation = 0,
210            currentValue = 0,
211            currentAlpha = 1,
212            palette = [],
213            paletteArray = [],
214            paletteLookup = {},
215            selectionPalette = opts.selectionPalette.slice(0),
216            maxSelectionSize = opts.maxSelectionSize,
217            draggingClass = "sp-dragging",
218            shiftMovementDirection = null;
219
220        var doc = element.ownerDocument,
221            body = doc.body,
222            boundElement = $(element),
223            disabled = false,
224            container = $(markup, doc).addClass(theme),
225            pickerContainer = container.find(".sp-picker-container"),
226            dragger = container.find(".sp-color"),
227            dragHelper = container.find(".sp-dragger"),
228            slider = container.find(".sp-hue"),
229            slideHelper = container.find(".sp-slider"),
230            alphaSliderInner = container.find(".sp-alpha-inner"),
231            alphaSlider = container.find(".sp-alpha"),
232            alphaSlideHelper = container.find(".sp-alpha-handle"),
233            textInput = container.find(".sp-input"),
234            paletteContainer = container.find(".sp-palette"),
235            initialColorContainer = container.find(".sp-initial"),
236            cancelButton = container.find(".sp-cancel"),
237            clearButton = container.find(".sp-clear"),
238            chooseButton = container.find(".sp-choose"),
239            toggleButton = container.find(".sp-palette-toggle"),
240            isInput = boundElement.is("input"),
241            isInputTypeColor = isInput && boundElement.attr("type") === "color" && inputTypeColorSupport(),
242            shouldReplace = isInput && !flat,
243            replacer = (shouldReplace) ? $(replaceInput).addClass(theme).addClass(opts.className).addClass(opts.replacerClassName) : $([]),
244            offsetElement = (shouldReplace) ? replacer : boundElement,
245            previewElement = replacer.find(".sp-preview-inner"),
246            initialColor = opts.color || (isInput && boundElement.val()),
247            colorOnShow = false,
248            currentPreferredFormat = opts.preferredFormat,
249            clickoutFiresChange = !opts.showButtons || opts.clickoutFiresChange,
250            isEmpty = !initialColor,
251            allowEmpty = opts.allowEmpty && !isInputTypeColor;
252
253        function applyOptions() {
254
255            if (opts.showPaletteOnly) {
256                opts.showPalette = true;
257            }
258
259            toggleButton.text(opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText);
260
261            if (opts.palette) {
262                palette = opts.palette.slice(0);
263                paletteArray = $.isArray(palette[0]) ? palette : [palette];
264                paletteLookup = {};
265                for (var i = 0; i < paletteArray.length; i++) {
266                    for (var j = 0; j < paletteArray[i].length; j++) {
267                        var rgb = tinycolor(paletteArray[i][j]).toRgbString();
268                        paletteLookup[rgb] = true;
269                    }
270                }
271            }
272
273            container.toggleClass("sp-flat", flat);
274            container.toggleClass("sp-input-disabled", !opts.showInput);
275            container.toggleClass("sp-alpha-enabled", opts.showAlpha);
276            container.toggleClass("sp-clear-enabled", allowEmpty);
277            container.toggleClass("sp-buttons-disabled", !opts.showButtons);
278            container.toggleClass("sp-palette-buttons-disabled", !opts.togglePaletteOnly);
279            container.toggleClass("sp-palette-disabled", !opts.showPalette);
280            container.toggleClass("sp-palette-only", opts.showPaletteOnly);
281            container.toggleClass("sp-initial-disabled", !opts.showInitial);
282            container.addClass(opts.className).addClass(opts.containerClassName);
283
284            reflow();
285        }
286
287        function initialize() {
288
289            if (IE) {
290                container.find("*:not(input)").attr("unselectable", "on");
291            }
292
293            applyOptions();
294
295            if (shouldReplace) {
296                boundElement.after(replacer).hide();
297            }
298
299            if (!allowEmpty) {
300                clearButton.hide();
301            }
302
303            if (flat) {
304                boundElement.after(container).hide();
305            }
306            else {
307
308                var appendTo = opts.appendTo === "parent" ? boundElement.parent() : $(opts.appendTo);
309                if (appendTo.length !== 1) {
310                    appendTo = $("body");
311                }
312
313                appendTo.append(container);
314            }
315
316            updateSelectionPaletteFromStorage();
317
318            offsetElement.on("click.spectrum touchstart.spectrum", function (e) {
319                if (!disabled) {
320                    toggle();
321                }
322
323                e.stopPropagation();
324
325                if (!$(e.target).is("input")) {
326                    e.preventDefault();
327                }
328            });
329
330            if(boundElement.is(":disabled") || (opts.disabled === true)) {
331                disable();
332            }
333
334            // Prevent clicks from bubbling up to document.  This would cause it to be hidden.
335            container.click(stopPropagation);
336
337            // Handle user typed input
338            textInput.change(setFromTextInput);
339            textInput.on("paste", function () {
340                setTimeout(setFromTextInput, 1);
341            });
342            textInput.keydown(function (e) { if (e.keyCode == 13) { setFromTextInput(); } });
343
344            cancelButton.text(opts.cancelText);
345            cancelButton.on("click.spectrum", function (e) {
346                e.stopPropagation();
347                e.preventDefault();
348                revert();
349                hide();
350            });
351
352            clearButton.attr("title", opts.clearText);
353            clearButton.on("click.spectrum", function (e) {
354                e.stopPropagation();
355                e.preventDefault();
356                isEmpty = true;
357                move();
358
359                if(flat) {
360                    //for the flat style, this is a change event
361                    updateOriginalInput(true);
362                }
363            });
364
365            chooseButton.text(opts.chooseText);
366            chooseButton.on("click.spectrum", function (e) {
367                e.stopPropagation();
368                e.preventDefault();
369
370                if (IE && textInput.is(":focus")) {
371                    textInput.trigger('change');
372                }
373
374                if (isValid()) {
375                    updateOriginalInput(true);
376                    hide();
377                }
378            });
379
380            toggleButton.text(opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText);
381            toggleButton.on("click.spectrum", function (e) {
382                e.stopPropagation();
383                e.preventDefault();
384
385                opts.showPaletteOnly = !opts.showPaletteOnly;
386
387                // To make sure the Picker area is drawn on the right, next to the
388                // Palette area (and not below the palette), first move the Palette
389                // to the left to make space for the picker, plus 5px extra.
390                // The 'applyOptions' function puts the whole container back into place
391                // and takes care of the button-text and the sp-palette-only CSS class.
392                if (!opts.showPaletteOnly && !flat) {
393                    container.css('left', '-=' + (pickerContainer.outerWidth(true) + 5));
394                }
395                applyOptions();
396            });
397
398            draggable(alphaSlider, function (dragX, dragY, e) {
399                currentAlpha = (dragX / alphaWidth);
400                isEmpty = false;
401                if (e.shiftKey) {
402                    currentAlpha = Math.round(currentAlpha * 10) / 10;
403                }
404
405                move();
406            }, dragStart, dragStop);
407
408            draggable(slider, function (dragX, dragY) {
409                currentHue = parseFloat(dragY / slideHeight);
410                isEmpty = false;
411                if (!opts.showAlpha) {
412                    currentAlpha = 1;
413                }
414                move();
415            }, dragStart, dragStop);
416
417            draggable(dragger, function (dragX, dragY, e) {
418
419                // shift+drag should snap the movement to either the x or y axis.
420                if (!e.shiftKey) {
421                    shiftMovementDirection = null;
422                }
423                else if (!shiftMovementDirection) {
424                    var oldDragX = currentSaturation * dragWidth;
425                    var oldDragY = dragHeight - (currentValue * dragHeight);
426                    var furtherFromX = Math.abs(dragX - oldDragX) > Math.abs(dragY - oldDragY);
427
428                    shiftMovementDirection = furtherFromX ? "x" : "y";
429                }
430
431                var setSaturation = !shiftMovementDirection || shiftMovementDirection === "x";
432                var setValue = !shiftMovementDirection || shiftMovementDirection === "y";
433
434                if (setSaturation) {
435                    currentSaturation = parseFloat(dragX / dragWidth);
436                }
437                if (setValue) {
438                    currentValue = parseFloat((dragHeight - dragY) / dragHeight);
439                }
440
441                isEmpty = false;
442                if (!opts.showAlpha) {
443                    currentAlpha = 1;
444                }
445
446                move();
447
448            }, dragStart, dragStop);
449
450            if (!!initialColor) {
451                set(initialColor);
452
453                // In case color was black - update the preview UI and set the format
454                // since the set function will not run (default color is black).
455                updateUI();
456                currentPreferredFormat = opts.preferredFormat || tinycolor(initialColor).format;
457
458                addColorToSelectionPalette(initialColor);
459            }
460            else {
461                updateUI();
462            }
463
464            if (flat) {
465                show();
466            }
467
468            function paletteElementClick(e) {
469                if (e.data && e.data.ignore) {
470                    set($(e.target).closest(".sp-thumb-el").data("color"));
471                    move();
472                }
473                else {
474                    set($(e.target).closest(".sp-thumb-el").data("color"));
475                    move();
476
477                    // If the picker is going to close immediately, a palette selection
478                    // is a change.  Otherwise, it's a move only.
479                    if (opts.hideAfterPaletteSelect) {
480                        updateOriginalInput(true);
481                        hide();
482                    } else {
483                        updateOriginalInput();
484                    }
485                }
486
487                return false;
488            }
489
490            var paletteEvent = IE ? "mousedown.spectrum" : "click.spectrum touchstart.spectrum";
491            paletteContainer.on(paletteEvent, ".sp-thumb-el", paletteElementClick);
492            initialColorContainer.on(paletteEvent, ".sp-thumb-el:nth-child(1)", { ignore: true }, paletteElementClick);
493        }
494
495        function updateSelectionPaletteFromStorage() {
496
497            if (localStorageKey && window.localStorage) {
498
499                // Migrate old palettes over to new format.  May want to remove this eventually.
500                try {
501                    var oldPalette = window.localStorage[localStorageKey].split(",#");
502                    if (oldPalette.length > 1) {
503                        delete window.localStorage[localStorageKey];
504                        $.each(oldPalette, function(i, c) {
505                             addColorToSelectionPalette(c);
506                        });
507                    }
508                }
509                catch(e) { }
510
511                try {
512                    selectionPalette = window.localStorage[localStorageKey].split(";");
513                }
514                catch (e) { }
515            }
516        }
517
518        function addColorToSelectionPalette(color) {
519            if (showSelectionPalette) {
520                var rgb = tinycolor(color).toRgbString();
521                if (!paletteLookup[rgb] && $.inArray(rgb, selectionPalette) === -1) {
522                    selectionPalette.push(rgb);
523                    while(selectionPalette.length > maxSelectionSize) {
524                        selectionPalette.shift();
525                    }
526                }
527
528                if (localStorageKey && window.localStorage) {
529                    try {
530                        window.localStorage[localStorageKey] = selectionPalette.join(";");
531                    }
532                    catch(e) { }
533                }
534            }
535        }
536
537        function getUniqueSelectionPalette() {
538            var unique = [];
539            if (opts.showPalette) {
540                for (var i = 0; i < selectionPalette.length; i++) {
541                    var rgb = tinycolor(selectionPalette[i]).toRgbString();
542
543                    if (!paletteLookup[rgb]) {
544                        unique.push(selectionPalette[i]);
545                    }
546                }
547            }
548
549            return unique.reverse().slice(0, opts.maxSelectionSize);
550        }
551
552        function drawPalette() {
553
554            var currentColor = get();
555
556            var html = $.map(paletteArray, function (palette, i) {
557                return paletteTemplate(palette, currentColor, "sp-palette-row sp-palette-row-" + i, opts);
558            });
559
560            updateSelectionPaletteFromStorage();
561
562            if (selectionPalette) {
563                html.push(paletteTemplate(getUniqueSelectionPalette(), currentColor, "sp-palette-row sp-palette-row-selection", opts));
564            }
565
566            paletteContainer.html(html.join(""));
567        }
568
569        function drawInitial() {
570            if (opts.showInitial) {
571                var initial = colorOnShow;
572                var current = get();
573                initialColorContainer.html(paletteTemplate([initial, current], current, "sp-palette-row-initial", opts));
574            }
575        }
576
577        function dragStart() {
578            if (dragHeight <= 0 || dragWidth <= 0 || slideHeight <= 0) {
579                reflow();
580            }
581            isDragging = true;
582            container.addClass(draggingClass);
583            shiftMovementDirection = null;
584            boundElement.trigger('dragstart.spectrum', [ get() ]);
585        }
586
587        function dragStop() {
588            isDragging = false;
589            container.removeClass(draggingClass);
590            boundElement.trigger('dragstop.spectrum', [ get() ]);
591        }
592
593        function setFromTextInput() {
594
595            var value = textInput.val();
596
597            if ((value === null || value === "") && allowEmpty) {
598                set(null);
599                move();
600                updateOriginalInput();
601            }
602            else {
603                var tiny = tinycolor(value);
604                if (tiny.isValid()) {
605                    set(tiny);
606                    move();
607                    updateOriginalInput();
608                }
609                else {
610                    textInput.addClass("sp-validation-error");
611                }
612            }
613        }
614
615        function toggle() {
616            if (visible) {
617                hide();
618            }
619            else {
620                show();
621            }
622        }
623
624        function show() {
625            var event = $.Event('beforeShow.spectrum');
626
627            if (visible) {
628                reflow();
629                return;
630            }
631
632            boundElement.trigger(event, [ get() ]);
633
634            if (callbacks.beforeShow(get()) === false || event.isDefaultPrevented()) {
635                return;
636            }
637
638            hideAll();
639            visible = true;
640
641            $(doc).on("keydown.spectrum", onkeydown);
642            $(doc).on("click.spectrum", clickout);
643            $(window).on("resize.spectrum", resize);
644            replacer.addClass("sp-active");
645            container.removeClass("sp-hidden");
646
647            reflow();
648            updateUI();
649
650            colorOnShow = get();
651
652            drawInitial();
653            callbacks.show(colorOnShow);
654            boundElement.trigger('show.spectrum', [ colorOnShow ]);
655        }
656
657        function onkeydown(e) {
658            // Close on ESC
659            if (e.keyCode === 27) {
660                hide();
661            }
662        }
663
664        function clickout(e) {
665            // Return on right click.
666            if (e.button == 2) { return; }
667
668            // If a drag event was happening during the mouseup, don't hide
669            // on click.
670            if (isDragging) { return; }
671
672            if (clickoutFiresChange) {
673                updateOriginalInput(true);
674            }
675            else {
676                revert();
677            }
678            hide();
679        }
680
681        function hide() {
682            // Return if hiding is unnecessary
683            if (!visible || flat) { return; }
684            visible = false;
685
686            $(doc).off("keydown.spectrum", onkeydown);
687            $(doc).off("click.spectrum", clickout);
688            $(window).off("resize.spectrum", resize);
689
690            replacer.removeClass("sp-active");
691            container.addClass("sp-hidden");
692
693            callbacks.hide(get());
694            boundElement.trigger('hide.spectrum', [ get() ]);
695        }
696
697        function revert() {
698            set(colorOnShow, true);
699            updateOriginalInput(true);
700        }
701
702        function set(color, ignoreFormatChange) {
703            if (tinycolor.equals(color, get())) {
704                // Update UI just in case a validation error needs
705                // to be cleared.
706                updateUI();
707                return;
708            }
709
710            var newColor, newHsv;
711            if (!color && allowEmpty) {
712                isEmpty = true;
713            } else {
714                isEmpty = false;
715                newColor = tinycolor(color);
716                newHsv = newColor.toHsv();
717
718                currentHue = (newHsv.h % 360) / 360;
719                currentSaturation = newHsv.s;
720                currentValue = newHsv.v;
721                currentAlpha = newHsv.a;
722            }
723            updateUI();
724
725            if (newColor && newColor.isValid() && !ignoreFormatChange) {
726                currentPreferredFormat = opts.preferredFormat || newColor.getFormat();
727            }
728        }
729
730        function get(opts) {
731            opts = opts || { };
732
733            if (allowEmpty && isEmpty) {
734                return null;
735            }
736
737            return tinycolor.fromRatio({
738                h: currentHue,
739                s: currentSaturation,
740                v: currentValue,
741                a: Math.round(currentAlpha * 1000) / 1000
742            }, { format: opts.format || currentPreferredFormat });
743        }
744
745        function isValid() {
746            return !textInput.hasClass("sp-validation-error");
747        }
748
749        function move() {
750            updateUI();
751
752            callbacks.move(get());
753            boundElement.trigger('move.spectrum', [ get() ]);
754        }
755
756        function updateUI() {
757
758            textInput.removeClass("sp-validation-error");
759
760            updateHelperLocations();
761
762            // Update dragger background color (gradients take care of saturation and value).
763            var flatColor = tinycolor.fromRatio({ h: currentHue, s: 1, v: 1 });
764            dragger.css("background-color", flatColor.toHexString());
765
766            // Get a format that alpha will be included in (hex and names ignore alpha)
767            var format = currentPreferredFormat;
768            if (currentAlpha < 1 && !(currentAlpha === 0 && format === "name")) {
769                if (format === "hex" || format === "hex3" || format === "hex6" || format === "name") {
770                    format = "rgb";
771                }
772            }
773
774            var realColor = get({ format: format }),
775                displayColor = '';
776
777             //reset background info for preview element
778            previewElement.removeClass("sp-clear-display");
779            previewElement.css('background-color', 'transparent');
780
781            if (!realColor && allowEmpty) {
782                // Update the replaced elements background with icon indicating no color selection
783                previewElement.addClass("sp-clear-display");
784            }
785            else {
786                var realHex = realColor.toHexString(),
787                    realRgb = realColor.toRgbString();
788
789                // Update the replaced elements background color (with actual selected color)
790                if (rgbaSupport || realColor.alpha === 1) {
791                    previewElement.css("background-color", realRgb);
792                }
793                else {
794                    previewElement.css("background-color", "transparent");
795                    previewElement.css("filter", realColor.toFilter());
796                }
797
798                if (opts.showAlpha) {
799                    var rgb = realColor.toRgb();
800                    rgb.a = 0;
801                    var realAlpha = tinycolor(rgb).toRgbString();
802                    var gradient = "linear-gradient(left, " + realAlpha + ", " + realHex + ")";
803
804                    if (IE) {
805                        alphaSliderInner.css("filter", tinycolor(realAlpha).toFilter({ gradientType: 1 }, realHex));
806                    }
807                    else {
808                        alphaSliderInner.css("background", "-webkit-" + gradient);
809                        alphaSliderInner.css("background", "-moz-" + gradient);
810                        alphaSliderInner.css("background", "-ms-" + gradient);
811                        // Use current syntax gradient on unprefixed property.
812                        alphaSliderInner.css("background",
813                            "linear-gradient(to right, " + realAlpha + ", " + realHex + ")");
814                    }
815                }
816
817                displayColor = realColor.toString(format);
818            }
819
820            // Update the text entry input as it changes happen
821            if (opts.showInput) {
822                textInput.val(displayColor);
823            }
824
825            if (opts.showPalette) {
826                drawPalette();
827            }
828
829            drawInitial();
830        }
831
832        function updateHelperLocations() {
833            var s = currentSaturation;
834            var v = currentValue;
835
836            if(allowEmpty && isEmpty) {
837                //if selected color is empty, hide the helpers
838                alphaSlideHelper.hide();
839                slideHelper.hide();
840                dragHelper.hide();
841            }
842            else {
843                //make sure helpers are visible
844                alphaSlideHelper.show();
845                slideHelper.show();
846                dragHelper.show();
847
848                // Where to show the little circle in that displays your current selected color
849                var dragX = s * dragWidth;
850                var dragY = dragHeight - (v * dragHeight);
851                dragX = Math.max(
852                    -dragHelperHeight,
853                    Math.min(dragWidth - dragHelperHeight, dragX - dragHelperHeight)
854                );
855                dragY = Math.max(
856                    -dragHelperHeight,
857                    Math.min(dragHeight - dragHelperHeight, dragY - dragHelperHeight)
858                );
859                dragHelper.css({
860                    "top": dragY + "px",
861                    "left": dragX + "px"
862                });
863
864                var alphaX = currentAlpha * alphaWidth;
865                alphaSlideHelper.css({
866                    "left": (alphaX - (alphaSlideHelperWidth / 2)) + "px"
867                });
868
869                // Where to show the bar that displays your current selected hue
870                var slideY = (currentHue) * slideHeight;
871                slideHelper.css({
872                    "top": (slideY - slideHelperHeight) + "px"
873                });
874            }
875        }
876
877        function updateOriginalInput(fireCallback) {
878            var color = get(),
879                displayColor = '',
880                hasChanged = !tinycolor.equals(color, colorOnShow);
881
882            if (color) {
883                displayColor = color.toString(currentPreferredFormat);
884                // Update the selection palette with the current color
885                addColorToSelectionPalette(color);
886            }
887
888            if (isInput) {
889                boundElement.val(displayColor);
890            }
891
892            if (fireCallback && hasChanged) {
893                callbacks.change(color);
894                boundElement.trigger('change', [ color ]);
895            }
896        }
897
898        function reflow() {
899            if (!visible) {
900                return; // Calculations would be useless and wouldn't be reliable anyways
901            }
902            dragWidth = dragger.width();
903            dragHeight = dragger.height();
904            dragHelperHeight = dragHelper.height();
905            slideWidth = slider.width();
906            slideHeight = slider.height();
907            slideHelperHeight = slideHelper.height();
908            alphaWidth = alphaSlider.width();
909            alphaSlideHelperWidth = alphaSlideHelper.width();
910
911            if (!flat) {
912                container.css("position", "absolute");
913                if (opts.offset) {
914                    container.offset(opts.offset);
915                } else {
916                    container.offset(getOffset(container, offsetElement));
917                }
918            }
919
920            updateHelperLocations();
921
922            if (opts.showPalette) {
923                drawPalette();
924            }
925
926            boundElement.trigger('reflow.spectrum');
927        }
928
929        function destroy() {
930            boundElement.show();
931            offsetElement.off("click.spectrum touchstart.spectrum");
932            container.remove();
933            replacer.remove();
934            spectrums[spect.id] = null;
935        }
936
937        function option(optionName, optionValue) {
938            if (optionName === undefined) {
939                return $.extend({}, opts);
940            }
941            if (optionValue === undefined) {
942                return opts[optionName];
943            }
944
945            opts[optionName] = optionValue;
946
947            if (optionName === "preferredFormat") {
948                currentPreferredFormat = opts.preferredFormat;
949            }
950            applyOptions();
951        }
952
953        function enable() {
954            disabled = false;
955            boundElement.attr("disabled", false);
956            offsetElement.removeClass("sp-disabled");
957        }
958
959        function disable() {
960            hide();
961            disabled = true;
962            boundElement.attr("disabled", true);
963            offsetElement.addClass("sp-disabled");
964        }
965
966        function setOffset(coord) {
967            opts.offset = coord;
968            reflow();
969        }
970
971        initialize();
972
973        var spect = {
974            show: show,
975            hide: hide,
976            toggle: toggle,
977            reflow: reflow,
978            option: option,
979            enable: enable,
980            disable: disable,
981            offset: setOffset,
982            set: function (c) {
983                set(c);
984                updateOriginalInput();
985            },
986            get: get,
987            destroy: destroy,
988            container: container
989        };
990
991        spect.id = spectrums.push(spect) - 1;
992
993        return spect;
994    }
995
996    /**
997    * checkOffset - get the offset below/above and left/right element depending on screen position
998    * Thanks https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js
999    */
1000    function getOffset(picker, input) {
1001        var extraY = 0;
1002        var dpWidth = picker.outerWidth();
1003        var dpHeight = picker.outerHeight();
1004        var inputHeight = input.outerHeight();
1005        var doc = picker[0].ownerDocument;
1006        var docElem = doc.documentElement;
1007        var viewWidth = docElem.clientWidth + $(doc).scrollLeft();
1008        var viewHeight = docElem.clientHeight + $(doc).scrollTop();
1009        var offset = input.offset();
1010        var offsetLeft = offset.left;
1011        var offsetTop = offset.top;
1012
1013        offsetTop += inputHeight;
1014
1015        offsetLeft -=
1016            Math.min(offsetLeft, (offsetLeft + dpWidth > viewWidth && viewWidth > dpWidth) ?
1017            Math.abs(offsetLeft + dpWidth - viewWidth) : 0);
1018
1019        offsetTop -=
1020            Math.min(offsetTop, ((offsetTop + dpHeight > viewHeight && viewHeight > dpHeight) ?
1021            Math.abs(dpHeight + inputHeight - extraY) : extraY));
vendor: 4,158 bytes, lines 1022-1155
1022
1023        return {
1024            top: offsetTop,
1025            bottom: offset.bottom,
1026            left: offsetLeft,
1027            right: offset.right,
1028            width: offset.width,
1029            height: offset.height
1030        };
1031    }
1032
1033    /**
1034    * noop - do nothing
1035    */
1036    function noop() {
1037
1038    }
1039
1040    /**
1041    * stopPropagation - makes the code only doing this a little easier to read in line
1042    */
1043    function stopPropagation(e) {
1044        e.stopPropagation();
1045    }
1046
1047    /**
1048    * Create a function bound to a given object
1049    * Thanks to underscore.js
1050    */
1051    function bind(func, obj) {
1052        var slice = Array.prototype.slice;
1053        var args = slice.call(arguments, 2);
1054        return function () {
1055            return func.apply(obj, args.concat(slice.call(arguments)));
1056        };
1057    }
1058
1059    /**
1060    * Lightweight drag helper.  Handles containment within the element, so that
1061    * when dragging, the x is within [0,element.width] and y is within [0,element.height]
1062    */
1063    function draggable(element, onmove, onstart, onstop) {
1064        onmove = onmove || function () { };
1065        onstart = onstart || function () { };
1066        onstop = onstop || function () { };
1067        var doc = document;
1068        var dragging = false;
1069        var offset = {};
1070        var maxHeight = 0;
1071        var maxWidth = 0;
1072        var hasTouch = ('ontouchstart' in window);
1073
1074        var duringDragEvents = {};
1075        duringDragEvents["selectstart"] = prevent;
1076        duringDragEvents["dragstart"] = prevent;
1077        duringDragEvents["touchmove mousemove"] = move;
1078        duringDragEvents["touchend mouseup"] = stop;
1079
1080        function prevent(e) {
1081            if (e.stopPropagation) {
1082                e.stopPropagation();
1083            }
1084            if (e.preventDefault) {
1085                e.preventDefault();
1086            }
1087            e.returnValue = false;
1088        }
1089
1090        function move(e) {
1091            if (dragging) {
1092                // Mouseup happened outside of window
1093                if (IE && doc.documentMode < 9 && !e.button) {
1094                    return stop();
1095                }
1096
1097                var t0 = e.originalEvent && e.originalEvent.touches && e.originalEvent.touches[0];
1098                var pageX = t0 && t0.pageX || e.pageX;
1099                var pageY = t0 && t0.pageY || e.pageY;
1100
1101                var dragX = Math.max(0, Math.min(pageX - offset.left, maxWidth));
1102                var dragY = Math.max(0, Math.min(pageY - offset.top, maxHeight));
1103
1104                if (hasTouch) {
1105                    // Stop scrolling in iOS
1106                    prevent(e);
1107                }
1108
1109                onmove.apply(element, [dragX, dragY, e]);
1110            }
1111        }
1112
1113        function start(e) {
1114            var rightclick = (e.which) ? (e.which == 3) : (e.button == 2);
1115
1116            if (!rightclick && !dragging) {
1117                if (onstart.apply(element, arguments) !== false) {
1118                    dragging = true;
1119                    maxHeight = $(element).height();
1120                    maxWidth = $(element).width();
1121                    offset = $(element).offset();
1122
1123                    $(doc).on(duringDragEvents);
1124                    $(doc.body).addClass("sp-dragging");
1125
1126                    move(e);
1127
1128                    prevent(e);
1129                }
1130            }
1131        }
1132
1133        function stop() {
1134            if (dragging) {
1135                $(doc).off(duringDragEvents);
1136                $(doc.body).removeClass("sp-dragging");
1137
1138                // Wait a tick before notifying observers to allow the click event
1139                // to fire in Chrome.
1140                setTimeout(function() {
1141                    onstop.apply(element, arguments);
1142                }, 0);
1143            }
1144            dragging = false;
1145        }
1146
1147        $(element).on("touchstart mousedown", start);
1148    }
1149
1150    function throttle(func, wait, debounce) {
1151        var timeout;
1152        return function () {
1153            var context = this, args = arguments;
1154            var throttler = function () {
1155                timeout = null;
vendor: 4,384 bytes, lines 1156-1283
1156                func.apply(context, args);
1157            };
1158            if (debounce) clearTimeout(timeout);
1159            if (debounce || !timeout) timeout = setTimeout(throttler, wait);
1160        };
1161    }
1162
1163    function inputTypeColorSupport() {
1164        return $.fn.spectrum.inputTypeColorSupport();
1165    }
1166
1167    /**
1168    * Define a jQuery plugin
1169    */
1170    var dataID = "spectrum.id";
1171    $.fn.spectrum = function (opts, extra) {
1172
1173        if (typeof opts == "string") {
1174
1175            var returnValue = this;
1176            var args = Array.prototype.slice.call( arguments, 1 );
1177
1178            this.each(function () {
1179                var spect = spectrums[$(this).data(dataID)];
1180                if (spect) {
1181                    var method = spect[opts];
1182                    if (!method) {
1183                        throw new Error( "Spectrum: no such method: '" + opts + "'" );
1184                    }
1185
1186                    if (opts == "get") {
1187                        returnValue = spect.get();
1188                    }
1189                    else if (opts == "container") {
1190                        returnValue = spect.container;
1191                    }
1192                    else if (opts == "option") {
1193                        returnValue = spect.option.apply(spect, args);
1194                    }
1195                    else if (opts == "destroy") {
1196                        spect.destroy();
1197                        $(this).removeData(dataID);
1198                    }
1199                    else {
1200                        method.apply(spect, args);
1201                    }
1202                }
1203            });
1204
1205            return returnValue;
1206        }
1207
1208        // Initializing a new instance of spectrum
1209        return this.spectrum("destroy").each(function () {
1210            var options = $.extend({}, $(this).data(), opts);
1211            var spect = spectrum(this, options);
1212            $(this).data(dataID, spect.id);
1213        });
1214    };
1215
1216    $.fn.spectrum.load = true;
1217    $.fn.spectrum.loadOpts = {};
1218    $.fn.spectrum.draggable = draggable;
1219    $.fn.spectrum.defaults = defaultOpts;
1220    $.fn.spectrum.inputTypeColorSupport = function inputTypeColorSupport() {
1221        if (typeof inputTypeColorSupport._cachedResult === "undefined") {
1222            var colorInput = $("<input type='color'/>")[0]; // if color element is supported, value will default to not null
1223            inputTypeColorSupport._cachedResult = colorInput.type === "color" && colorInput.value !== "";
1224        }
1225        return inputTypeColorSupport._cachedResult;
1226    };
1227
1228    $.spectrum = { };
1229    $.spectrum.localization = { };
1230    $.spectrum.palettes = { };
1231
1232    $.fn.spectrum.processNativeColorInputs = function () {
1233        var colorInputs = $("input[type=color]");
1234        if (colorInputs.length && !inputTypeColorSupport()) {
1235            colorInputs.spectrum({
1236                preferredFormat: "hex6"
1237            });
1238        }
1239    };
1240
1241    // TinyColor v1.1.2
1242    // https://github.com/bgrins/TinyColor
1243    // Brian Grinstead, MIT License
1244
1245    (function() {
1246
1247    var trimLeft = /^[\s,#]+/,
1248        trimRight = /\s+$/,
1249        tinyCounter = 0,
1250        math = Math,
1251        mathRound = math.round,
1252        mathMin = math.min,
1253        mathMax = math.max,
1254        mathRandom = math.random;
1255
1256    var tinycolor = function(color, opts) {
1257
1258        color = (color) ? color : '';
1259        opts = opts || { };
1260
1261        // If input is already a tinycolor, return itself
1262        if (color instanceof tinycolor) {
1263           return color;
1264        }
1265        // If we are called as a function, call using new instead
1266        if (!(this instanceof tinycolor)) {
1267            return new tinycolor(color, opts);
1268        }
1269
1270        var rgb = inputToRGB(color);
1271        this._originalInput = color,
1272        this._r = rgb.r,
1273        this._g = rgb.g,
1274        this._b = rgb.b,
1275        this._a = rgb.a,
1276        this._roundA = mathRound(1000 * this._a) / 1000,
1277        this._format = opts.format || rgb.format;
1278        this._gradientType = opts.gradientType;
1279
1280        // Don't let the range of [0,255] come back in [0,1].
1281        // Potentially lose a little bit of precision here, but will fix issues where
1282        // .5 gets interpreted as half of the total, instead of half of 1
1283        // If it was supposed to be 128, this was already taken care of b
vendor: 36,641 bytes, lines 1283-2350
1283y `inputToRgb`
1284        if (this._r < 1) { this._r = mathRound(this._r); }
1285        if (this._g < 1) { this._g = mathRound(this._g); }
1286        if (this._b < 1) { this._b = mathRound(this._b); }
1287
1288        this._ok = rgb.ok;
1289        this._tc_id = tinyCounter++;
1290    };
1291
1292    tinycolor.prototype = {
1293        isDark: function() {
1294            return this.getBrightness() < 128;
1295        },
1296        isLight: function() {
1297            return !this.isDark();
1298        },
1299        isValid: function() {
1300            return this._ok;
1301        },
1302        getOriginalInput: function() {
1303          return this._originalInput;
1304        },
1305        getFormat: function() {
1306            return this._format;
1307        },
1308        getAlpha: function() {
1309            return this._a;
1310        },
1311        getBrightness: function() {
1312            var rgb = this.toRgb();
1313            return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
1314        },
1315        setAlpha: function(value) {
1316            this._a = boundAlpha(value);
1317            this._roundA = mathRound(1000 * this._a) / 1000;
1318            return this;
1319        },
1320        toHsv: function() {
1321            var hsv = rgbToHsv(this._r, this._g, this._b);
1322            return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };
1323        },
1324        toHsvString: function() {
1325            var hsv = rgbToHsv(this._r, this._g, this._b);
1326            var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);
1327            return (this._a == 1) ?
1328              "hsv("  + h + ", " + s + "%, " + v + "%)" :
1329              "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")";
1330        },
1331        toHsl: function() {
1332            var hsl = rgbToHsl(this._r, this._g, this._b);
1333            return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };
1334        },
1335        toHslString: function() {
1336            var hsl = rgbToHsl(this._r, this._g, this._b);
1337            var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);
1338            return (this._a == 1) ?
1339              "hsl("  + h + ", " + s + "%, " + l + "%)" :
1340              "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")";
1341        },
1342        toHex: function(allow3Char) {
1343            return rgbToHex(this._r, this._g, this._b, allow3Char);
1344        },
1345        toHexString: function(allow3Char) {
1346            return '#' + this.toHex(allow3Char);
1347        },
1348        toHex8: function() {
1349            return rgbaToHex(this._r, this._g, this._b, this._a);
1350        },
1351        toHex8String: function() {
1352            return '#' + this.toHex8();
1353        },
1354        toRgb: function() {
1355            return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a };
1356        },
1357        toRgbString: function() {
1358            return (this._a == 1) ?
1359              "rgb("  + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" :
1360              "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")";
1361        },
1362        toPercentageRgb: function() {
1363            return { r: mathRound(bound01(this._r, 255) * 100) + "%", g: mathRound(bound01(this._g, 255) * 100) + "%", b: mathRound(bound01(this._b, 255) * 100) + "%", a: this._a };
1364        },
1365        toPercentageRgbString: function() {
1366            return (this._a == 1) ?
1367              "rgb("  + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" :
1368              "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")";
1369        },
1370        toName: function() {
1371            if (this._a === 0) {
1372                return "transparent";
1373            }
1374
1375            if (this._a < 1) {
1376                return false;
1377            }
1378
1379            return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false;
1380        },
1381        toFilter: function(secondColor) {
1382            var hex8String = '#' + rgbaToHex(this._r, this._g, this._b, this._a);
1383            var secondHex8String = hex8String;
1384            var gradientType = this._gradientType ? "GradientType = 1, " : "";
1385
1386            if (secondColor) {
1387                var s = tinycolor(secondColor);
1388                secondHex8String = s.toHex8String();
1389            }
1390
1391            return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")";
1392        },
1393        toString: function(format) {
1394            var formatSet = !!format;
1395            format = format || this._format;
1396
1397            var formattedString = false;
1398            var hasAlpha = this._a < 1 && this._a >= 0;
1399            var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "name");
1400
1401            if (needsAlphaFormat) {
1402                // Special case for "transparent", all other non-alpha formats
1403                // will return rgba when there is transparency.
1404                if (format === "name" && this._a === 0) {
1405                    return this.toName();
1406                }
1407                return this.toRgbString();
1408            }
1409            if (format === "rgb") {
1410                formattedString = this.toRgbString();
1411            }
1412            if (format === "prgb") {
1413                formattedString = this.toPercentageRgbString();
1414            }
1415            if (format === "hex" || format === "hex6") {
1416                formattedString = this.toHexString();
1417            }
1418            if (format === "hex3") {
1419                formattedString = this.toHexString(true);
1420            }
1421            if (format === "hex8") {
1422                formattedString = this.toHex8String();
1423            }
1424            if (format === "name") {
1425                formattedString = this.toName();
1426            }
1427            if (format === "hsl") {
1428                formattedString = this.toHslString();
1429            }
1430            if (format === "hsv") {
1431                formattedString = this.toHsvString();
1432            }
1433
1434            return formattedString || this.toHexString();
1435        },
1436
1437        _applyModification: function(fn, args) {
1438            var color = fn.apply(null, [this].concat([].slice.call(args)));
1439            this._r = color._r;
1440            this._g = color._g;
1441            this._b = color._b;
1442            this.setAlpha(color._a);
1443            return this;
1444        },
1445        lighten: function() {
1446            return this._applyModification(lighten, arguments);
1447        },
1448        brighten: function() {
1449            return this._applyModification(brighten, arguments);
1450        },
1451        darken: function() {
1452            return this._applyModification(darken, arguments);
1453        },
1454        desaturate: function() {
1455            return this._applyModification(desaturate, arguments);
1456        },
1457        saturate: function() {
1458            return this._applyModification(saturate, arguments);
1459        },
1460        greyscale: function() {
1461            return this._applyModification(greyscale, arguments);
1462        },
1463        spin: function() {
1464            return this._applyModification(spin, arguments);
1465        },
1466
1467        _applyCombination: function(fn, args) {
1468            return fn.apply(null, [this].concat([].slice.call(args)));
1469        },
1470        analogous: function() {
1471            return this._applyCombination(analogous, arguments);
1472        },
1473        complement: function() {
1474            return this._applyCombination(complement, arguments);
1475        },
1476        monochromatic: function() {
1477            return this._applyCombination(monochromatic, arguments);
1478        },
1479        splitcomplement: function() {
1480            return this._applyCombination(splitcomplement, arguments);
1481        },
1482        triad: function() {
1483            return this._applyCombination(triad, arguments);
1484        },
1485        tetrad: function() {
1486            return this._applyCombination(tetrad, arguments);
1487        }
1488    };
1489
1490    // If input is an object, force 1 into "1.0" to handle ratios properly
1491    // String input requires "1.0" as input, so 1 will be treated as 1
1492    tinycolor.fromRatio = function(color, opts) {
1493        if (typeof color == "object") {
1494            var newColor = {};
1495            for (var i in color) {
1496                if (color.hasOwnProperty(i)) {
1497                    if (i === "a") {
1498                        newColor[i] = color[i];
1499                    }
1500                    else {
1501                        newColor[i] = convertToPercentage(color[i]);
1502                    }
1503                }
1504            }
1505            color = newColor;
1506        }
1507
1508        return tinycolor(color, opts);
1509    };
1510
1511    // Given a string or object, convert that input to RGB
1512    // Possible string inputs:
1513    //
1514    //     "red"
1515    //     "#f00" or "f00"
1516    //     "#ff0000" or "ff0000"
1517    //     "#ff000000" or "ff000000"
1518    //     "rgb 255 0 0" or "rgb (255, 0, 0)"
1519    //     "rgb 1.0 0 0" or "rgb (1, 0, 0)"
1520    //     "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
1521    //     "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
1522    //     "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
1523    //     "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
1524    //     "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
1525    //
1526    function inputToRGB(color) {
1527
1528        var rgb = { r: 0, g: 0, b: 0 };
1529        var a = 1;
1530        var ok = false;
1531        var format = false;
1532
1533        if (typeof color == "string") {
1534            color = stringInputToObject(color);
1535        }
1536
1537        if (typeof color == "object") {
1538            if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) {
1539                rgb = rgbToRgb(color.r, color.g, color.b);
1540                ok = true;
1541                format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb";
1542            }
1543            else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("v")) {
1544                color.s = convertToPercentage(color.s);
1545                color.v = convertToPercentage(color.v);
1546                rgb = hsvToRgb(color.h, color.s, color.v);
1547                ok = true;
1548                format = "hsv";
1549            }
1550            else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("l")) {
1551                color.s = convertToPercentage(color.s);
1552                color.l = convertToPercentage(color.l);
1553                rgb = hslToRgb(color.h, color.s, color.l);
1554                ok = true;
1555                format = "hsl";
1556            }
1557
1558            if (color.hasOwnProperty("a")) {
1559                a = color.a;
1560            }
1561        }
1562
1563        a = boundAlpha(a);
1564
1565        return {
1566            ok: ok,
1567            format: color.format || format,
1568            r: mathMin(255, mathMax(rgb.r, 0)),
1569            g: mathMin(255, mathMax(rgb.g, 0)),
1570            b: mathMin(255, mathMax(rgb.b, 0)),
1571            a: a
1572        };
1573    }
1574
1575
1576    // Conversion Functions
1577    // --------------------
1578
1579    // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:
1580    // <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>
1581
1582    // `rgbToRgb`
1583    // Handle bounds / percentage checking to conform to CSS color spec
1584    // <http://www.w3.org/TR/css3-color/>
1585    // *Assumes:* r, g, b in [0, 255] or [0, 1]
1586    // *Returns:* { r, g, b } in [0, 255]
1587    function rgbToRgb(r, g, b){
1588        return {
1589            r: bound01(r, 255) * 255,
1590            g: bound01(g, 255) * 255,
1591            b: bound01(b, 255) * 255
1592        };
1593    }
1594
1595    // `rgbToHsl`
1596    // Converts an RGB color value to HSL.
1597    // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]
1598    // *Returns:* { h, s, l } in [0,1]
1599    function rgbToHsl(r, g, b) {
1600
1601        r = bound01(r, 255);
1602        g = bound01(g, 255);
1603        b = bound01(b, 255);
1604
1605        var max = mathMax(r, g, b), min = mathMin(r, g, b);
1606        var h, s, l = (max + min) / 2;
1607
1608        if(max == min) {
1609            h = s = 0; // achromatic
1610        }
1611        else {
1612            var d = max - min;
1613            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
1614            switch(max) {
1615                case r: h = (g - b) / d + (g < b ? 6 : 0); break;
1616                case g: h = (b - r) / d + 2; break;
1617                case b: h = (r - g) / d + 4; break;
1618            }
1619
1620            h /= 6;
1621        }
1622
1623        return { h: h, s: s, l: l };
1624    }
1625
1626    // `hslToRgb`
1627    // Converts an HSL color value to RGB.
1628    // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]
1629    // *Returns:* { r, g, b } in the set [0, 255]
1630    function hslToRgb(h, s, l) {
1631        var r, g, b;
1632
1633        h = bound01(h, 360);
1634        s = bound01(s, 100);
1635        l = bound01(l, 100);
1636
1637        function hue2rgb(p, q, t) {
1638            if(t < 0) t += 1;
1639            if(t > 1) t -= 1;
1640            if(t < 1/6) return p + (q - p) * 6 * t;
1641            if(t < 1/2) return q;
1642            if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
1643            return p;
1644        }
1645
1646        if(s === 0) {
1647            r = g = b = l; // achromatic
1648        }
1649        else {
1650            var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
1651            var p = 2 * l - q;
1652            r = hue2rgb(p, q, h + 1/3);
1653            g = hue2rgb(p, q, h);
1654            b = hue2rgb(p, q, h - 1/3);
1655        }
1656
1657        return { r: r * 255, g: g * 255, b: b * 255 };
1658    }
1659
1660    // `rgbToHsv`
1661    // Converts an RGB color value to HSV
1662    // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
1663    // *Returns:* { h, s, v } in [0,1]
1664    function rgbToHsv(r, g, b) {
1665
1666        r = bound01(r, 255);
1667        g = bound01(g, 255);
1668        b = bound01(b, 255);
1669
1670        var max = mathMax(r, g, b), min = mathMin(r, g, b);
1671        var h, s, v = max;
1672
1673        var d = max - min;
1674        s = max === 0 ? 0 : d / max;
1675
1676        if(max == min) {
1677            h = 0; // achromatic
1678        }
1679        else {
1680            switch(max) {
1681                case r: h = (g - b) / d + (g < b ? 6 : 0); break;
1682                case g: h = (b - r) / d + 2; break;
1683                case b: h = (r - g) / d + 4; break;
1684            }
1685            h /= 6;
1686        }
1687        return { h: h, s: s, v: v };
1688    }
1689
1690    // `hsvToRgb`
1691    // Converts an HSV color value to RGB.
1692    // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
1693    // *Returns:* { r, g, b } in the set [0, 255]
1694     function hsvToRgb(h, s, v) {
1695
1696        h = bound01(h, 360) * 6;
1697        s = bound01(s, 100);
1698        v = bound01(v, 100);
1699
1700        var i = math.floor(h),
1701            f = h - i,
1702            p = v * (1 - s),
1703            q = v * (1 - f * s),
1704            t = v * (1 - (1 - f) * s),
1705            mod = i % 6,
1706            r = [v, q, p, p, t, v][mod],
1707            g = [t, v, v, q, p, p][mod],
1708            b = [p, p, t, v, v, q][mod];
1709
1710        return { r: r * 255, g: g * 255, b: b * 255 };
1711    }
1712
1713    // `rgbToHex`
1714    // Converts an RGB color to hex
1715    // Assumes r, g, and b are contained in the set [0, 255]
1716    // Returns a 3 or 6 character hex
1717    function rgbToHex(r, g, b, allow3Char) {
1718
1719        var hex = [
1720            pad2(mathRound(r).toString(16)),
1721            pad2(mathRound(g).toString(16)),
1722            pad2(mathRound(b).toString(16))
1723        ];
1724
1725        // Return a 3 character hex if possible
1726        if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) {
1727            return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
1728        }
1729
1730        return hex.join("");
1731    }
1732        // `rgbaToHex`
1733        // Converts an RGBA color plus alpha transparency to hex
1734        // Assumes r, g, b and a are contained in the set [0, 255]
1735        // Returns an 8 character hex
1736        function rgbaToHex(r, g, b, a) {
1737
1738            var hex = [
1739                pad2(convertDecimalToHex(a)),
1740                pad2(mathRound(r).toString(16)),
1741                pad2(mathRound(g).toString(16)),
1742                pad2(mathRound(b).toString(16))
1743            ];
1744
1745            return hex.join("");
1746        }
1747
1748    // `equals`
1749    // Can be called with any tinycolor input
1750    tinycolor.equals = function (color1, color2) {
1751        if (!color1 || !color2) { return false; }
1752        return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();
1753    };
1754    tinycolor.random = function() {
1755        return tinycolor.fromRatio({
1756            r: mathRandom(),
1757            g: mathRandom(),
1758            b: mathRandom()
1759        });
1760    };
1761
1762
1763    // Modification Functions
1764    // ----------------------
1765    // Thanks to less.js for some of the basics here
1766    // <https://github.com/cloudhead/less.js/blob/master/lib/less/functions.js>
1767
1768    function desaturate(color, amount) {
1769        amount = (amount === 0) ? 0 : (amount || 10);
1770        var hsl = tinycolor(color).toHsl();
1771        hsl.s -= amount / 100;
1772        hsl.s = clamp01(hsl.s);
1773        return tinycolor(hsl);
1774    }
1775
1776    function saturate(color, amount) {
1777        amount = (amount === 0) ? 0 : (amount || 10);
1778        var hsl = tinycolor(color).toHsl();
1779        hsl.s += amount / 100;
1780        hsl.s = clamp01(hsl.s);
1781        return tinycolor(hsl);
1782    }
1783
1784    function greyscale(color) {
1785        return tinycolor(color).desaturate(100);
1786    }
1787
1788    function lighten (color, amount) {
1789        amount = (amount === 0) ? 0 : (amount || 10);
1790        var hsl = tinycolor(color).toHsl();
1791        hsl.l += amount / 100;
1792        hsl.l = clamp01(hsl.l);
1793        return tinycolor(hsl);
1794    }
1795
1796    function brighten(color, amount) {
1797        amount = (amount === 0) ? 0 : (amount || 10);
1798        var rgb = tinycolor(color).toRgb();
1799        rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100))));
1800        rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100))));
1801        rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100))));
1802        return tinycolor(rgb);
1803    }
1804
1805    function darken (color, amount) {
1806        amount = (amount === 0) ? 0 : (amount || 10);
1807        var hsl = tinycolor(color).toHsl();
1808        hsl.l -= amount / 100;
1809        hsl.l = clamp01(hsl.l);
1810        return tinycolor(hsl);
1811    }
1812
1813    // Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.
1814    // Values outside of this range will be wrapped into this range.
1815    function spin(color, amount) {
1816        var hsl = tinycolor(color).toHsl();
1817        var hue = (mathRound(hsl.h) + amount) % 360;
1818        hsl.h = hue < 0 ? 360 + hue : hue;
1819        return tinycolor(hsl);
1820    }
1821
1822    // Combination Functions
1823    // ---------------------
1824    // Thanks to jQuery xColor for some of the ideas behind these
1825    // <https://github.com/infusion/jQuery-xcolor/blob/master/jquery.xcolor.js>
1826
1827    function complement(color) {
1828        var hsl = tinycolor(color).toHsl();
1829        hsl.h = (hsl.h + 180) % 360;
1830        return tinycolor(hsl);
1831    }
1832
1833    function triad(color) {
1834        var hsl = tinycolor(color).toHsl();
1835        var h = hsl.h;
1836        return [
1837            tinycolor(color),
1838            tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),
1839            tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })
1840        ];
1841    }
1842
1843    function tetrad(color) {
1844        var hsl = tinycolor(color).toHsl();
1845        var h = hsl.h;
1846        return [
1847            tinycolor(color),
1848            tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),
1849            tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),
1850            tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })
1851        ];
1852    }
1853
1854    function splitcomplement(color) {
1855        var hsl = tinycolor(color).toHsl();
1856        var h = hsl.h;
1857        return [
1858            tinycolor(color),
1859            tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}),
1860            tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l})
1861        ];
1862    }
1863
1864    function analogous(color, results, slices) {
1865        results = results || 6;
1866        slices = slices || 30;
1867
1868        var hsl = tinycolor(color).toHsl();
1869        var part = 360 / slices;
1870        var ret = [tinycolor(color)];
1871
1872        for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {
1873            hsl.h = (hsl.h + part) % 360;
1874            ret.push(tinycolor(hsl));
1875        }
1876        return ret;
1877    }
1878
1879    function monochromatic(color, results) {
1880        results = results || 6;
1881        var hsv = tinycolor(color).toHsv();
1882        var h = hsv.h, s = hsv.s, v = hsv.v;
1883        var ret = [];
1884        var modification = 1 / results;
1885
1886        while (results--) {
1887            ret.push(tinycolor({ h: h, s: s, v: v}));
1888            v = (v + modification) % 1;
1889        }
1890
1891        return ret;
1892    }
1893
1894    // Utility Functions
1895    // ---------------------
1896
1897    tinycolor.mix = function(color1, color2, amount) {
1898        amount = (amount === 0) ? 0 : (amount || 50);
1899
1900        var rgb1 = tinycolor(color1).toRgb();
1901        var rgb2 = tinycolor(color2).toRgb();
1902
1903        var p = amount / 100;
1904        var w = p * 2 - 1;
1905        var a = rgb2.a - rgb1.a;
1906
1907        var w1;
1908
1909        if (w * a == -1) {
1910            w1 = w;
1911        } else {
1912            w1 = (w + a) / (1 + w * a);
1913        }
1914
1915        w1 = (w1 + 1) / 2;
1916
1917        var w2 = 1 - w1;
1918
1919        var rgba = {
1920            r: rgb2.r * w1 + rgb1.r * w2,
1921            g: rgb2.g * w1 + rgb1.g * w2,
1922            b: rgb2.b * w1 + rgb1.b * w2,
1923            a: rgb2.a * p  + rgb1.a * (1 - p)
1924        };
1925
1926        return tinycolor(rgba);
1927    };
1928
1929
1930    // Readability Functions
1931    // ---------------------
1932    // <http://www.w3.org/TR/AERT#color-contrast>
1933
1934    // `readability`
1935    // Analyze the 2 colors and returns an object with the following properties:
1936    //    `brightness`: difference in brightness between the two colors
1937    //    `color`: difference in color/hue between the two colors
1938    tinycolor.readability = function(color1, color2) {
1939        var c1 = tinycolor(color1);
1940        var c2 = tinycolor(color2);
1941        var rgb1 = c1.toRgb();
1942        var rgb2 = c2.toRgb();
1943        var brightnessA = c1.getBrightness();
1944        var brightnessB = c2.getBrightness();
1945        var colorDiff = (
1946            Math.max(rgb1.r, rgb2.r) - Math.min(rgb1.r, rgb2.r) +
1947            Math.max(rgb1.g, rgb2.g) - Math.min(rgb1.g, rgb2.g) +
1948            Math.max(rgb1.b, rgb2.b) - Math.min(rgb1.b, rgb2.b)
1949        );
1950
1951        return {
1952            brightness: Math.abs(brightnessA - brightnessB),
1953            color: colorDiff
1954        };
1955    };
1956
1957    // `readable`
1958    // http://www.w3.org/TR/AERT#color-contrast
1959    // Ensure that foreground and background color combinations provide sufficient contrast.
1960    // *Example*
1961    //    tinycolor.isReadable("#000", "#111") => false
1962    tinycolor.isReadable = function(color1, color2) {
1963        var readability = tinycolor.readability(color1, color2);
1964        return readability.brightness > 125 && readability.color > 500;
1965    };
1966
1967    // `mostReadable`
1968    // Given a base color and a list of possible foreground or background
1969    // colors for that base, returns the most readable color.
1970    // *Example*
1971    //    tinycolor.mostReadable("#123", ["#fff", "#000"]) => "#000"
1972    tinycolor.mostReadable = function(baseColor, colorList) {
1973        var bestColor = null;
1974        var bestScore = 0;
1975        var bestIsReadable = false;
1976        for (var i=0; i < colorList.length; i++) {
1977
1978            // We normalize both around the "acceptable" breaking point,
1979            // but rank brightness constrast higher than hue.
1980
1981            var readability = tinycolor.readability(baseColor, colorList[i]);
1982            var readable = readability.brightness > 125 && readability.color > 500;
1983            var score = 3 * (readability.brightness / 125) + (readability.color / 500);
1984
1985            if ((readable && ! bestIsReadable) ||
1986                (readable && bestIsReadable && score > bestScore) ||
1987                ((! readable) && (! bestIsReadable) && score > bestScore)) {
1988                bestIsReadable = readable;
1989                bestScore = score;
1990                bestColor = tinycolor(colorList[i]);
1991            }
1992        }
1993        return bestColor;
1994    };
1995
1996
1997    // Big List of Colors
1998    // ------------------
1999    // <http://www.w3.org/TR/css3-color/#svg-color>
2000    var names = tinycolor.names = {
2001        aliceblue: "f0f8ff",
2002        antiquewhite: "faebd7",
2003        aqua: "0ff",
2004        aquamarine: "7fffd4",
2005        azure: "f0ffff",
2006        beige: "f5f5dc",
2007        bisque: "ffe4c4",
2008        black: "000",
2009        blanchedalmond: "ffebcd",
2010        blue: "00f",
2011        blueviolet: "8a2be2",
2012        brown: "a52a2a",
2013        burlywood: "deb887",
2014        burntsienna: "ea7e5d",
2015        cadetblue: "5f9ea0",
2016        chartreuse: "7fff00",
2017        chocolate: "d2691e",
2018        coral: "ff7f50",
2019        cornflowerblue: "6495ed",
2020        cornsilk: "fff8dc",
2021        crimson: "dc143c",
2022        cyan: "0ff",
2023        darkblue: "00008b",
2024        darkcyan: "008b8b",
2025        darkgoldenrod: "b8860b",
2026        darkgray: "a9a9a9",
2027        darkgreen: "006400",
2028        darkgrey: "a9a9a9",
2029        darkkhaki: "bdb76b",
2030        darkmagenta: "8b008b",
2031        darkolivegreen: "556b2f",
2032        darkorange: "ff8c00",
2033        darkorchid: "9932cc",
2034        darkred: "8b0000",
2035        darksalmon: "e9967a",
2036        darkseagreen: "8fbc8f",
2037        darkslateblue: "483d8b",
2038        darkslategray: "2f4f4f",
2039        darkslategrey: "2f4f4f",
2040        darkturquoise: "00ced1",
2041        darkviolet: "9400d3",
2042        deeppink: "ff1493",
2043        deepskyblue: "00bfff",
2044        dimgray: "696969",
2045        dimgrey: "696969",
2046        dodgerblue: "1e90ff",
2047        firebrick: "b22222",
2048        floralwhite: "fffaf0",
2049        forestgreen: "228b22",
2050        fuchsia: "f0f",
2051        gainsboro: "dcdcdc",
2052        ghostwhite: "f8f8ff",
2053        gold: "ffd700",
2054        goldenrod: "daa520",
2055        gray: "808080",
2056        green: "008000",
2057        greenyellow: "adff2f",
2058        grey: "808080",
2059        honeydew: "f0fff0",
2060        hotpink: "ff69b4",
2061        indianred: "cd5c5c",
2062        indigo: "4b0082",
2063        ivory: "fffff0",
2064        khaki: "f0e68c",
2065        lavender: "e6e6fa",
2066        lavenderblush: "fff0f5",
2067        lawngreen: "7cfc00",
2068        lemonchiffon: "fffacd",
2069        lightblue: "add8e6",
2070        lightcoral: "f08080",
2071        lightcyan: "e0ffff",
2072        lightgoldenrodyellow: "fafad2",
2073        lightgray: "d3d3d3",
2074        lightgreen: "90ee90",
2075        lightgrey: "d3d3d3",
2076        lightpink: "ffb6c1",
2077        lightsalmon: "ffa07a",
2078        lightseagreen: "20b2aa",
2079        lightskyblue: "87cefa",
2080        lightslategray: "789",
2081        lightslategrey: "789",
2082        lightsteelblue: "b0c4de",
2083        lightyellow: "ffffe0",
2084        lime: "0f0",
2085        limegreen: "32cd32",
2086        linen: "faf0e6",
2087        magenta: "f0f",
2088        maroon: "800000",
2089        mediumaquamarine: "66cdaa",
2090        mediumblue: "0000cd",
2091        mediumorchid: "ba55d3",
2092        mediumpurple: "9370db",
2093        mediumseagreen: "3cb371",
2094        mediumslateblue: "7b68ee",
2095        mediumspringgreen: "00fa9a",
2096        mediumturquoise: "48d1cc",
2097        mediumvioletred: "c71585",
2098        midnightblue: "191970",
2099        mintcream: "f5fffa",
2100        mistyrose: "ffe4e1",
2101        moccasin: "ffe4b5",
2102        navajowhite: "ffdead",
2103        navy: "000080",
2104        oldlace: "fdf5e6",
2105        olive: "808000",
2106        olivedrab: "6b8e23",
2107        orange: "ffa500",
2108        orangered: "ff4500",
2109        orchid: "da70d6",
2110        palegoldenrod: "eee8aa",
2111        palegreen: "98fb98",
2112        paleturquoise: "afeeee",
2113        palevioletred: "db7093",
2114        papayawhip: "ffefd5",
2115        peachpuff: "ffdab9",
2116        peru: "cd853f",
2117        pink: "ffc0cb",
2118        plum: "dda0dd",
2119        powderblue: "b0e0e6",
2120        purple: "800080",
2121        rebeccapurple: "663399",
2122        red: "f00",
2123        rosybrown: "bc8f8f",
2124        royalblue: "4169e1",
2125        saddlebrown: "8b4513",
2126        salmon: "fa8072",
2127        sandybrown: "f4a460",
2128        seagreen: "2e8b57",
2129        seashell: "fff5ee",
2130        sienna: "a0522d",
2131        silver: "c0c0c0",
2132        skyblue: "87ceeb",
2133        slateblue: "6a5acd",
2134        slategray: "708090",
2135        slategrey: "708090",
2136        snow: "fffafa",
2137        springgreen: "00ff7f",
2138        steelblue: "4682b4",
2139        tan: "d2b48c",
2140        teal: "008080",
2141        thistle: "d8bfd8",
2142        tomato: "ff6347",
2143        turquoise: "40e0d0",
2144        violet: "ee82ee",
2145        wheat: "f5deb3",
2146        white: "fff",
2147        whitesmoke: "f5f5f5",
2148        yellow: "ff0",
2149        yellowgreen: "9acd32"
2150    };
2151
2152    // Make it easy to access colors via `hexNames[hex]`
2153    var hexNames = tinycolor.hexNames = flip(names);
2154
2155
2156    // Utilities
2157    // ---------
2158
2159    // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`
2160    function flip(o) {
2161        var flipped = { };
2162        for (var i in o) {
2163            if (o.hasOwnProperty(i)) {
2164                flipped[o[i]] = i;
2165            }
2166        }
2167        return flipped;
2168    }
2169
2170    // Return a valid alpha value [0,1] with all invalid values being set to 1
2171    function boundAlpha(a) {
2172        a = parseFloat(a);
2173
2174        if (isNaN(a) || a < 0 || a > 1) {
2175            a = 1;
2176        }
2177
2178        return a;
2179    }
2180
2181    // Take input from [0, n] and return it as [0, 1]
2182    function bound01(n, max) {
2183        if (isOnePointZero(n)) { n = "100%"; }
2184
2185        var processPercent = isPercentage(n);
2186        n = mathMin(max, mathMax(0, parseFloat(n)));
2187
2188        // Automatically convert percentage into number
2189        if (processPercent) {
2190            n = parseInt(n * max, 10) / 100;
2191        }
2192
2193        // Handle floating point rounding errors
2194        if ((math.abs(n - max) < 0.000001)) {
2195            return 1;
2196        }
2197
2198        // Convert into [0, 1] range if it isn't already
2199        return (n % max) / parseFloat(max);
2200    }
2201
2202    // Force a number between 0 and 1
2203    function clamp01(val) {
2204        return mathMin(1, mathMax(0, val));
2205    }
2206
2207    // Parse a base-16 hex value into a base-10 integer
2208    function parseIntFromHex(val) {
2209        return parseInt(val, 16);
2210    }
2211
2212    // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
2213    // <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
2214    function isOnePointZero(n) {
2215        return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1;
2216    }
2217
2218    // Check to see if string passed in is a percentage
2219    function isPercentage(n) {
2220        return typeof n === "string" && n.indexOf('%') != -1;
2221    }
2222
2223    // Force a hex value to have 2 characters
2224    function pad2(c) {
2225        return c.length == 1 ? '0' + c : '' + c;
2226    }
2227
2228    // Replace a decimal with it's percentage value
2229    function convertToPercentage(n) {
2230        if (n <= 1) {
2231            n = (n * 100) + "%";
2232        }
2233
2234        return n;
2235    }
2236
2237    // Converts a decimal to a hex value
2238    function convertDecimalToHex(d) {
2239        return Math.round(parseFloat(d) * 255).toString(16);
2240    }
2241    // Converts a hex value to a decimal
2242    function convertHexToDecimal(h) {
2243        return (parseIntFromHex(h) / 255);
2244    }
2245
2246    var matchers = (function() {
2247
2248        // <http://www.w3.org/TR/css3-values/#integers>
2249        var CSS_INTEGER = "[-\\+]?\\d+%?";
2250
2251        // <http://www.w3.org/TR/css3-values/#number-value>
2252        var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?";
2253
2254        // Allow positive/negative integer/number.  Don't capture the either/or, just the entire outcome.
2255        var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")";
2256
2257        // Actual matching.
2258        // Parentheses and commas are optional, but not required.
2259        // Whitespace can take the place of commas or opening paren
2260        var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
2261        var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
2262
2263        return {
2264            rgb: new RegExp("rgb" + PERMISSIVE_MATCH3),
2265            rgba: new RegExp("rgba" + PERMISSIVE_MATCH4),
2266            hsl: new RegExp("hsl" + PERMISSIVE_MATCH3),
2267            hsla: new RegExp("hsla" + PERMISSIVE_MATCH4),
2268            hsv: new RegExp("hsv" + PERMISSIVE_MATCH3),
2269            hsva: new RegExp("hsva" + PERMISSIVE_MATCH4),
2270            hex3: /^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
2271            hex6: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
2272            hex8: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/
2273        };
2274    })();
2275
2276    // `stringInputToObject`
2277    // Permissive string parsing.  Take in a number of formats, and output an object
2278    // based on detected format.  Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`
2279    function stringInputToObject(color) {
2280
2281        color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase();
2282        var named = false;
2283        if (names[color]) {
2284            color = names[color];
2285            named = true;
2286        }
2287        else if (color == 'transparent') {
2288            return { r: 0, g: 0, b: 0, a: 0, format: "name" };
2289        }
2290
2291        // Try to match string input using regular expressions.
2292        // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]
2293        // Just return an object and let the conversion functions handle that.
2294        // This way the result will be the same whether the tinycolor is initialized with string or object.
2295        var match;
2296        if ((match = matchers.rgb.exec(color))) {
2297            return { r: match[1], g: match[2], b: match[3] };
2298        }
2299        if ((match = matchers.rgba.exec(color))) {
2300            return { r: match[1], g: match[2], b: match[3], a: match[4] };
2301        }
2302        if ((match = matchers.hsl.exec(color))) {
2303            return { h: match[1], s: match[2], l: match[3] };
2304        }
2305        if ((match = matchers.hsla.exec(color))) {
2306            return { h: match[1], s: match[2], l: match[3], a: match[4] };
2307        }
2308        if ((match = matchers.hsv.exec(color))) {
2309            return { h: match[1], s: match[2], v: match[3] };
2310        }
2311        if ((match = matchers.hsva.exec(color))) {
2312            return { h: match[1], s: match[2], v: match[3], a: match[4] };
2313        }
2314        if ((match = matchers.hex8.exec(color))) {
2315            return {
2316                a: convertHexToDecimal(match[1]),
2317                r: parseIntFromHex(match[2]),
2318                g: parseIntFromHex(match[3]),
2319                b: parseIntFromHex(match[4]),
2320                format: named ? "name" : "hex8"
2321            };
2322        }
2323        if ((match = matchers.hex6.exec(color))) {
2324            return {
2325                r: parseIntFromHex(match[1]),
2326                g: parseIntFromHex(match[2]),
2327                b: parseIntFromHex(match[3]),
2328                format: named ? "name" : "hex"
2329            };
2330        }
2331        if ((match = matchers.hex3.exec(color))) {
2332            return {
2333                r: parseIntFromHex(match[1] + '' + match[1]),
2334                g: parseIntFromHex(match[2] + '' + match[2]),
2335                b: parseIntFromHex(match[3] + '' + match[3]),
2336                format: named ? "name" : "hex"
2337            };
2338        }
2339
2340        return false;
2341    }
2342
2343    window.tinycolor = tinycolor;
2344    })();
2345
2346    $(function () {
2347        if ($.fn.spectrum.load) {
2348            $.fn.spectrum.processNativeColorInputs();
2349        }
2350    });
2351
2352});

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.