From 0a33a0a59fdf428c968f48fb53b6f9e87c9160b3 Mon Sep 17 00:00:00 2001 From: Joan Gimenez Date: Fri, 23 Nov 2018 13:15:02 +0100 Subject: [PATCH] psr2 fixes --- spectrum.js | 2359 ++++++++++++++++++++++++++------------------------- 1 file changed, 1205 insertions(+), 1154 deletions(-) diff --git a/spectrum.js b/spectrum.js index e129e184..b80bc8fb 100644 --- a/spectrum.js +++ b/spectrum.js @@ -8,14 +8,12 @@ if (typeof define === 'function' && define.amd) { // AMD define(['jquery'], factory); - } - else if (typeof exports == "object" && typeof module == "object") { // CommonJS + } else if (typeof exports == "object" && typeof module == "object") { // CommonJS module.exports = factory(require('jquery')); - } - else { // Browser + } else { // Browser factory(jQuery); } -})(function($, undefined) { +})(function ($, undefined) { "use strict"; var defaultOpts = { @@ -61,9 +59,10 @@ offset: null }, spectrums = [], - IE = !!/msie/i.exec( window.navigator.userAgent ), - rgbaSupport = (function() { - function contains( str, substr ) { + IE = !!/msie/i.exec(window.navigator.userAgent), + rgbaSupport = (function () { + function contains(str, substr) + { return !!~('' + str).indexOf(substr); } @@ -130,11 +129,12 @@ ].join(""); })(); - function paletteTemplate (p, color, className, opts) { + function paletteTemplate(p, color, className, opts) + { var html = []; for (var i = 0; i < p.length; i++) { var current = p[i]; - if(current) { + if (current) { var tiny = tinycolor(current); var c = tiny.toHsl().l < 0.5 ? "sp-thumb-el sp-thumb-dark" : "sp-thumb-el sp-thumb-light"; c += (tinycolor.equals(color, current)) ? " sp-thumb-active" : ""; @@ -143,18 +143,20 @@ html.push(''); } else { var cls = 'sp-clear-display'; - html.push($('
') - .append($('') - .attr('title', opts.noColorSelectedText) - ) - .html() + html.push( + $('
'). + append( + $(''). + attr('title', opts.noColorSelectedText) + ).html() ); } } return "
" + html.join('') + "
"; } - function hideAll() { + function hideAll() + { for (var i = 0; i < spectrums.length; i++) { if (spectrums[i]) { spectrums[i].hide(); @@ -162,7 +164,8 @@ } } - function instanceOptions(o, callbackContext) { + function instanceOptions(o, callbackContext) + { var opts = $.extend({}, defaultOpts, o); opts.callbacks = { 'move': bind(opts.move, callbackContext), @@ -175,8 +178,8 @@ return opts; } - function spectrum(element, o) { - + function spectrum(element, o) + { var opts = instanceOptions(o, element), flat = opts.flat, showSelectionPalette = opts.showSelectionPalette, @@ -239,8 +242,8 @@ isEmpty = !initialColor, allowEmpty = opts.allowEmpty && !isInputTypeColor; - function applyOptions() { - + function applyOptions() + { if (opts.showPaletteOnly) { opts.showPalette = true; } @@ -273,8 +276,8 @@ reflow(); } - function initialize() { - + function initialize() + { if (IE) { container.find("*:not(input)").attr("unselectable", "on"); } @@ -291,9 +294,7 @@ if (flat) { boundElement.after(container).hide(); - } - else { - + } else { var appendTo = opts.appendTo === "parent" ? boundElement.parent() : $(opts.appendTo); if (appendTo.length !== 1) { appendTo = $("body"); @@ -304,7 +305,7 @@ updateSelectionPaletteFromStorage(); - offsetElement.on("click.spectrum touchstart.spectrum", function (e) { + offsetElement.bind("click.spectrum touchstart.spectrum", function (e) { if (!disabled) { toggle(); } @@ -316,7 +317,7 @@ } }); - if(boundElement.is(":disabled") || (opts.disabled === true)) { + if (boundElement.is(":disabled") || (opts.disabled === true)) { disable(); } @@ -325,13 +326,17 @@ // Handle user typed input textInput.change(setFromTextInput); - textInput.on("paste", function () { + textInput.bind("paste", function () { setTimeout(setFromTextInput, 1); }); - textInput.keydown(function (e) { if (e.keyCode == 13) { setFromTextInput(); } }); + textInput.keydown(function (e) { + if (e.keyCode == 13) { + setFromTextInput(); + } + }); cancelButton.text(opts.cancelText); - cancelButton.on("click.spectrum", function (e) { + cancelButton.bind("click.spectrum", function (e) { e.stopPropagation(); e.preventDefault(); revert(); @@ -339,20 +344,20 @@ }); clearButton.attr("title", opts.clearText); - clearButton.on("click.spectrum", function (e) { + clearButton.bind("click.spectrum", function (e) { e.stopPropagation(); e.preventDefault(); isEmpty = true; move(); - if(flat) { + if (flat) { //for the flat style, this is a change event updateOriginalInput(true); } }); chooseButton.text(opts.chooseText); - chooseButton.on("click.spectrum", function (e) { + chooseButton.bind("click.spectrum", function (e) { e.stopPropagation(); e.preventDefault(); @@ -367,7 +372,7 @@ }); toggleButton.text(opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText); - toggleButton.on("click.spectrum", function (e) { + toggleButton.bind("click.spectrum", function (e) { e.stopPropagation(); e.preventDefault(); @@ -408,8 +413,7 @@ // shift+drag should snap the movement to either the x or y axis. if (!e.shiftKey) { shiftMovementDirection = null; - } - else if (!shiftMovementDirection) { + } else if (!shiftMovementDirection) { var oldDragX = currentSaturation * dragWidth; var oldDragY = dragHeight - (currentValue * dragHeight); var furtherFromX = Math.abs(dragX - oldDragX) > Math.abs(dragY - oldDragY); @@ -445,8 +449,7 @@ currentPreferredFormat = opts.preferredFormat || tinycolor(initialColor).format; addColorToSelectionPalette(initialColor); - } - else { + } else { updateUI(); } @@ -454,22 +457,17 @@ show(); } - function paletteElementClick(e) { + function paletteElementClick(e) + { if (e.data && e.data.ignore) { set($(e.target).closest(".sp-thumb-el").data("color")); move(); - } - else { + } else { set($(e.target).closest(".sp-thumb-el").data("color")); move(); - - // If the picker is going to close immediately, a palette selection - // is a change. Otherwise, it's a move only. + updateOriginalInput(true); if (opts.hideAfterPaletteSelect) { - updateOriginalInput(true); hide(); - } else { - updateOriginalInput(); } } @@ -477,39 +475,39 @@ } var paletteEvent = IE ? "mousedown.spectrum" : "click.spectrum touchstart.spectrum"; - paletteContainer.on(paletteEvent, ".sp-thumb-el", paletteElementClick); - initialColorContainer.on(paletteEvent, ".sp-thumb-el:nth-child(1)", { ignore: true }, paletteElementClick); + paletteContainer.delegate(".sp-thumb-el", paletteEvent, paletteElementClick); + initialColorContainer.delegate(".sp-thumb-el:nth-child(1)", paletteEvent, { ignore: true }, paletteElementClick); } - function updateSelectionPaletteFromStorage() { - + function updateSelectionPaletteFromStorage() + { if (localStorageKey && window.localStorage) { - // Migrate old palettes over to new format. May want to remove this eventually. try { var oldPalette = window.localStorage[localStorageKey].split(",#"); if (oldPalette.length > 1) { delete window.localStorage[localStorageKey]; - $.each(oldPalette, function(i, c) { - addColorToSelectionPalette(c); + $.each(oldPalette, function (i, c) { + addColorToSelectionPalette(c); }); } + } catch (e) { } - catch(e) { } try { selectionPalette = window.localStorage[localStorageKey].split(";"); + } catch (e) { } - catch (e) { } } } - function addColorToSelectionPalette(color) { + function addColorToSelectionPalette(color) + { if (showSelectionPalette) { var rgb = tinycolor(color).toRgbString(); if (!paletteLookup[rgb] && $.inArray(rgb, selectionPalette) === -1) { selectionPalette.push(rgb); - while(selectionPalette.length > maxSelectionSize) { + while (selectionPalette.length > maxSelectionSize) { selectionPalette.shift(); } } @@ -517,13 +515,14 @@ if (localStorageKey && window.localStorage) { try { window.localStorage[localStorageKey] = selectionPalette.join(";"); + } catch (e) { } - catch(e) { } } } } - function getUniqueSelectionPalette() { + function getUniqueSelectionPalette() + { var unique = []; if (opts.showPalette) { for (var i = 0; i < selectionPalette.length; i++) { @@ -538,8 +537,8 @@ return unique.reverse().slice(0, opts.maxSelectionSize); } - function drawPalette() { - + function drawPalette() + { var currentColor = get(); var html = $.map(paletteArray, function (palette, i) { @@ -555,7 +554,8 @@ paletteContainer.html(html.join("")); } - function drawInitial() { + function drawInitial() + { if (opts.showInitial) { var initial = colorOnShow; var current = get(); @@ -563,7 +563,8 @@ } } - function dragStart() { + function dragStart() + { if (dragHeight <= 0 || dragWidth <= 0 || slideHeight <= 0) { reflow(); } @@ -573,44 +574,42 @@ boundElement.trigger('dragstart.spectrum', [ get() ]); } - function dragStop() { + function dragStop() + { isDragging = false; container.removeClass(draggingClass); boundElement.trigger('dragstop.spectrum', [ get() ]); } - function setFromTextInput() { - + function setFromTextInput() + { var value = textInput.val(); if ((value === null || value === "") && allowEmpty) { set(null); - move(); - updateOriginalInput(); - } - else { + updateOriginalInput(true); + } else { var tiny = tinycolor(value); if (tiny.isValid()) { set(tiny); - move(); - updateOriginalInput(); - } - else { + updateOriginalInput(true); + } else { textInput.addClass("sp-validation-error"); } } } - function toggle() { + function toggle() + { if (visible) { hide(); - } - else { + } else { show(); } } - function show() { + function show() + { var event = $.Event('beforeShow.spectrum'); if (visible) { @@ -627,9 +626,9 @@ hideAll(); visible = true; - $(doc).on("keydown.spectrum", onkeydown); - $(doc).on("click.spectrum", clickout); - $(window).on("resize.spectrum", resize); + $(doc).bind("keydown.spectrum", onkeydown); + $(doc).bind("click.spectrum", clickout); + $(window).bind("resize.spectrum", resize); replacer.addClass("sp-active"); container.removeClass("sp-hidden"); @@ -643,38 +642,46 @@ boundElement.trigger('show.spectrum', [ colorOnShow ]); } - function onkeydown(e) { + function onkeydown(e) + { // Close on ESC if (e.keyCode === 27) { hide(); } } - function clickout(e) { + function clickout(e) + { // Return on right click. - if (e.button == 2) { return; } + if (e.button == 2) { + return; + } // If a drag event was happening during the mouseup, don't hide // on click. - if (isDragging) { return; } + if (isDragging) { + return; + } if (clickoutFiresChange) { updateOriginalInput(true); - } - else { + } else { revert(); } hide(); } - function hide() { + function hide() + { // Return if hiding is unnecessary - if (!visible || flat) { return; } + if (!visible || flat) { + return; + } visible = false; - $(doc).off("keydown.spectrum", onkeydown); - $(doc).off("click.spectrum", clickout); - $(window).off("resize.spectrum", resize); + $(doc).unbind("keydown.spectrum", onkeydown); + $(doc).unbind("click.spectrum", clickout); + $(window).unbind("resize.spectrum", resize); replacer.removeClass("sp-active"); container.addClass("sp-hidden"); @@ -683,12 +690,13 @@ boundElement.trigger('hide.spectrum', [ get() ]); } - function revert() { + function revert() + { set(colorOnShow, true); - updateOriginalInput(true); } - function set(color, ignoreFormatChange) { + function set(color, ignoreFormatChange) + { if (tinycolor.equals(color, get())) { // Update UI just in case a validation error needs // to be cleared. @@ -716,7 +724,8 @@ } } - function get(opts) { + function get(opts) + { opts = opts || { }; if (allowEmpty && isEmpty) { @@ -727,23 +736,25 @@ h: currentHue, s: currentSaturation, v: currentValue, - a: Math.round(currentAlpha * 1000) / 1000 + a: Math.round(currentAlpha * 100) / 100 }, { format: opts.format || currentPreferredFormat }); } - function isValid() { + function isValid() + { return !textInput.hasClass("sp-validation-error"); } - function move() { + function move() + { updateUI(); callbacks.move(get()); boundElement.trigger('move.spectrum', [ get() ]); } - function updateUI() { - + function updateUI() + { textInput.removeClass("sp-validation-error"); updateHelperLocations(); @@ -770,16 +781,14 @@ if (!realColor && allowEmpty) { // Update the replaced elements background with icon indicating no color selection previewElement.addClass("sp-clear-display"); - } - else { + } else { var realHex = realColor.toHexString(), realRgb = realColor.toRgbString(); // Update the replaced elements background color (with actual selected color) if (rgbaSupport || realColor.alpha === 1) { previewElement.css("background-color", realRgb); - } - else { + } else { previewElement.css("background-color", "transparent"); previewElement.css("filter", realColor.toFilter()); } @@ -792,14 +801,15 @@ if (IE) { alphaSliderInner.css("filter", tinycolor(realAlpha).toFilter({ gradientType: 1 }, realHex)); - } - else { + } else { alphaSliderInner.css("background", "-webkit-" + gradient); alphaSliderInner.css("background", "-moz-" + gradient); alphaSliderInner.css("background", "-ms-" + gradient); // Use current syntax gradient on unprefixed property. - alphaSliderInner.css("background", - "linear-gradient(to right, " + realAlpha + ", " + realHex + ")"); + alphaSliderInner.css( + "background", + "linear-gradient(to right, " + realAlpha + ", " + realHex + ")" + ); } } @@ -818,17 +828,17 @@ drawInitial(); } - function updateHelperLocations() { + function updateHelperLocations() + { var s = currentSaturation; var v = currentValue; - if(allowEmpty && isEmpty) { + if (allowEmpty && isEmpty) { //if selected color is empty, hide the helpers alphaSlideHelper.hide(); slideHelper.hide(); dragHelper.hide(); - } - else { + } else { //make sure helpers are visible alphaSlideHelper.show(); slideHelper.show(); @@ -863,7 +873,8 @@ } } - function updateOriginalInput(fireCallback) { + function updateOriginalInput(fireCallback) + { var color = get(), displayColor = '', hasChanged = !tinycolor.equals(color, colorOnShow); @@ -884,7 +895,8 @@ } } - function reflow() { + function reflow() + { if (!visible) { return; // Calculations would be useless and wouldn't be reliable anyways } @@ -915,15 +927,17 @@ boundElement.trigger('reflow.spectrum'); } - function destroy() { + function destroy() + { boundElement.show(); - offsetElement.off("click.spectrum touchstart.spectrum"); + offsetElement.unbind("click.spectrum touchstart.spectrum"); container.remove(); replacer.remove(); spectrums[spect.id] = null; } - function option(optionName, optionValue) { + function option(optionName, optionValue) + { if (optionName === undefined) { return $.extend({}, opts); } @@ -939,20 +953,23 @@ applyOptions(); } - function enable() { + function enable() + { disabled = false; boundElement.attr("disabled", false); offsetElement.removeClass("sp-disabled"); } - function disable() { + function disable() + { hide(); disabled = true; boundElement.attr("disabled", true); offsetElement.addClass("sp-disabled"); } - function setOffset(coord) { + function setOffset(coord) + { opts.offset = coord; reflow(); } @@ -986,7 +1003,8 @@ * checkOffset - get the offset below/above and left/right element depending on screen position * Thanks https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js */ - function getOffset(picker, input) { + function getOffset(picker, input) + { var extraY = 0; var dpWidth = picker.outerWidth(); var dpHeight = picker.outerHeight(); @@ -996,40 +1014,31 @@ var viewWidth = docElem.clientWidth + $(doc).scrollLeft(); var viewHeight = docElem.clientHeight + $(doc).scrollTop(); var offset = input.offset(); - var offsetLeft = offset.left; - var offsetTop = offset.top; - - offsetTop += inputHeight; + offset.top += inputHeight; - offsetLeft -= - Math.min(offsetLeft, (offsetLeft + dpWidth > viewWidth && viewWidth > dpWidth) ? - Math.abs(offsetLeft + dpWidth - viewWidth) : 0); + offset.left -= + Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ? + Math.abs(offset.left + dpWidth - viewWidth) : 0); - offsetTop -= - Math.min(offsetTop, ((offsetTop + dpHeight > viewHeight && viewHeight > dpHeight) ? + offset.top -= + Math.min(offset.top, ((offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ? Math.abs(dpHeight + inputHeight - extraY) : extraY)); - return { - top: offsetTop, - bottom: offset.bottom, - left: offsetLeft, - right: offset.right, - width: offset.width, - height: offset.height - }; + return offset; } /** * noop - do nothing */ - function noop() { - + function noop() + { } /** * stopPropagation - makes the code only doing this a little easier to read in line */ - function stopPropagation(e) { + function stopPropagation(e) + { e.stopPropagation(); } @@ -1037,7 +1046,8 @@ * Create a function bound to a given object * Thanks to underscore.js */ - function bind(func, obj) { + function bind(func, obj) + { var slice = Array.prototype.slice; var args = slice.call(arguments, 2); return function () { @@ -1049,7 +1059,8 @@ * Lightweight drag helper. Handles containment within the element, so that * when dragging, the x is within [0,element.width] and y is within [0,element.height] */ - function draggable(element, onmove, onstart, onstop) { + function draggable(element, onmove, onstart, onstop) + { onmove = onmove || function () { }; onstart = onstart || function () { }; onstop = onstop || function () { }; @@ -1066,7 +1077,8 @@ duringDragEvents["touchmove mousemove"] = move; duringDragEvents["touchend mouseup"] = stop; - function prevent(e) { + function prevent(e) + { if (e.stopPropagation) { e.stopPropagation(); } @@ -1076,7 +1088,8 @@ e.returnValue = false; } - function move(e) { + function move(e) + { if (dragging) { // Mouseup happened outside of window if (IE && doc.documentMode < 9 && !e.button) { @@ -1099,7 +1112,8 @@ } } - function start(e) { + function start(e) + { var rightclick = (e.which) ? (e.which == 3) : (e.button == 2); if (!rightclick && !dragging) { @@ -1109,7 +1123,7 @@ maxWidth = $(element).width(); offset = $(element).offset(); - $(doc).on(duringDragEvents); + $(doc).bind(duringDragEvents); $(doc.body).addClass("sp-dragging"); move(e); @@ -1119,24 +1133,26 @@ } } - function stop() { + function stop() + { if (dragging) { - $(doc).off(duringDragEvents); + $(doc).unbind(duringDragEvents); $(doc.body).removeClass("sp-dragging"); // Wait a tick before notifying observers to allow the click event // to fire in Chrome. - setTimeout(function() { + setTimeout(function () { onstop.apply(element, arguments); }, 0); } dragging = false; } - $(element).on("touchstart mousedown", start); + $(element).bind("touchstart mousedown", start); } - function throttle(func, wait, debounce) { + function throttle(func, wait, debounce) + { var timeout; return function () { var context = this, args = arguments; @@ -1144,12 +1160,17 @@ timeout = null; func.apply(context, args); }; - if (debounce) clearTimeout(timeout); - if (debounce || !timeout) timeout = setTimeout(throttler, wait); + if (debounce) { + clearTimeout(timeout); + } + if (debounce || !timeout) { + timeout = setTimeout(throttler, wait); + } }; } - function inputTypeColorSupport() { + function inputTypeColorSupport() + { return $.fn.spectrum.inputTypeColorSupport(); } @@ -1158,34 +1179,28 @@ */ var dataID = "spectrum.id"; $.fn.spectrum = function (opts, extra) { - if (typeof opts == "string") { - var returnValue = this; - var args = Array.prototype.slice.call( arguments, 1 ); + var args = Array.prototype.slice.call(arguments, 1); this.each(function () { var spect = spectrums[$(this).data(dataID)]; if (spect) { var method = spect[opts]; if (!method) { - throw new Error( "Spectrum: no such method: '" + opts + "'" ); + throw new Error("Spectrum: no such method: '" + opts + "'"); } if (opts == "get") { returnValue = spect.get(); - } - else if (opts == "container") { + } else if (opts == "container") { returnValue = spect.container; - } - else if (opts == "option") { + } else if (opts == "option") { returnValue = spect.option.apply(spect, args); - } - else if (opts == "destroy") { + } else if (opts == "destroy") { spect.destroy(); $(this).removeData(dataID); - } - else { + } else { method.apply(spect, args); } } @@ -1196,7 +1211,7 @@ // Initializing a new instance of spectrum return this.spectrum("destroy").each(function () { - var options = $.extend({}, $(this).data(), opts); + var options = $.extend({}, opts, $(this).data()); var spect = spectrum(this, options); $(this).data(dataID, spect.id); }); @@ -1206,7 +1221,8 @@ $.fn.spectrum.loadOpts = {}; $.fn.spectrum.draggable = draggable; $.fn.spectrum.defaults = defaultOpts; - $.fn.spectrum.inputTypeColorSupport = function inputTypeColorSupport() { + $.fn.spectrum.inputTypeColorSupport = function inputTypeColorSupport() + { if (typeof inputTypeColorSupport._cachedResult === "undefined") { var colorInput = $("")[0]; // if color element is supported, value will default to not null inputTypeColorSupport._cachedResult = colorInput.type === "color" && colorInput.value !== ""; @@ -1231,499 +1247,511 @@ // https://github.com/bgrins/TinyColor // Brian Grinstead, MIT License - (function() { - - var trimLeft = /^[\s,#]+/, - trimRight = /\s+$/, - tinyCounter = 0, - math = Math, - mathRound = math.round, - mathMin = math.min, - mathMax = math.max, - mathRandom = math.random; - - var tinycolor = function(color, opts) { - - color = (color) ? color : ''; - opts = opts || { }; - - // If input is already a tinycolor, return itself - if (color instanceof tinycolor) { - return color; - } - // If we are called as a function, call using new instead - if (!(this instanceof tinycolor)) { - return new tinycolor(color, opts); - } - - var rgb = inputToRGB(color); - this._originalInput = color, - this._r = rgb.r, - this._g = rgb.g, - this._b = rgb.b, - this._a = rgb.a, - this._roundA = mathRound(1000 * this._a) / 1000, - this._format = opts.format || rgb.format; - this._gradientType = opts.gradientType; - - // Don't let the range of [0,255] come back in [0,1]. - // Potentially lose a little bit of precision here, but will fix issues where - // .5 gets interpreted as half of the total, instead of half of 1 - // If it was supposed to be 128, this was already taken care of by `inputToRgb` - if (this._r < 1) { this._r = mathRound(this._r); } - if (this._g < 1) { this._g = mathRound(this._g); } - if (this._b < 1) { this._b = mathRound(this._b); } - - this._ok = rgb.ok; - this._tc_id = tinyCounter++; - }; + (function () { + var trimLeft = /^[\s,#]+/, + trimRight = /\s+$/, + tinyCounter = 0, + math = Math, + mathRound = math.round, + mathMin = math.min, + mathMax = math.max, + mathRandom = math.random; + + var tinycolor = function (color, opts) { + color = (color) ? color : ''; + opts = opts || { }; - tinycolor.prototype = { - isDark: function() { - return this.getBrightness() < 128; - }, - isLight: function() { - return !this.isDark(); - }, - isValid: function() { - return this._ok; - }, - getOriginalInput: function() { - return this._originalInput; - }, - getFormat: function() { - return this._format; - }, - getAlpha: function() { - return this._a; - }, - getBrightness: function() { - var rgb = this.toRgb(); - return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000; - }, - setAlpha: function(value) { - this._a = boundAlpha(value); - this._roundA = mathRound(1000 * this._a) / 1000; - return this; - }, - toHsv: function() { - var hsv = rgbToHsv(this._r, this._g, this._b); - return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a }; - }, - toHsvString: function() { - var hsv = rgbToHsv(this._r, this._g, this._b); - var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100); - return (this._a == 1) ? - "hsv(" + h + ", " + s + "%, " + v + "%)" : - "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")"; - }, - toHsl: function() { - var hsl = rgbToHsl(this._r, this._g, this._b); - return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a }; - }, - toHslString: function() { - var hsl = rgbToHsl(this._r, this._g, this._b); - var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100); - return (this._a == 1) ? - "hsl(" + h + ", " + s + "%, " + l + "%)" : - "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")"; - }, - toHex: function(allow3Char) { - return rgbToHex(this._r, this._g, this._b, allow3Char); - }, - toHexString: function(allow3Char) { - return '#' + this.toHex(allow3Char); - }, - toHex8: function() { - return rgbaToHex(this._r, this._g, this._b, this._a); - }, - toHex8String: function() { - return '#' + this.toHex8(); - }, - toRgb: function() { - return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a }; - }, - toRgbString: function() { - return (this._a == 1) ? - "rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" : - "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")"; - }, - toPercentageRgb: function() { - 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 }; - }, - toPercentageRgbString: function() { - return (this._a == 1) ? - "rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" : - "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")"; - }, - toName: function() { - if (this._a === 0) { - return "transparent"; - } - - if (this._a < 1) { - return false; + // If input is already a tinycolor, return itself + if (color instanceof tinycolor) { + return color; + } + // If we are called as a function, call using new instead + if (!(this instanceof tinycolor)) { + return new tinycolor(color, opts); } - return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false; - }, - toFilter: function(secondColor) { - var hex8String = '#' + rgbaToHex(this._r, this._g, this._b, this._a); - var secondHex8String = hex8String; - var gradientType = this._gradientType ? "GradientType = 1, " : ""; + var rgb = inputToRGB(color); + this._originalInput = color, + this._r = rgb.r, + this._g = rgb.g, + this._b = rgb.b, + this._a = rgb.a, + this._roundA = mathRound(100*this._a) / 100, + this._format = opts.format || rgb.format; + this._gradientType = opts.gradientType; - if (secondColor) { - var s = tinycolor(secondColor); - secondHex8String = s.toHex8String(); + // Don't let the range of [0,255] come back in [0,1]. + // Potentially lose a little bit of precision here, but will fix issues where + // .5 gets interpreted as half of the total, instead of half of 1 + // If it was supposed to be 128, this was already taken care of by `inputToRgb` + if (this._r < 1) { + this._r = mathRound(this._r); + } + if (this._g < 1) { + this._g = mathRound(this._g); + } + if (this._b < 1) { + this._b = mathRound(this._b); } - return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")"; - }, - toString: function(format) { - var formatSet = !!format; - format = format || this._format; + this._ok = rgb.ok; + this._tc_id = tinyCounter++; + }; - var formattedString = false; - var hasAlpha = this._a < 1 && this._a >= 0; - var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "name"); + tinycolor.prototype = { + isDark: function () { + return this.getBrightness() < 128; + }, + isLight: function () { + return !this.isDark(); + }, + isValid: function () { + return this._ok; + }, + getOriginalInput: function () { + return this._originalInput; + }, + getFormat: function () { + return this._format; + }, + getAlpha: function () { + return this._a; + }, + getBrightness: function () { + var rgb = this.toRgb(); + return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000; + }, + setAlpha: function (value) { + this._a = boundAlpha(value); + this._roundA = mathRound(100*this._a) / 100; + return this; + }, + toHsv: function () { + var hsv = rgbToHsv(this._r, this._g, this._b); + return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a }; + }, + toHsvString: function () { + var hsv = rgbToHsv(this._r, this._g, this._b); + var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100); + return (this._a == 1) ? + "hsv(" + h + ", " + s + "%, " + v + "%)" : + "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")"; + }, + toHsl: function () { + var hsl = rgbToHsl(this._r, this._g, this._b); + return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a }; + }, + toHslString: function () { + var hsl = rgbToHsl(this._r, this._g, this._b); + var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100); + return (this._a == 1) ? + "hsl(" + h + ", " + s + "%, " + l + "%)" : + "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")"; + }, + toHex: function (allow3Char) { + return rgbToHex(this._r, this._g, this._b, allow3Char); + }, + toHexString: function (allow3Char) { + return '#' + this.toHex(allow3Char); + }, + toHex8: function () { + return rgbaToHex(this._r, this._g, this._b, this._a); + }, + toHex8String: function () { + return '#' + this.toHex8(); + }, + toRgb: function () { + return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a }; + }, + toRgbString: function () { + return (this._a == 1) ? + "rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" : + "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")"; + }, + toPercentageRgb: function () { + 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 }; + }, + toPercentageRgbString: function () { + return (this._a == 1) ? + "rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" : + "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")"; + }, + toName: function () { + if (this._a === 0) { + return "transparent"; + } - if (needsAlphaFormat) { - // Special case for "transparent", all other non-alpha formats - // will return rgba when there is transparency. - if (format === "name" && this._a === 0) { - return this.toName(); + if (this._a < 1) { + return false; } - return this.toRgbString(); - } - if (format === "rgb") { - formattedString = this.toRgbString(); - } - if (format === "prgb") { - formattedString = this.toPercentageRgbString(); - } - if (format === "hex" || format === "hex6") { - formattedString = this.toHexString(); - } - if (format === "hex3") { - formattedString = this.toHexString(true); - } - if (format === "hex8") { - formattedString = this.toHex8String(); - } - if (format === "name") { - formattedString = this.toName(); - } - if (format === "hsl") { - formattedString = this.toHslString(); - } - if (format === "hsv") { - formattedString = this.toHsvString(); - } - - return formattedString || this.toHexString(); - }, - - _applyModification: function(fn, args) { - var color = fn.apply(null, [this].concat([].slice.call(args))); - this._r = color._r; - this._g = color._g; - this._b = color._b; - this.setAlpha(color._a); - return this; - }, - lighten: function() { - return this._applyModification(lighten, arguments); - }, - brighten: function() { - return this._applyModification(brighten, arguments); - }, - darken: function() { - return this._applyModification(darken, arguments); - }, - desaturate: function() { - return this._applyModification(desaturate, arguments); - }, - saturate: function() { - return this._applyModification(saturate, arguments); - }, - greyscale: function() { - return this._applyModification(greyscale, arguments); - }, - spin: function() { - return this._applyModification(spin, arguments); - }, - - _applyCombination: function(fn, args) { - return fn.apply(null, [this].concat([].slice.call(args))); - }, - analogous: function() { - return this._applyCombination(analogous, arguments); - }, - complement: function() { - return this._applyCombination(complement, arguments); - }, - monochromatic: function() { - return this._applyCombination(monochromatic, arguments); - }, - splitcomplement: function() { - return this._applyCombination(splitcomplement, arguments); - }, - triad: function() { - return this._applyCombination(triad, arguments); - }, - tetrad: function() { - return this._applyCombination(tetrad, arguments); - } - }; - // If input is an object, force 1 into "1.0" to handle ratios properly - // String input requires "1.0" as input, so 1 will be treated as 1 - tinycolor.fromRatio = function(color, opts) { - if (typeof color == "object") { - var newColor = {}; - for (var i in color) { - if (color.hasOwnProperty(i)) { - if (i === "a") { - newColor[i] = color[i]; - } - else { - newColor[i] = convertToPercentage(color[i]); + return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false; + }, + toFilter: function (secondColor) { + var hex8String = '#' + rgbaToHex(this._r, this._g, this._b, this._a); + var secondHex8String = hex8String; + var gradientType = this._gradientType ? "GradientType = 1, " : ""; + + if (secondColor) { + var s = tinycolor(secondColor); + secondHex8String = s.toHex8String(); + } + + return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")"; + }, + toString: function (format) { + var formatSet = !!format; + format = format || this._format; + + var formattedString = false; + var hasAlpha = this._a < 1 && this._a >= 0; + var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "name"); + + if (needsAlphaFormat) { + // Special case for "transparent", all other non-alpha formats + // will return rgba when there is transparency. + if (format === "name" && this._a === 0) { + return this.toName(); } + return this.toRgbString(); + } + if (format === "rgb") { + formattedString = this.toRgbString(); } + if (format === "prgb") { + formattedString = this.toPercentageRgbString(); + } + if (format === "hex" || format === "hex6") { + formattedString = this.toHexString(); + } + if (format === "hex3") { + formattedString = this.toHexString(true); + } + if (format === "hex8") { + formattedString = this.toHex8String(); + } + if (format === "name") { + formattedString = this.toName(); + } + if (format === "hsl") { + formattedString = this.toHslString(); + } + if (format === "hsv") { + formattedString = this.toHsvString(); + } + + return formattedString || this.toHexString(); + }, + + _applyModification: function (fn, args) { + var color = fn.apply(null, [this].concat([].slice.call(args))); + this._r = color._r; + this._g = color._g; + this._b = color._b; + this.setAlpha(color._a); + return this; + }, + lighten: function () { + return this._applyModification(lighten, arguments); + }, + brighten: function () { + return this._applyModification(brighten, arguments); + }, + darken: function () { + return this._applyModification(darken, arguments); + }, + desaturate: function () { + return this._applyModification(desaturate, arguments); + }, + saturate: function () { + return this._applyModification(saturate, arguments); + }, + greyscale: function () { + return this._applyModification(greyscale, arguments); + }, + spin: function () { + return this._applyModification(spin, arguments); + }, + + _applyCombination: function (fn, args) { + return fn.apply(null, [this].concat([].slice.call(args))); + }, + analogous: function () { + return this._applyCombination(analogous, arguments); + }, + complement: function () { + return this._applyCombination(complement, arguments); + }, + monochromatic: function () { + return this._applyCombination(monochromatic, arguments); + }, + splitcomplement: function () { + return this._applyCombination(splitcomplement, arguments); + }, + triad: function () { + return this._applyCombination(triad, arguments); + }, + tetrad: function () { + return this._applyCombination(tetrad, arguments); } - color = newColor; - } + }; - return tinycolor(color, opts); - }; + // If input is an object, force 1 into "1.0" to handle ratios properly + // String input requires "1.0" as input, so 1 will be treated as 1 + tinycolor.fromRatio = function (color, opts) { + if (typeof color == "object") { + var newColor = {}; + for (var i in color) { + if (color.hasOwnProperty(i)) { + if (i === "a") { + newColor[i] = color[i]; + } else { + newColor[i] = convertToPercentage(color[i]); + } + } + } + color = newColor; + } - // Given a string or object, convert that input to RGB - // Possible string inputs: - // - // "red" - // "#f00" or "f00" - // "#ff0000" or "ff0000" - // "#ff000000" or "ff000000" - // "rgb 255 0 0" or "rgb (255, 0, 0)" - // "rgb 1.0 0 0" or "rgb (1, 0, 0)" - // "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1" - // "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1" - // "hsl(0, 100%, 50%)" or "hsl 0 100% 50%" - // "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1" - // "hsv(0, 100%, 100%)" or "hsv 0 100% 100%" - // - function inputToRGB(color) { - - var rgb = { r: 0, g: 0, b: 0 }; - var a = 1; - var ok = false; - var format = false; - - if (typeof color == "string") { - color = stringInputToObject(color); - } - - if (typeof color == "object") { - if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) { - rgb = rgbToRgb(color.r, color.g, color.b); - ok = true; - format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb"; - } - else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("v")) { - color.s = convertToPercentage(color.s); - color.v = convertToPercentage(color.v); - rgb = hsvToRgb(color.h, color.s, color.v); - ok = true; - format = "hsv"; - } - else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("l")) { - color.s = convertToPercentage(color.s); - color.l = convertToPercentage(color.l); - rgb = hslToRgb(color.h, color.s, color.l); - ok = true; - format = "hsl"; - } - - if (color.hasOwnProperty("a")) { - a = color.a; - } - } - - a = boundAlpha(a); - - return { - ok: ok, - format: color.format || format, - r: mathMin(255, mathMax(rgb.r, 0)), - g: mathMin(255, mathMax(rgb.g, 0)), - b: mathMin(255, mathMax(rgb.b, 0)), - a: a + return tinycolor(color, opts); }; - } + // Given a string or object, convert that input to RGB + // Possible string inputs: + // + // "red" + // "#f00" or "f00" + // "#ff0000" or "ff0000" + // "#ff000000" or "ff000000" + // "rgb 255 0 0" or "rgb (255, 0, 0)" + // "rgb 1.0 0 0" or "rgb (1, 0, 0)" + // "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1" + // "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1" + // "hsl(0, 100%, 50%)" or "hsl 0 100% 50%" + // "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1" + // "hsv(0, 100%, 100%)" or "hsv 0 100% 100%" + // + function inputToRGB(color) + { + var rgb = { r: 0, g: 0, b: 0 }; + var a = 1; + var ok = false; + var format = false; + + if (typeof color == "string") { + color = stringInputToObject(color); + } + + if (typeof color == "object") { + if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) { + rgb = rgbToRgb(color.r, color.g, color.b); + ok = true; + format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb"; + } else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("v")) { + color.s = convertToPercentage(color.s); + color.v = convertToPercentage(color.v); + rgb = hsvToRgb(color.h, color.s, color.v); + ok = true; + format = "hsv"; + } else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("l")) { + color.s = convertToPercentage(color.s); + color.l = convertToPercentage(color.l); + rgb = hslToRgb(color.h, color.s, color.l); + ok = true; + format = "hsl"; + } - // Conversion Functions - // -------------------- + if (color.hasOwnProperty("a")) { + a = color.a; + } + } - // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from: - // + a = boundAlpha(a); - // `rgbToRgb` - // Handle bounds / percentage checking to conform to CSS color spec - // - // *Assumes:* r, g, b in [0, 255] or [0, 1] - // *Returns:* { r, g, b } in [0, 255] - function rgbToRgb(r, g, b){ - return { - r: bound01(r, 255) * 255, - g: bound01(g, 255) * 255, - b: bound01(b, 255) * 255 - }; - } + return { + ok: ok, + format: color.format || format, + r: mathMin(255, mathMax(rgb.r, 0)), + g: mathMin(255, mathMax(rgb.g, 0)), + b: mathMin(255, mathMax(rgb.b, 0)), + a: a + }; + } - // `rgbToHsl` - // Converts an RGB color value to HSL. - // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1] - // *Returns:* { h, s, l } in [0,1] - function rgbToHsl(r, g, b) { - r = bound01(r, 255); - g = bound01(g, 255); - b = bound01(b, 255); + // Conversion Functions + // -------------------- - var max = mathMax(r, g, b), min = mathMin(r, g, b); - var h, s, l = (max + min) / 2; + // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from: + // - if(max == min) { - h = s = 0; // achromatic + // `rgbToRgb` + // Handle bounds / percentage checking to conform to CSS color spec + // + // *Assumes:* r, g, b in [0, 255] or [0, 1] + // *Returns:* { r, g, b } in [0, 255] + function rgbToRgb(r, g, b) + { + return { + r: bound01(r, 255) * 255, + g: bound01(g, 255) * 255, + b: bound01(b, 255) * 255 + }; } - else { - var d = max - min; - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - switch(max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } + // `rgbToHsl` + // Converts an RGB color value to HSL. + // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1] + // *Returns:* { h, s, l } in [0,1] + function rgbToHsl(r, g, b) + { + r = bound01(r, 255); + g = bound01(g, 255); + b = bound01(b, 255); - return { h: h, s: s, l: l }; - } + var max = mathMax(r, g, b), min = mathMin(r, g, b); + var h, s, l = (max + min) / 2; - // `hslToRgb` - // Converts an HSL color value to RGB. - // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100] - // *Returns:* { r, g, b } in the set [0, 255] - function hslToRgb(h, s, l) { - var r, g, b; + if (max == min) { + h = s = 0; // achromatic + } else { + var d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + switch (max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } - h = bound01(h, 360); - s = bound01(s, 100); - l = bound01(l, 100); + h /= 6; + } - function hue2rgb(p, q, t) { - if(t < 0) t += 1; - if(t > 1) t -= 1; - if(t < 1/6) return p + (q - p) * 6 * t; - if(t < 1/2) return q; - if(t < 2/3) return p + (q - p) * (2/3 - t) * 6; - return p; + return { h: h, s: s, l: l }; } - if(s === 0) { - r = g = b = l; // achromatic - } - else { - var q = l < 0.5 ? l * (1 + s) : l + s - l * s; - var p = 2 * l - q; - r = hue2rgb(p, q, h + 1/3); - g = hue2rgb(p, q, h); - b = hue2rgb(p, q, h - 1/3); - } + // `hslToRgb` + // Converts an HSL color value to RGB. + // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100] + // *Returns:* { r, g, b } in the set [0, 255] + function hslToRgb(h, s, l) + { + var r, g, b; - return { r: r * 255, g: g * 255, b: b * 255 }; - } + h = bound01(h, 360); + s = bound01(s, 100); + l = bound01(l, 100); - // `rgbToHsv` - // Converts an RGB color value to HSV - // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1] - // *Returns:* { h, s, v } in [0,1] - function rgbToHsv(r, g, b) { + function hue2rgb(p, q, t) + { + if (t < 0) { + t += 1; + } + if (t > 1) { + t -= 1; + } + if (t < 1/6) { + return p + (q - p) * 6 * t; + } + if (t < 1/2) { + return q; + } + if (t < 2/3) { + return p + (q - p) * (2/3 - t) * 6; + } + return p; + } - r = bound01(r, 255); - g = bound01(g, 255); - b = bound01(b, 255); + if (s === 0) { + r = g = b = l; // achromatic + } else { + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = hue2rgb(p, q, h + 1/3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1/3); + } - var max = mathMax(r, g, b), min = mathMin(r, g, b); - var h, s, v = max; + return { r: r * 255, g: g * 255, b: b * 255 }; + } - var d = max - min; - s = max === 0 ? 0 : d / max; + // `rgbToHsv` + // Converts an RGB color value to HSV + // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1] + // *Returns:* { h, s, v } in [0,1] + function rgbToHsv(r, g, b) + { + r = bound01(r, 255); + g = bound01(g, 255); + b = bound01(b, 255); - if(max == min) { - h = 0; // achromatic - } - else { - switch(max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h, s: s, v: v }; - } + var max = mathMax(r, g, b), min = mathMin(r, g, b); + var h, s, v = max; - // `hsvToRgb` - // Converts an HSV color value to RGB. - // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100] - // *Returns:* { r, g, b } in the set [0, 255] - function hsvToRgb(h, s, v) { - - h = bound01(h, 360) * 6; - s = bound01(s, 100); - v = bound01(v, 100); - - var i = math.floor(h), - f = h - i, - p = v * (1 - s), - q = v * (1 - f * s), - t = v * (1 - (1 - f) * s), - mod = i % 6, - r = [v, q, p, p, t, v][mod], - g = [t, v, v, q, p, p][mod], - b = [p, p, t, v, v, q][mod]; - - return { r: r * 255, g: g * 255, b: b * 255 }; - } + var d = max - min; + s = max === 0 ? 0 : d / max; - // `rgbToHex` - // Converts an RGB color to hex - // Assumes r, g, and b are contained in the set [0, 255] - // Returns a 3 or 6 character hex - function rgbToHex(r, g, b, allow3Char) { + if (max == min) { + h = 0; // achromatic + } else { + switch (max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + h /= 6; + } + return { h: h, s: s, v: v }; + } + + // `hsvToRgb` + // Converts an HSV color value to RGB. + // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100] + // *Returns:* { r, g, b } in the set [0, 255] + function hsvToRgb(h, s, v) + { + h = bound01(h, 360) * 6; + s = bound01(s, 100); + v = bound01(v, 100); + + var i = math.floor(h), + f = h - i, + p = v * (1 - s), + q = v * (1 - f * s), + t = v * (1 - (1 - f) * s), + mod = i % 6, + r = [v, q, p, p, t, v][mod], + g = [t, v, v, q, p, p][mod], + b = [p, p, t, v, v, q][mod]; + + return { r: r * 255, g: g * 255, b: b * 255 }; + } + + // `rgbToHex` + // Converts an RGB color to hex + // Assumes r, g, and b are contained in the set [0, 255] + // Returns a 3 or 6 character hex + function rgbToHex(r, g, b, allow3Char) + { + var hex = [ + pad2(mathRound(r).toString(16)), + pad2(mathRound(g).toString(16)), + pad2(mathRound(b).toString(16)) + ]; - var hex = [ - pad2(mathRound(r).toString(16)), - pad2(mathRound(g).toString(16)), - pad2(mathRound(b).toString(16)) - ]; + // Return a 3 character hex if possible + 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)) { + return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0); + } - // Return a 3 character hex if possible - 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)) { - return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0); + return hex.join(""); } - return hex.join(""); - } // `rgbaToHex` // Converts an RGBA color plus alpha transparency to hex // Assumes r, g, b and a are contained in the set [0, 255] // Returns an 8 character hex - function rgbaToHex(r, g, b, a) { - + function rgbaToHex(r, g, b, a) + { var hex = [ pad2(convertDecimalToHex(a)), pad2(mathRound(r).toString(16)), @@ -1734,602 +1762,625 @@ return hex.join(""); } - // `equals` - // Can be called with any tinycolor input - tinycolor.equals = function (color1, color2) { - if (!color1 || !color2) { return false; } - return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString(); - }; - tinycolor.random = function() { - return tinycolor.fromRatio({ - r: mathRandom(), - g: mathRandom(), - b: mathRandom() - }); - }; - - - // Modification Functions - // ---------------------- - // Thanks to less.js for some of the basics here - // - - function desaturate(color, amount) { - amount = (amount === 0) ? 0 : (amount || 10); - var hsl = tinycolor(color).toHsl(); - hsl.s -= amount / 100; - hsl.s = clamp01(hsl.s); - return tinycolor(hsl); - } - - function saturate(color, amount) { - amount = (amount === 0) ? 0 : (amount || 10); - var hsl = tinycolor(color).toHsl(); - hsl.s += amount / 100; - hsl.s = clamp01(hsl.s); - return tinycolor(hsl); - } - - function greyscale(color) { - return tinycolor(color).desaturate(100); - } - - function lighten (color, amount) { - amount = (amount === 0) ? 0 : (amount || 10); - var hsl = tinycolor(color).toHsl(); - hsl.l += amount / 100; - hsl.l = clamp01(hsl.l); - return tinycolor(hsl); - } - - function brighten(color, amount) { - amount = (amount === 0) ? 0 : (amount || 10); - var rgb = tinycolor(color).toRgb(); - rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100)))); - rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100)))); - rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100)))); - return tinycolor(rgb); - } - - function darken (color, amount) { - amount = (amount === 0) ? 0 : (amount || 10); - var hsl = tinycolor(color).toHsl(); - hsl.l -= amount / 100; - hsl.l = clamp01(hsl.l); - return tinycolor(hsl); - } - - // Spin takes a positive or negative amount within [-360, 360] indicating the change of hue. - // Values outside of this range will be wrapped into this range. - function spin(color, amount) { - var hsl = tinycolor(color).toHsl(); - var hue = (mathRound(hsl.h) + amount) % 360; - hsl.h = hue < 0 ? 360 + hue : hue; - return tinycolor(hsl); - } - - // Combination Functions - // --------------------- - // Thanks to jQuery xColor for some of the ideas behind these - // + // `equals` + // Can be called with any tinycolor input + tinycolor.equals = function (color1, color2) { + if (!color1 || !color2) { + return false; + } + return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString(); + }; + tinycolor.random = function () { + return tinycolor.fromRatio({ + r: mathRandom(), + g: mathRandom(), + b: mathRandom() + }); + }; - function complement(color) { - var hsl = tinycolor(color).toHsl(); - hsl.h = (hsl.h + 180) % 360; - return tinycolor(hsl); - } - function triad(color) { - var hsl = tinycolor(color).toHsl(); - var h = hsl.h; - return [ - tinycolor(color), - tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }), - tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l }) - ]; - } + // Modification Functions + // ---------------------- + // Thanks to less.js for some of the basics here + // + + function desaturate(color, amount) + { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.s -= amount / 100; + hsl.s = clamp01(hsl.s); + return tinycolor(hsl); + } + + function saturate(color, amount) + { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.s += amount / 100; + hsl.s = clamp01(hsl.s); + return tinycolor(hsl); + } + + function greyscale(color) + { + return tinycolor(color).desaturate(100); + } + + function lighten(color, amount) + { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.l += amount / 100; + hsl.l = clamp01(hsl.l); + return tinycolor(hsl); + } + + function brighten(color, amount) + { + amount = (amount === 0) ? 0 : (amount || 10); + var rgb = tinycolor(color).toRgb(); + rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100)))); + rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100)))); + rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100)))); + return tinycolor(rgb); + } + + function darken(color, amount) + { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.l -= amount / 100; + hsl.l = clamp01(hsl.l); + return tinycolor(hsl); + } + + // Spin takes a positive or negative amount within [-360, 360] indicating the change of hue. + // Values outside of this range will be wrapped into this range. + function spin(color, amount) + { + var hsl = tinycolor(color).toHsl(); + var hue = (mathRound(hsl.h) + amount) % 360; + hsl.h = hue < 0 ? 360 + hue : hue; + return tinycolor(hsl); + } + + // Combination Functions + // --------------------- + // Thanks to jQuery xColor for some of the ideas behind these + // + + function complement(color) + { + var hsl = tinycolor(color).toHsl(); + hsl.h = (hsl.h + 180) % 360; + return tinycolor(hsl); + } + + function triad(color) + { + var hsl = tinycolor(color).toHsl(); + var h = hsl.h; + return [ + tinycolor(color), + tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }), + tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l }) + ]; + } - function tetrad(color) { - var hsl = tinycolor(color).toHsl(); - var h = hsl.h; - return [ - tinycolor(color), - tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }), - tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }), - tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l }) - ]; - } + function tetrad(color) + { + var hsl = tinycolor(color).toHsl(); + var h = hsl.h; + return [ + tinycolor(color), + tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }), + tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }), + tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l }) + ]; + } - function splitcomplement(color) { - var hsl = tinycolor(color).toHsl(); - var h = hsl.h; - return [ - tinycolor(color), - tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}), - tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l}) - ]; - } + function splitcomplement(color) + { + var hsl = tinycolor(color).toHsl(); + var h = hsl.h; + return [ + tinycolor(color), + tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}), + tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l}) + ]; + } - function analogous(color, results, slices) { - results = results || 6; - slices = slices || 30; + function analogous(color, results, slices) + { + results = results || 6; + slices = slices || 30; - var hsl = tinycolor(color).toHsl(); - var part = 360 / slices; - var ret = [tinycolor(color)]; + var hsl = tinycolor(color).toHsl(); + var part = 360 / slices; + var ret = [tinycolor(color)]; - for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) { - hsl.h = (hsl.h + part) % 360; - ret.push(tinycolor(hsl)); + for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results;) { + hsl.h = (hsl.h + part) % 360; + ret.push(tinycolor(hsl)); + } + return ret; } - return ret; - } - function monochromatic(color, results) { - results = results || 6; - var hsv = tinycolor(color).toHsv(); - var h = hsv.h, s = hsv.s, v = hsv.v; - var ret = []; - var modification = 1 / results; + function monochromatic(color, results) + { + results = results || 6; + var hsv = tinycolor(color).toHsv(); + var h = hsv.h, s = hsv.s, v = hsv.v; + var ret = []; + var modification = 1 / results; - while (results--) { - ret.push(tinycolor({ h: h, s: s, v: v})); - v = (v + modification) % 1; + while (results--) { + ret.push(tinycolor({ h: h, s: s, v: v})); + v = (v + modification) % 1; + } + + return ret; } - return ret; - } + // Utility Functions + // --------------------- - // Utility Functions - // --------------------- + tinycolor.mix = function (color1, color2, amount) { + amount = (amount === 0) ? 0 : (amount || 50); - tinycolor.mix = function(color1, color2, amount) { - amount = (amount === 0) ? 0 : (amount || 50); + var rgb1 = tinycolor(color1).toRgb(); + var rgb2 = tinycolor(color2).toRgb(); - var rgb1 = tinycolor(color1).toRgb(); - var rgb2 = tinycolor(color2).toRgb(); + var p = amount / 100; + var w = p * 2 - 1; + var a = rgb2.a - rgb1.a; - var p = amount / 100; - var w = p * 2 - 1; - var a = rgb2.a - rgb1.a; + var w1; - var w1; + if (w * a == -1) { + w1 = w; + } else { + w1 = (w + a) / (1 + w * a); + } - if (w * a == -1) { - w1 = w; - } else { - w1 = (w + a) / (1 + w * a); - } + w1 = (w1 + 1) / 2; - w1 = (w1 + 1) / 2; + var w2 = 1 - w1; - var w2 = 1 - w1; + var rgba = { + r: rgb2.r * w1 + rgb1.r * w2, + g: rgb2.g * w1 + rgb1.g * w2, + b: rgb2.b * w1 + rgb1.b * w2, + a: rgb2.a * p + rgb1.a * (1 - p) + }; - var rgba = { - r: rgb2.r * w1 + rgb1.r * w2, - g: rgb2.g * w1 + rgb1.g * w2, - b: rgb2.b * w1 + rgb1.b * w2, - a: rgb2.a * p + rgb1.a * (1 - p) + return tinycolor(rgba); }; - return tinycolor(rgba); - }; + // Readability Functions + // --------------------- + // + + // `readability` + // Analyze the 2 colors and returns an object with the following properties: + // `brightness`: difference in brightness between the two colors + // `color`: difference in color/hue between the two colors + tinycolor.readability = function (color1, color2) { + var c1 = tinycolor(color1); + var c2 = tinycolor(color2); + var rgb1 = c1.toRgb(); + var rgb2 = c2.toRgb(); + var brightnessA = c1.getBrightness(); + var brightnessB = c2.getBrightness(); + var colorDiff = ( + Math.max(rgb1.r, rgb2.r) - Math.min(rgb1.r, rgb2.r) + + Math.max(rgb1.g, rgb2.g) - Math.min(rgb1.g, rgb2.g) + + Math.max(rgb1.b, rgb2.b) - Math.min(rgb1.b, rgb2.b) + ); - // Readability Functions - // --------------------- - // - - // `readability` - // Analyze the 2 colors and returns an object with the following properties: - // `brightness`: difference in brightness between the two colors - // `color`: difference in color/hue between the two colors - tinycolor.readability = function(color1, color2) { - var c1 = tinycolor(color1); - var c2 = tinycolor(color2); - var rgb1 = c1.toRgb(); - var rgb2 = c2.toRgb(); - var brightnessA = c1.getBrightness(); - var brightnessB = c2.getBrightness(); - var colorDiff = ( - Math.max(rgb1.r, rgb2.r) - Math.min(rgb1.r, rgb2.r) + - Math.max(rgb1.g, rgb2.g) - Math.min(rgb1.g, rgb2.g) + - Math.max(rgb1.b, rgb2.b) - Math.min(rgb1.b, rgb2.b) - ); - - return { - brightness: Math.abs(brightnessA - brightnessB), - color: colorDiff + return { + brightness: Math.abs(brightnessA - brightnessB), + color: colorDiff + }; }; - }; - - // `readable` - // http://www.w3.org/TR/AERT#color-contrast - // Ensure that foreground and background color combinations provide sufficient contrast. - // *Example* - // tinycolor.isReadable("#000", "#111") => false - tinycolor.isReadable = function(color1, color2) { - var readability = tinycolor.readability(color1, color2); - return readability.brightness > 125 && readability.color > 500; - }; - - // `mostReadable` - // Given a base color and a list of possible foreground or background - // colors for that base, returns the most readable color. - // *Example* - // tinycolor.mostReadable("#123", ["#fff", "#000"]) => "#000" - tinycolor.mostReadable = function(baseColor, colorList) { - var bestColor = null; - var bestScore = 0; - var bestIsReadable = false; - for (var i=0; i < colorList.length; i++) { - - // We normalize both around the "acceptable" breaking point, - // but rank brightness constrast higher than hue. - - var readability = tinycolor.readability(baseColor, colorList[i]); - var readable = readability.brightness > 125 && readability.color > 500; - var score = 3 * (readability.brightness / 125) + (readability.color / 500); - - if ((readable && ! bestIsReadable) || - (readable && bestIsReadable && score > bestScore) || - ((! readable) && (! bestIsReadable) && score > bestScore)) { - bestIsReadable = readable; - bestScore = score; - bestColor = tinycolor(colorList[i]); - } - } - return bestColor; - }; + // `readable` + // http://www.w3.org/TR/AERT#color-contrast + // Ensure that foreground and background color combinations provide sufficient contrast. + // *Example* + // tinycolor.isReadable("#000", "#111") => false + tinycolor.isReadable = function (color1, color2) { + var readability = tinycolor.readability(color1, color2); + return readability.brightness > 125 && readability.color > 500; + }; - // Big List of Colors - // ------------------ - // - var names = tinycolor.names = { - aliceblue: "f0f8ff", - antiquewhite: "faebd7", - aqua: "0ff", - aquamarine: "7fffd4", - azure: "f0ffff", - beige: "f5f5dc", - bisque: "ffe4c4", - black: "000", - blanchedalmond: "ffebcd", - blue: "00f", - blueviolet: "8a2be2", - brown: "a52a2a", - burlywood: "deb887", - burntsienna: "ea7e5d", - cadetblue: "5f9ea0", - chartreuse: "7fff00", - chocolate: "d2691e", - coral: "ff7f50", - cornflowerblue: "6495ed", - cornsilk: "fff8dc", - crimson: "dc143c", - cyan: "0ff", - darkblue: "00008b", - darkcyan: "008b8b", - darkgoldenrod: "b8860b", - darkgray: "a9a9a9", - darkgreen: "006400", - darkgrey: "a9a9a9", - darkkhaki: "bdb76b", - darkmagenta: "8b008b", - darkolivegreen: "556b2f", - darkorange: "ff8c00", - darkorchid: "9932cc", - darkred: "8b0000", - darksalmon: "e9967a", - darkseagreen: "8fbc8f", - darkslateblue: "483d8b", - darkslategray: "2f4f4f", - darkslategrey: "2f4f4f", - darkturquoise: "00ced1", - darkviolet: "9400d3", - deeppink: "ff1493", - deepskyblue: "00bfff", - dimgray: "696969", - dimgrey: "696969", - dodgerblue: "1e90ff", - firebrick: "b22222", - floralwhite: "fffaf0", - forestgreen: "228b22", - fuchsia: "f0f", - gainsboro: "dcdcdc", - ghostwhite: "f8f8ff", - gold: "ffd700", - goldenrod: "daa520", - gray: "808080", - green: "008000", - greenyellow: "adff2f", - grey: "808080", - honeydew: "f0fff0", - hotpink: "ff69b4", - indianred: "cd5c5c", - indigo: "4b0082", - ivory: "fffff0", - khaki: "f0e68c", - lavender: "e6e6fa", - lavenderblush: "fff0f5", - lawngreen: "7cfc00", - lemonchiffon: "fffacd", - lightblue: "add8e6", - lightcoral: "f08080", - lightcyan: "e0ffff", - lightgoldenrodyellow: "fafad2", - lightgray: "d3d3d3", - lightgreen: "90ee90", - lightgrey: "d3d3d3", - lightpink: "ffb6c1", - lightsalmon: "ffa07a", - lightseagreen: "20b2aa", - lightskyblue: "87cefa", - lightslategray: "789", - lightslategrey: "789", - lightsteelblue: "b0c4de", - lightyellow: "ffffe0", - lime: "0f0", - limegreen: "32cd32", - linen: "faf0e6", - magenta: "f0f", - maroon: "800000", - mediumaquamarine: "66cdaa", - mediumblue: "0000cd", - mediumorchid: "ba55d3", - mediumpurple: "9370db", - mediumseagreen: "3cb371", - mediumslateblue: "7b68ee", - mediumspringgreen: "00fa9a", - mediumturquoise: "48d1cc", - mediumvioletred: "c71585", - midnightblue: "191970", - mintcream: "f5fffa", - mistyrose: "ffe4e1", - moccasin: "ffe4b5", - navajowhite: "ffdead", - navy: "000080", - oldlace: "fdf5e6", - olive: "808000", - olivedrab: "6b8e23", - orange: "ffa500", - orangered: "ff4500", - orchid: "da70d6", - palegoldenrod: "eee8aa", - palegreen: "98fb98", - paleturquoise: "afeeee", - palevioletred: "db7093", - papayawhip: "ffefd5", - peachpuff: "ffdab9", - peru: "cd853f", - pink: "ffc0cb", - plum: "dda0dd", - powderblue: "b0e0e6", - purple: "800080", - rebeccapurple: "663399", - red: "f00", - rosybrown: "bc8f8f", - royalblue: "4169e1", - saddlebrown: "8b4513", - salmon: "fa8072", - sandybrown: "f4a460", - seagreen: "2e8b57", - seashell: "fff5ee", - sienna: "a0522d", - silver: "c0c0c0", - skyblue: "87ceeb", - slateblue: "6a5acd", - slategray: "708090", - slategrey: "708090", - snow: "fffafa", - springgreen: "00ff7f", - steelblue: "4682b4", - tan: "d2b48c", - teal: "008080", - thistle: "d8bfd8", - tomato: "ff6347", - turquoise: "40e0d0", - violet: "ee82ee", - wheat: "f5deb3", - white: "fff", - whitesmoke: "f5f5f5", - yellow: "ff0", - yellowgreen: "9acd32" - }; + // `mostReadable` + // Given a base color and a list of possible foreground or background + // colors for that base, returns the most readable color. + // *Example* + // tinycolor.mostReadable("#123", ["#fff", "#000"]) => "#000" + tinycolor.mostReadable = function (baseColor, colorList) { + var bestColor = null; + var bestScore = 0; + var bestIsReadable = false; + for (var i = 0; i < colorList.length; i++) { + // We normalize both around the "acceptable" breaking point, + // but rank brightness constrast higher than hue. + + var readability = tinycolor.readability(baseColor, colorList[i]); + var readable = readability.brightness > 125 && readability.color > 500; + var score = 3 * (readability.brightness / 125) + (readability.color / 500); + + if ((readable && ! bestIsReadable) || + (readable && bestIsReadable && score > bestScore) || + ((! readable) && (! bestIsReadable) && score > bestScore)) { + bestIsReadable = readable; + bestScore = score; + bestColor = tinycolor(colorList[i]); + } + } + return bestColor; + }; - // Make it easy to access colors via `hexNames[hex]` - var hexNames = tinycolor.hexNames = flip(names); + // Big List of Colors + // ------------------ + // + var names = tinycolor.names = { + aliceblue: "f0f8ff", + antiquewhite: "faebd7", + aqua: "0ff", + aquamarine: "7fffd4", + azure: "f0ffff", + beige: "f5f5dc", + bisque: "ffe4c4", + black: "000", + blanchedalmond: "ffebcd", + blue: "00f", + blueviolet: "8a2be2", + brown: "a52a2a", + burlywood: "deb887", + burntsienna: "ea7e5d", + cadetblue: "5f9ea0", + chartreuse: "7fff00", + chocolate: "d2691e", + coral: "ff7f50", + cornflowerblue: "6495ed", + cornsilk: "fff8dc", + crimson: "dc143c", + cyan: "0ff", + darkblue: "00008b", + darkcyan: "008b8b", + darkgoldenrod: "b8860b", + darkgray: "a9a9a9", + darkgreen: "006400", + darkgrey: "a9a9a9", + darkkhaki: "bdb76b", + darkmagenta: "8b008b", + darkolivegreen: "556b2f", + darkorange: "ff8c00", + darkorchid: "9932cc", + darkred: "8b0000", + darksalmon: "e9967a", + darkseagreen: "8fbc8f", + darkslateblue: "483d8b", + darkslategray: "2f4f4f", + darkslategrey: "2f4f4f", + darkturquoise: "00ced1", + darkviolet: "9400d3", + deeppink: "ff1493", + deepskyblue: "00bfff", + dimgray: "696969", + dimgrey: "696969", + dodgerblue: "1e90ff", + firebrick: "b22222", + floralwhite: "fffaf0", + forestgreen: "228b22", + fuchsia: "f0f", + gainsboro: "dcdcdc", + ghostwhite: "f8f8ff", + gold: "ffd700", + goldenrod: "daa520", + gray: "808080", + green: "008000", + greenyellow: "adff2f", + grey: "808080", + honeydew: "f0fff0", + hotpink: "ff69b4", + indianred: "cd5c5c", + indigo: "4b0082", + ivory: "fffff0", + khaki: "f0e68c", + lavender: "e6e6fa", + lavenderblush: "fff0f5", + lawngreen: "7cfc00", + lemonchiffon: "fffacd", + lightblue: "add8e6", + lightcoral: "f08080", + lightcyan: "e0ffff", + lightgoldenrodyellow: "fafad2", + lightgray: "d3d3d3", + lightgreen: "90ee90", + lightgrey: "d3d3d3", + lightpink: "ffb6c1", + lightsalmon: "ffa07a", + lightseagreen: "20b2aa", + lightskyblue: "87cefa", + lightslategray: "789", + lightslategrey: "789", + lightsteelblue: "b0c4de", + lightyellow: "ffffe0", + lime: "0f0", + limegreen: "32cd32", + linen: "faf0e6", + magenta: "f0f", + maroon: "800000", + mediumaquamarine: "66cdaa", + mediumblue: "0000cd", + mediumorchid: "ba55d3", + mediumpurple: "9370db", + mediumseagreen: "3cb371", + mediumslateblue: "7b68ee", + mediumspringgreen: "00fa9a", + mediumturquoise: "48d1cc", + mediumvioletred: "c71585", + midnightblue: "191970", + mintcream: "f5fffa", + mistyrose: "ffe4e1", + moccasin: "ffe4b5", + navajowhite: "ffdead", + navy: "000080", + oldlace: "fdf5e6", + olive: "808000", + olivedrab: "6b8e23", + orange: "ffa500", + orangered: "ff4500", + orchid: "da70d6", + palegoldenrod: "eee8aa", + palegreen: "98fb98", + paleturquoise: "afeeee", + palevioletred: "db7093", + papayawhip: "ffefd5", + peachpuff: "ffdab9", + peru: "cd853f", + pink: "ffc0cb", + plum: "dda0dd", + powderblue: "b0e0e6", + purple: "800080", + rebeccapurple: "663399", + red: "f00", + rosybrown: "bc8f8f", + royalblue: "4169e1", + saddlebrown: "8b4513", + salmon: "fa8072", + sandybrown: "f4a460", + seagreen: "2e8b57", + seashell: "fff5ee", + sienna: "a0522d", + silver: "c0c0c0", + skyblue: "87ceeb", + slateblue: "6a5acd", + slategray: "708090", + slategrey: "708090", + snow: "fffafa", + springgreen: "00ff7f", + steelblue: "4682b4", + tan: "d2b48c", + teal: "008080", + thistle: "d8bfd8", + tomato: "ff6347", + turquoise: "40e0d0", + violet: "ee82ee", + wheat: "f5deb3", + white: "fff", + whitesmoke: "f5f5f5", + yellow: "ff0", + yellowgreen: "9acd32" + }; + // Make it easy to access colors via `hexNames[hex]` + var hexNames = tinycolor.hexNames = flip(names); - // Utilities - // --------- + // Utilities + // --------- - // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }` - function flip(o) { - var flipped = { }; - for (var i in o) { - if (o.hasOwnProperty(i)) { - flipped[o[i]] = i; + // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }` + function flip(o) + { + var flipped = { }; + for (var i in o) { + if (o.hasOwnProperty(i)) { + flipped[o[i]] = i; + } } + return flipped; } - return flipped; - } - // Return a valid alpha value [0,1] with all invalid values being set to 1 - function boundAlpha(a) { - a = parseFloat(a); + // Return a valid alpha value [0,1] with all invalid values being set to 1 + function boundAlpha(a) + { + a = parseFloat(a); - if (isNaN(a) || a < 0 || a > 1) { - a = 1; + if (isNaN(a) || a < 0 || a > 1) { + a = 1; + } + + return a; } - return a; - } + // Take input from [0, n] and return it as [0, 1] + function bound01(n, max) + { + if (isOnePointZero(n)) { + n = "100%"; + } - // Take input from [0, n] and return it as [0, 1] - function bound01(n, max) { - if (isOnePointZero(n)) { n = "100%"; } + var processPercent = isPercentage(n); + n = mathMin(max, mathMax(0, parseFloat(n))); - var processPercent = isPercentage(n); - n = mathMin(max, mathMax(0, parseFloat(n))); + // Automatically convert percentage into number + if (processPercent) { + n = parseInt(n * max, 10) / 100; + } - // Automatically convert percentage into number - if (processPercent) { - n = parseInt(n * max, 10) / 100; - } + // Handle floating point rounding errors + if ((math.abs(n - max) < 0.000001)) { + return 1; + } - // Handle floating point rounding errors - if ((math.abs(n - max) < 0.000001)) { - return 1; + // Convert into [0, 1] range if it isn't already + return (n % max) / parseFloat(max); } - // Convert into [0, 1] range if it isn't already - return (n % max) / parseFloat(max); - } + // Force a number between 0 and 1 + function clamp01(val) + { + return mathMin(1, mathMax(0, val)); + } - // Force a number between 0 and 1 - function clamp01(val) { - return mathMin(1, mathMax(0, val)); - } + // Parse a base-16 hex value into a base-10 integer + function parseIntFromHex(val) + { + return parseInt(val, 16); + } - // Parse a base-16 hex value into a base-10 integer - function parseIntFromHex(val) { - return parseInt(val, 16); - } + // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1 + // + function isOnePointZero(n) + { + return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1; + } - // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1 - // - function isOnePointZero(n) { - return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1; - } + // Check to see if string passed in is a percentage + function isPercentage(n) + { + return typeof n === "string" && n.indexOf('%') != -1; + } - // Check to see if string passed in is a percentage - function isPercentage(n) { - return typeof n === "string" && n.indexOf('%') != -1; - } + // Force a hex value to have 2 characters + function pad2(c) + { + return c.length == 1 ? '0' + c : '' + c; + } - // Force a hex value to have 2 characters - function pad2(c) { - return c.length == 1 ? '0' + c : '' + c; - } + // Replace a decimal with it's percentage value + function convertToPercentage(n) + { + if (n <= 1) { + n = (n * 100) + "%"; + } - // Replace a decimal with it's percentage value - function convertToPercentage(n) { - if (n <= 1) { - n = (n * 100) + "%"; + return n; } - return n; - } + // Converts a decimal to a hex value + function convertDecimalToHex(d) + { + return Math.round(parseFloat(d) * 255).toString(16); + } + // Converts a hex value to a decimal + function convertHexToDecimal(h) + { + return (parseIntFromHex(h) / 255); + } - // Converts a decimal to a hex value - function convertDecimalToHex(d) { - return Math.round(parseFloat(d) * 255).toString(16); - } - // Converts a hex value to a decimal - function convertHexToDecimal(h) { - return (parseIntFromHex(h) / 255); - } + var matchers = (function () { + // + var CSS_INTEGER = "[-\\+]?\\d+%?"; - var matchers = (function() { - - // - var CSS_INTEGER = "[-\\+]?\\d+%?"; - - // - var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?"; - - // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome. - var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")"; - - // Actual matching. - // Parentheses and commas are optional, but not required. - // Whitespace can take the place of commas or opening paren - var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; - var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; - - return { - rgb: new RegExp("rgb" + PERMISSIVE_MATCH3), - rgba: new RegExp("rgba" + PERMISSIVE_MATCH4), - hsl: new RegExp("hsl" + PERMISSIVE_MATCH3), - hsla: new RegExp("hsla" + PERMISSIVE_MATCH4), - hsv: new RegExp("hsv" + PERMISSIVE_MATCH3), - hsva: new RegExp("hsva" + PERMISSIVE_MATCH4), - hex3: /^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, - hex6: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/, - hex8: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/ - }; - })(); + // + var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?"; - // `stringInputToObject` - // Permissive string parsing. Take in a number of formats, and output an object - // based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}` - function stringInputToObject(color) { + // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome. + var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")"; - color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase(); - var named = false; - if (names[color]) { - color = names[color]; - named = true; - } - else if (color == 'transparent') { - return { r: 0, g: 0, b: 0, a: 0, format: "name" }; - } + // Actual matching. + // Parentheses and commas are optional, but not required. + // Whitespace can take the place of commas or opening paren + var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; + var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; - // Try to match string input using regular expressions. - // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360] - // Just return an object and let the conversion functions handle that. - // This way the result will be the same whether the tinycolor is initialized with string or object. - var match; - if ((match = matchers.rgb.exec(color))) { - return { r: match[1], g: match[2], b: match[3] }; - } - if ((match = matchers.rgba.exec(color))) { - return { r: match[1], g: match[2], b: match[3], a: match[4] }; - } - if ((match = matchers.hsl.exec(color))) { - return { h: match[1], s: match[2], l: match[3] }; - } - if ((match = matchers.hsla.exec(color))) { - return { h: match[1], s: match[2], l: match[3], a: match[4] }; - } - if ((match = matchers.hsv.exec(color))) { - return { h: match[1], s: match[2], v: match[3] }; - } - if ((match = matchers.hsva.exec(color))) { - return { h: match[1], s: match[2], v: match[3], a: match[4] }; - } - if ((match = matchers.hex8.exec(color))) { return { - a: convertHexToDecimal(match[1]), - r: parseIntFromHex(match[2]), - g: parseIntFromHex(match[3]), - b: parseIntFromHex(match[4]), - format: named ? "name" : "hex8" + rgb: new RegExp("rgb" + PERMISSIVE_MATCH3), + rgba: new RegExp("rgba" + PERMISSIVE_MATCH4), + hsl: new RegExp("hsl" + PERMISSIVE_MATCH3), + hsla: new RegExp("hsla" + PERMISSIVE_MATCH4), + hsv: new RegExp("hsv" + PERMISSIVE_MATCH3), + hsva: new RegExp("hsva" + PERMISSIVE_MATCH4), + hex3: /^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, + hex6: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/, + hex8: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/ }; - } - if ((match = matchers.hex6.exec(color))) { - return { - r: parseIntFromHex(match[1]), - g: parseIntFromHex(match[2]), - b: parseIntFromHex(match[3]), - format: named ? "name" : "hex" - }; - } - if ((match = matchers.hex3.exec(color))) { - return { - r: parseIntFromHex(match[1] + '' + match[1]), - g: parseIntFromHex(match[2] + '' + match[2]), - b: parseIntFromHex(match[3] + '' + match[3]), - format: named ? "name" : "hex" - }; - } - - return false; - } - - window.tinycolor = tinycolor; + })(); + + // `stringInputToObject` + // Permissive string parsing. Take in a number of formats, and output an object + // based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}` + function stringInputToObject(color) + { + color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase(); + var named = false; + if (names[color]) { + color = names[color]; + named = true; + } else if (color == 'transparent') { + return { r: 0, g: 0, b: 0, a: 0, format: "name" }; + } + + // Try to match string input using regular expressions. + // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360] + // Just return an object and let the conversion functions handle that. + // This way the result will be the same whether the tinycolor is initialized with string or object. + var match; + if ((match = matchers.rgb.exec(color))) { + return { r: match[1], g: match[2], b: match[3] }; + } + if ((match = matchers.rgba.exec(color))) { + return { r: match[1], g: match[2], b: match[3], a: match[4] }; + } + if ((match = matchers.hsl.exec(color))) { + return { h: match[1], s: match[2], l: match[3] }; + } + if ((match = matchers.hsla.exec(color))) { + return { h: match[1], s: match[2], l: match[3], a: match[4] }; + } + if ((match = matchers.hsv.exec(color))) { + return { h: match[1], s: match[2], v: match[3] }; + } + if ((match = matchers.hsva.exec(color))) { + return { h: match[1], s: match[2], v: match[3], a: match[4] }; + } + if ((match = matchers.hex8.exec(color))) { + return { + a: convertHexToDecimal(match[1]), + r: parseIntFromHex(match[2]), + g: parseIntFromHex(match[3]), + b: parseIntFromHex(match[4]), + format: named ? "name" : "hex8" + }; + } + if ((match = matchers.hex6.exec(color))) { + return { + r: parseIntFromHex(match[1]), + g: parseIntFromHex(match[2]), + b: parseIntFromHex(match[3]), + format: named ? "name" : "hex" + }; + } + if ((match = matchers.hex3.exec(color))) { + return { + r: parseIntFromHex(match[1] + '' + match[1]), + g: parseIntFromHex(match[2] + '' + match[2]), + b: parseIntFromHex(match[3] + '' + match[3]), + format: named ? "name" : "hex" + }; + } + + return false; + } + + window.tinycolor = tinycolor; })(); $(function () {