diff --git a/src/css/app.css b/src/css/app.css index 34a6e486..7ffa0449 100644 --- a/src/css/app.css +++ b/src/css/app.css @@ -139,6 +139,17 @@ html,body { z-index: 100; } +/* + * MiniColors 2 renders its panel inside the input's own wrapper rather than + * appending it to the body as 1.5 did, so the panel is trapped in the float + * panel's stacking context. The float panels all sit at z-index 1000, and the + * navigator comes after the inspector in the document, so it painted straight + * over the colour grid. Lift whichever panel currently has a picker open. + */ +.float-panel:has(.minicolors-focus) { + z-index: 1001; +} + .buttons-right { position: absolute; right: 0; diff --git a/src/css/minicolors/images/circle.gif b/src/css/minicolors/images/circle.gif deleted file mode 100644 index 599f7f13..00000000 Binary files a/src/css/minicolors/images/circle.gif and /dev/null differ diff --git a/src/css/minicolors/images/gradient.png b/src/css/minicolors/images/gradient.png deleted file mode 100644 index 561cdd9c..00000000 Binary files a/src/css/minicolors/images/gradient.png and /dev/null differ diff --git a/src/css/minicolors/images/line.gif b/src/css/minicolors/images/line.gif deleted file mode 100644 index 9eb19837..00000000 Binary files a/src/css/minicolors/images/line.gif and /dev/null differ diff --git a/src/css/minicolors/images/rainbow.png b/src/css/minicolors/images/rainbow.png deleted file mode 100644 index 8d6c71bf..00000000 Binary files a/src/css/minicolors/images/rainbow.png and /dev/null differ diff --git a/src/css/minicolors/images/trigger.png b/src/css/minicolors/images/trigger.png deleted file mode 100644 index 8c169fd6..00000000 Binary files a/src/css/minicolors/images/trigger.png and /dev/null differ diff --git a/src/css/minicolors/jquery.miniColors.css b/src/css/minicolors/jquery.miniColors.css index 6c6fa5b1..1644897f 100644 --- a/src/css/minicolors/jquery.miniColors.css +++ b/src/css/minicolors/jquery.miniColors.css @@ -1,65 +1,454 @@ -.miniColors-trigger { - height: 22px; - width: 22px; - background: url("images/trigger.png") center no-repeat; - vertical-align: middle; - margin: 0 .25em; - display: inline-block; - outline: none; -} - -.miniColors-selector { - position: absolute; - width: 175px; - height: 150px; - background: #FFF; - border: solid 1px #BBB; - -moz-box-shadow: 0 0 6px rgba(0, 0, 0, .25); - -webkit-box-shadow: 0 0 6px rgba(0, 0, 0, .25); - box-shadow: 0 0 6px rgba(0, 0, 0, .25); - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - border-radius: 5px; - padding: 5px; - z-index: 999999; -} - -.miniColors-selector.black { - background: #000; - border-color: #000; -} - -.miniColors-colors { - position: absolute; - top: 5px; - left: 5px; - width: 150px; - height: 150px; - background: url("images/gradient.png") center no-repeat; - cursor: crosshair; -} - -.miniColors-hues { - position: absolute; - top: 5px; - left: 160px; - width: 20px; - height: 150px; - background: url("images/rainbow.png") center no-repeat; - cursor: crosshair; -} - -.miniColors-colorPicker { - position: absolute; - width: 11px; - height: 11px; - background: url("images/circle.gif") center no-repeat; -} - -.miniColors-huePicker { - position: absolute; - left: -3px; - width: 26px; - height: 3px; - background: url("images/line.gif") center no-repeat; -} \ No newline at end of file +/* + * jQuery MiniColors 2.3.6 -- https://github.com/claviska/jquery-minicolors + * Copyright Cory LaViska, released under the MIT license. + * + * Vendored unchanged apart from this note. The file keeps its historical + * jquery.miniColors.css spelling rather than upstream's jquery.minicolors.css: + * the two differ only in case, which is the same file on a case-insensitive + * filesystem, and eXeLearning links this path from its own editor page. + */ +.minicolors { + position: relative; +} + +.minicolors-sprite { + background-image: url(jquery.minicolors.png); +} + +.minicolors-swatch { + position: absolute; + vertical-align: middle; + background-position: -80px 0; + cursor: text; + padding: 0; + margin: 0; + display: inline-block; +} + +.minicolors-swatch::after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .15); + border-radius: 2px; +} + +.minicolors-swatch-color { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.minicolors input[type=hidden] + .minicolors-swatch { + width: 28px; + position: static; + cursor: pointer; +} + +.minicolors input[type=hidden][disabled] + .minicolors-swatch { + cursor: default; +} + +/* Panel */ +.minicolors-panel { + position: absolute; + width: 173px; + background: white; + border-radius: 2px; + box-shadow: 0 0 20px rgba(0, 0, 0, .2); + z-index: 99999; + box-sizing: content-box; + display: none; + touch-action: none; +} + +.minicolors-panel.minicolors-visible { + display: block; +} + +/* Panel positioning */ +.minicolors-position-top .minicolors-panel { + top: -154px; +} + +.minicolors-position-right .minicolors-panel { + right: 0; +} + +.minicolors-position-bottom .minicolors-panel { + top: auto; +} + +.minicolors-position-left .minicolors-panel { + left: 0; +} + +.minicolors-with-opacity .minicolors-panel { + width: 194px; +} + +.minicolors .minicolors-grid { + position: relative; + top: 1px; + left: 1px; /* LTR */ + width: 150px; + height: 150px; + margin-bottom: 2px; + background-position: -120px 0; + cursor: crosshair; +} +[dir=rtl] .minicolors .minicolors-grid { + right: 1px; +} + +.minicolors .minicolors-grid-inner { + position: absolute; + top: 0; + left: 0; + width: 150px; + height: 150px; +} + +.minicolors-slider-saturation .minicolors-grid { + background-position: -420px 0; +} + +.minicolors-slider-saturation .minicolors-grid-inner { + background-position: -270px 0; + background-image: inherit; +} + +.minicolors-slider-brightness .minicolors-grid { + background-position: -570px 0; +} + +.minicolors-slider-brightness .minicolors-grid-inner { + background-color: black; +} + +.minicolors-slider-wheel .minicolors-grid { + background-position: -720px 0; +} + +.minicolors-slider, +.minicolors-opacity-slider { + position: absolute; + top: 1px; + left: 152px; /* LTR */ + width: 20px; + height: 150px; + background-color: white; + background-position: 0 0; + cursor: row-resize; +} +[dir=rtl] .minicolors-slider, +[dir=rtl] .minicolors-opacity-slider { + right: 152px; +} + +.minicolors-slider-saturation .minicolors-slider { + background-position: -60px 0; +} + +.minicolors-slider-brightness .minicolors-slider { + background-position: -20px 0; +} + +.minicolors-slider-wheel .minicolors-slider { + background-position: -20px 0; +} + +.minicolors-opacity-slider { + left: 173px; /* LTR */ + background-position: -40px 0; + display: none; +} +[dir=rtl] .minicolors-opacity-slider { + right: 173px; +} + +.minicolors-with-opacity .minicolors-opacity-slider { + display: block; +} + +/* Pickers */ +.minicolors-grid .minicolors-picker { + position: absolute; + top: 70px; + left: 70px; + width: 12px; + height: 12px; + box-shadow: 0 0 0 1px rgba(0, 0, 0, .25); + border-radius: 10px; + margin-top: -6px; + margin-left: -6px; + background: none; +} + +.minicolors-grid .minicolors-picker > div { + position: absolute; + top: 0; + left: 0; + width: 8px; + height: 8px; + border-radius: 8px; + border: solid 2px white; + box-sizing: content-box; +} + +.minicolors-picker { + position: absolute; + top: 0; + left: 0; + width: 18px; + height: 3px; + background: white; + box-shadow: 0 0 0 1px rgba(0, 0, 0, .25); + border-radius: 2px; + margin-top: -2px; + margin-left: 1px; + box-sizing: content-box; +} + +/* Swatches */ +.minicolors-swatches, +.minicolors-swatches li { + margin: 5px 0 3px 5px; /* LTR */ + padding: 0; + list-style: none; + overflow: hidden; +} +[dir=rtl] .minicolors-swatches, +[dir=rtl] .minicolors-swatches li { + margin: 5px 5px 3px 0; +} + +.minicolors-swatches .minicolors-swatch { + position: relative; + float: left; /* LTR */ + cursor: pointer; + margin: 0 4px 0 0; /* LTR */ +} +[dir=rtl] .minicolors-swatches .minicolors-swatch { + float: right; + margin: 0 0 0 4px; +} + +.minicolors-with-opacity .minicolors-swatches .minicolors-swatch { + margin-right: 7px; /* LTR */ +} +[dir=rtl] .minicolors-with-opacity .minicolors-swatches .minicolors-swatch { + margin-right: 0; + margin-left: 7px; +} + +.minicolors-swatch.selected { + border-color: #000; +} + +/* Inline controls */ +.minicolors-inline { + display: inline-block; +} + +.minicolors-inline .minicolors-input { + display: none !important; +} + +.minicolors-inline .minicolors-panel { + position: relative; + top: auto; + left: auto; /* LTR */ + box-shadow: none; + z-index: auto; + display: inline-block; +} +[dir=rtl] .minicolors-inline .minicolors-panel { + right: auto; +} + +/* Default theme */ +.minicolors-theme-default .minicolors-swatch { + top: 5px; + left: 5px; /* LTR */ + width: 18px; + height: 18px; +} +[dir=rtl] .minicolors-theme-default .minicolors-swatch { + right: 5px; +} +.minicolors-theme-default .minicolors-swatches .minicolors-swatch { + margin-bottom: 2px; + top: 0; + left: 0; /* LTR */ + width: 18px; + height: 18px; +} +[dir=rtl] .minicolors-theme-default .minicolors-swatches .minicolors-swatch { + right: 0; +} +.minicolors-theme-default.minicolors-position-right .minicolors-swatch { + left: auto; /* LTR */ + right: 5px; /* LTR */ +} +[dir=rtl] .minicolors-theme-default.minicolors-position-left .minicolors-swatch { + right: auto; + left: 5px; +} +.minicolors-theme-default.minicolors { + width: auto; + display: inline-block; +} +.minicolors-theme-default .minicolors-input { + height: 20px; + width: auto; + display: inline-block; + padding-left: 26px; /* LTR */ +} +[dir=rtl] .minicolors-theme-default .minicolors-input { + text-align: right; + unicode-bidi: plaintext; + padding-left: 1px; + padding-right: 26px; +} +.minicolors-theme-default.minicolors-position-right .minicolors-input { + padding-right: 26px; /* LTR */ + padding-left: inherit; /* LTR */ +} +[dir=rtl] .minicolors-theme-default.minicolors-position-left .minicolors-input { + padding-right: inherit; + padding-left: 26px; +} + +/* Bootstrap theme */ +.minicolors-theme-bootstrap .minicolors-swatch { + z-index: 2; + top: 3px; + left: 3px; /* LTR */ + width: 28px; + height: 28px; + border-radius: 2px; +} +[dir=rtl] .minicolors-theme-bootstrap .minicolors-swatch { + right: 3px; +} +.minicolors-theme-bootstrap .minicolors-swatches .minicolors-swatch { + margin-bottom: 2px; + top: 0; + left: 0; /* LTR */ + width: 20px; + height: 20px; +} +[dir=rtl] .minicolors-theme-bootstrap .minicolors-swatches .minicolors-swatch { + right: 0; +} +.minicolors-theme-bootstrap .minicolors-swatch-color { + border-radius: inherit; +} +.minicolors-theme-bootstrap.minicolors-position-right > .minicolors-swatch { + left: auto; /* LTR */ + right: 3px; /* LTR */ +} +[dir=rtl] .minicolors-theme-bootstrap.minicolors-position-left > .minicolors-swatch { + right: auto; + left: 3px; +} +.minicolors-theme-bootstrap .minicolors-input { + float: none; + padding-left: 44px; /* LTR */ +} +[dir=rtl] .minicolors-theme-bootstrap .minicolors-input { + text-align: right; + unicode-bidi: plaintext; + padding-left: 12px; + padding-right: 44px; +} +.minicolors-theme-bootstrap.minicolors-position-right .minicolors-input { + padding-right: 44px; /* LTR */ + padding-left: 12px; /* LTR */ +} +[dir=rtl] .minicolors-theme-bootstrap.minicolors-position-left .minicolors-input { + padding-right: 12px; + padding-left: 44px; +} +.minicolors-theme-bootstrap .minicolors-input.input-lg + .minicolors-swatch { + top: 4px; + left: 4px; /* LTR */ + width: 37px; + height: 37px; + border-radius: 5px; +} +[dir=rtl] .minicolors-theme-bootstrap .minicolors-input.input-lg + .minicolors-swatch { + right: 4px; +} +.minicolors-theme-bootstrap .minicolors-input.input-sm + .minicolors-swatch { + width: 24px; + height: 24px; +} +.minicolors-theme-bootstrap .minicolors-input.input-xs + .minicolors-swatch { + width: 18px; + height: 18px; +} +.input-group .minicolors-theme-bootstrap:not(:first-child) .minicolors-input { + border-top-left-radius: 0; /* LTR */ + border-bottom-left-radius: 0; /* LTR */ +} +[dir=rtl] .input-group .minicolors-theme-bootstrap .minicolors-input { + border-radius: 4px; +} +[dir=rtl] .input-group .minicolors-theme-bootstrap:not(:first-child) .minicolors-input { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +[dir=rtl] .input-group .minicolors-theme-bootstrap:not(:last-child) .minicolors-input { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +/* bootstrap input-group rtl override */ +[dir=rtl] .input-group .form-control, +[dir=rtl] .input-group-addon, +[dir=rtl] .input-group-btn > .btn, +[dir=rtl] .input-group-btn > .btn-group > .btn, +[dir=rtl] .input-group-btn > .dropdown-toggle { + border: 1px solid #ccc; + border-radius: 4px; +} +[dir=rtl] .input-group .form-control:first-child, +[dir=rtl] .input-group-addon:first-child, +[dir=rtl] .input-group-btn:first-child > .btn, +[dir=rtl] .input-group-btn:first-child > .btn-group > .btn, +[dir=rtl] .input-group-btn:first-child > .dropdown-toggle, +[dir=rtl] .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +[dir=rtl] .input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-left: 0; +} +[dir=rtl] .input-group .form-control:last-child, +[dir=rtl] .input-group-addon:last-child, +[dir=rtl] .input-group-btn:last-child > .btn, +[dir=rtl] .input-group-btn:last-child > .btn-group > .btn, +[dir=rtl] .input-group-btn:last-child > .dropdown-toggle, +[dir=rtl] .input-group-btn:first-child > .btn:not(:first-child), +[dir=rtl] .input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +/* Semantic Ui theme */ +.minicolors-theme-semanticui .minicolors-swatch { + top: 0; + left: 0; /* LTR */ + padding: 18px; +} +[dir=rtl] .minicolors-theme-semanticui .minicolors-swatch { + right: 0; +} +.minicolors-theme-semanticui input { + text-indent: 30px; +} diff --git a/src/css/minicolors/jquery.minicolors.png b/src/css/minicolors/jquery.minicolors.png new file mode 100644 index 00000000..bccc2012 Binary files /dev/null and b/src/css/minicolors/jquery.minicolors.png differ diff --git a/src/js/Inspector.js b/src/js/Inspector.js index f2af152f..6cad3f68 100644 --- a/src/js/Inspector.js +++ b/src/js/Inspector.js @@ -52,7 +52,9 @@ mindmaps.InspectorView = function() { }); $allColorpickers.forEach(function($colorpicker) { - $colorpicker.miniColors("disabled", !enabled); + // MiniColors 2 has no "disabled" method: it checks the input's own disabled + // property before opening, so setting that is the supported way. + $colorpicker.prop("disabled", !enabled); }); }; @@ -98,7 +100,7 @@ mindmaps.InspectorView = function() { * @param {String} color */ this.setBranchColorPickerColor = function(color) { - branchColorPicker.miniColors("value", color); + branchColorPicker.minicolors("value", color); }; /** @@ -107,7 +109,7 @@ mindmaps.InspectorView = function() { * @param {String} color */ this.setFontColorPickerColor = function(color) { - fontColorPicker.miniColors("value", color); + fontColorPicker.minicolors("value", color); }; /** @@ -157,39 +159,50 @@ mindmaps.InspectorView = function() { } }); - branchColorPicker.miniColors({ - hide : function(hex) { + // MiniColors 2 renamed "move" to "change" and drops the hex argument from + // "hide", where `this` is now the input element rather than a jQuery object. + branchColorPicker.minicolors({ + // Right-aligned so the panel stays inside the inspector. Left-aligned it + // spills past the panel edge and the canvas paints over the colour grid. + position : 'bottom right', + + hide : function() { + var $input = $(this); // dont emit event if picker was hidden due to disable - if (this.attr('disabled')) { + if ($input.prop('disabled')) { return; } - console.log("hide branch", hex); if (self.branchColorPicked) { - self.branchColorPicked(hex); + self.branchColorPicked($input.val()); } }, - move : function(hex) { + change : function(hex) { if (self.branchColorPreview) { self.branchColorPreview(hex); } } }); - fontColorPicker.miniColors({ - hide : function(hex) { + fontColorPicker.minicolors({ + // Right-aligned so the panel stays inside the inspector. Left-aligned it + // spills past the panel edge and the canvas paints over the colour grid. + position : 'bottom right', + + hide : function() { + var $input = $(this); // dont emit event if picker was hidden due to disable - if (this.attr('disabled')) { + if ($input.prop('disabled')) { return; } - console.log("font", hex); + if (self.fontColorPicked) { - self.fontColorPicked(hex); + self.fontColorPicked($input.val()); } }, - move: function(hex) { + change: function(hex) { if (self.fontColorPreview) { self.fontColorPreview(hex); } diff --git a/src/js/libs/jquery.minicolors.js b/src/js/libs/jquery.minicolors.js index d06ac82e..946a4c85 100644 --- a/src/js/libs/jquery.minicolors.js +++ b/src/js/libs/jquery.minicolors.js @@ -1,679 +1,1136 @@ -/*! - * jQuery miniColors: A small color selector - * - * Copyright 2011 Cory LaViska for A Beautiful Site, LLC. (http://abeautifulsite.net/) - * - * Dual licensed under the MIT or GPL Version 2 licenses - * - */ -/* - * Usage: - * - * 1. Link to jQuery: - * - * 2. Link to miniColors: - * - * 3. Include miniColors stylesheet: - * - * 4. Apply $([selector]).miniColors() to one or more INPUT elements - * - * - * Options: - * - * disabled [true|false] - * readonly [true|false] - * - * - * Specify options on creation: - * - * $([selector]).miniColors({ - * - * optionName: value, - * optionName: value, - * ... - * - * }); - * - * - * Methods: - * - * Call a method using: $([selector]).miniColors('methodName', [value]); - * - * disabled [true|false] - * readonly [true|false] - * value [hex value] - * destroy - * - * - * Events: - * - * Attach events on creation: - * - * $([selector]).miniColors({ - * - * change: function(hex, rgb) { ... } - * - * }); - * - * change(hex, rgb) called when the color value changes; 'this' will refer to the original input element; - * hex is the string hex value of the selected color; rgb is an object with the RGB values - * - * - * Change log: - * - * - v0.1 (2011-02-24) - Initial release - * - * - * Attribution: - * - * - The color picker icon is based on an icon from the amazing Fugue icon set: - * http://p.yusukekamiyamane.com/ - * - * - The gradient image, the hue image, and the math functions are courtesy of - * the eyecon.co jQuery color picker: http://www.eyecon.ro/colorpicker/ - * - * -*/ - - -/* - * Changes by David (20/05/2011): - * - added hide callback - * - center colorpicker beneath trigger element (hardcoded) - * - add invisible overlay - */ - -/* - * Changes by David (25/04/2012): - * - Add move callback - */ -if(jQuery) (function($) { - - $.extend($.fn, { - - miniColors: function(o, data) { - - - var create = function(input, o, data) { - - // - // Creates a new instance of the miniColors selector - // - - // Determine initial color (defaults to white) - var color = cleanHex(input.val()); - if( !color ) color = 'FFFFFF'; - var hsb = hex2hsb(color); - - // Create trigger - var trigger = $(''); - trigger.insertAfter(input); - - // Add necessary attributes - input.addClass('miniColors').attr('maxlength', 7).attr('autocomplete', 'off'); - - // Set input data - input.data('trigger', trigger); - input.data('hsb', hsb); - if( o.change ) input.data('change', o.change); - if (o.hide) input.data('hide', o.hide); - if (o.move) input.data('move', o.move); - - // Handle options - if( o.readonly ) input.attr('readonly', true); - if( o.disabled ) disable(input); - - // Show selector when trigger is clicked - trigger.bind('click.miniColors', function(event) { - event.preventDefault(); - input.trigger('focus'); - }); - - // Show selector when input receives focus - input.bind('focus.miniColors', function(event) { - show(input); - }); - - // Hide on blur - input.bind('blur.miniColors', function(event) { - var hex = cleanHex(input.val()); - input.val( hex ? '#' + hex : '' ); - }); - - // Hide when tabbing out of the input - input.bind('keydown.miniColors', function(event) { - if( event.keyCode === 9 ) hide(input); - }); - - // Update when color is typed in - input.bind('keyup.miniColors', function(event) { - // Remove non-hex characters - var filteredHex = input.val().replace(/[^A-F0-9#]/ig, ''); - input.val(filteredHex); - if( !setColorFromInput(input) ) { - // Reset trigger color when color is invalid - input.data('trigger').css('backgroundColor', '#FFF'); - } - }); - - // Handle pasting - input.bind('paste.miniColors', function(event) { - // Short pause to wait for paste to complete - setTimeout( function() { - input.trigger('keyup'); - }, 5); - }); - - }; - - - var destroy = function(input) { - - // - // Destroys an active instance of the miniColors selector - // - - hide(); - - input = $(input); - input.data('trigger').remove(); - input.removeAttr('autocomplete'); - input.removeData('trigger'); - input.removeData('selector'); - input.removeData('hsb'); - input.removeData('huePicker'); - input.removeData('colorPicker'); - input.removeData('mousebutton'); - input.removeData('moving'); - input.unbind('click.miniColors'); - input.unbind('focus.miniColors'); - input.unbind('blur.miniColors'); - input.unbind('keyup.miniColors'); - input.unbind('keydown.miniColors'); - input.unbind('paste.miniColors'); - - }; - - - var enable = function(input) { - - // - // Disables the input control and the selector - // - - input.attr('disabled', false); - input.data('trigger').css('opacity', 1); - - }; - - - var disable = function(input) { - - // - // Disables the input control and the selector - // - - - input.attr('disabled', true); - input.data('trigger').css('opacity', .5); - hide(input); - - }; - - - var show = function(input) { - - // - // Shows the miniColors selector - // - - if( input.attr('disabled') ) return false; - - // Hide all other instances - hide(); - - // Generate the selector - var selector = $('
'); - selector.append('
'); - selector.append('
'); - selector.css({ - top: input.is(':visible') ? input.offset().top + input.outerHeight() : input.data('trigger').offset().top + input.data('trigger').outerHeight(), - left: input.is(':visible') ? input.offset().left : input.data('trigger').offset().left - /** david: hack! - align middle*/ 87, - display: 'none' - }).addClass( input.attr('class') ); - - // Set background for colors - var hsb = input.data('hsb'); - selector.find('.miniColors-colors').css('backgroundColor', '#' + hsb2hex({ h: hsb.h, s: 100, b: 100 })); - - // Set colorPicker position - var colorPosition = input.data('colorPosition'); - if( !colorPosition ) colorPosition = getColorPositionFromHSB(hsb); - selector.find('.miniColors-colorPicker').css('top', colorPosition.y + 'px').css('left', colorPosition.x + 'px'); - - // Set huePicker position - var huePosition = input.data('huePosition'); - if( !huePosition ) huePosition = getHuePositionFromHSB(hsb); - selector.find('.miniColors-huePicker').css('top', huePosition.y + 'px'); - - - // Set input data - input.data('selector', selector); - input.data('huePicker', selector.find('.miniColors-huePicker')); - input.data('colorPicker', selector.find('.miniColors-colorPicker')); - input.data('mousebutton', 0); - - // invisible overlay accept clicks outside the color picker area - var $overlay = $("
").css({ - position: "absolute", - left: 0, - top: 0, - width: "100%", - height: "100%", - "z-index": 10000 - }).appendTo("BODY").mousedown(function(){ - hide(input); - $overlay.remove(); - }); - - $('BODY').append(selector); - - - - selector.fadeIn(100); - - // Prevent text selection in IE - selector.bind('selectstart', function() { return false; }); - - selector.bind('mousedown.miniColors', function(event) { - input.data('mousebutton', 1); - - if( $(event.target).hasClass('miniColors-colors') ) { - event.preventDefault(); - input.data('moving', 'colors'); - moveColor(input, event); - } - - if( $(event.target).hasClass('miniColors-hues') ) { - event.preventDefault(); - input.data('moving', 'hues'); - moveHue(input, event); - } - - if (input.data('move')) { - input.data('move').call(input, input.val()); - } - }); - - selector.bind('mouseup.miniColors', function(event) { - input.data('mousebutton', 0); - input.removeData('moving'); - }); - - selector.bind('mousemove.miniColors', function(event) { - if( input.data('mousebutton') === 1 ) { - if( input.data('moving') === 'colors' ) moveColor(input, event); - if( input.data('moving') === 'hues' ) moveHue(input, event); - - if (input.data('move')) { - input.data('move').call(input, input.val()); - } - } - }); - - }; - - - var hide = function(input) { - - // call hide callback - if (input) { - if( input.data('hide') ) { - input.data('hide').call(input, input.val()); - } - } - // - // Hides one or more miniColors selectors - // - - // Hide all other instances if input isn't specified - if( !input ) input = '.miniColors'; - - $(input).each( function() { - var selector = $(this).data('selector'); - $(this).removeData('selector'); - $(selector).fadeOut(100, function() { - $(this).remove(); - }); - }); - }; - - - var moveColor = function(input, event) { - - var colorPicker = input.data('colorPicker'); - - colorPicker.hide(); - - var position = { - x: event.clientX - input.data('selector').find('.miniColors-colors').offset().left + $(document).scrollLeft() - 5, - y: event.clientY - input.data('selector').find('.miniColors-colors').offset().top + $(document).scrollTop() - 5 - }; - - if( position.x <= -5 ) position.x = -5; - if( position.x >= 144 ) position.x = 144; - if( position.y <= -5 ) position.y = -5; - if( position.y >= 144 ) position.y = 144; - input.data('colorPosition', position); - colorPicker.css('left', position.x).css('top', position.y).show(); - - // Calculate saturation - var s = Math.round((position.x + 5) * .67); - if( s < 0 ) s = 0; - if( s > 100 ) s = 100; - - // Calculate brightness - var b = 100 - Math.round((position.y + 5) * .67); - if( b < 0 ) b = 0; - if( b > 100 ) b = 100; - - // Update HSB values - var hsb = input.data('hsb'); - hsb.s = s; - hsb.b = b; - - // Set color - setColor(input, hsb, true); - - }; - - - var moveHue = function(input, event) { - - var huePicker = input.data('huePicker'); - - huePicker.hide(); - - var position = { - y: event.clientY - input.data('selector').find('.miniColors-colors').offset().top + $(document).scrollTop() - 1 - }; - - if( position.y <= -1 ) position.y = -1; - if( position.y >= 149 ) position.y = 149; - input.data('huePosition', position); - huePicker.css('top', position.y).show(); - - // Calculate hue - var h = Math.round((150 - position.y - 1) * 2.4); - if( h < 0 ) h = 0; - if( h > 360 ) h = 360; - - // Update HSB values - var hsb = input.data('hsb'); - hsb.h = h; - - // Set color - setColor(input, hsb, true); - - }; - - - var setColor = function(input, hsb, updateInputValue) { - - input.data('hsb', hsb); - var hex = hsb2hex(hsb); - if( updateInputValue ) input.val('#' + hex); - input.data('trigger').css('backgroundColor', '#' + hex); - if( input.data('selector') ) input.data('selector').find('.miniColors-colors').css('backgroundColor', '#' + hsb2hex({ h: hsb.h, s: 100, b: 100 })); - - if( input.data('change') ) { - input.data('change').call(input, '#' + hex, hsb2rgb(hsb)); - } - - }; - - - var setColorFromInput = function(input) { - - // Don't update if the hex color is invalid - var hex = cleanHex(input.val()); - if( !hex ) return false; - - // Get HSB equivalent - var hsb = hex2hsb(hex); - - // If color is the same, no change required - var currentHSB = input.data('hsb'); - if( hsb.h === currentHSB.h && hsb.s === currentHSB.s && hsb.b === currentHSB.b ) return true; - - // Set colorPicker position - var colorPosition = getColorPositionFromHSB(hsb); - var colorPicker = $(input.data('colorPicker')); - colorPicker.css('top', colorPosition.y + 'px').css('left', colorPosition.x + 'px'); - - // Set huePosition position - var huePosition = getHuePositionFromHSB(hsb); - var huePicker = $(input.data('huePicker')); - huePicker.css('top', huePosition.y + 'px'); - - setColor(input, hsb, false); - - return true; - - }; - - - var getColorPositionFromHSB = function(hsb) { - - var x = Math.ceil(hsb.s / .67); - if( x < 0 ) x = 0; - if( x > 150 ) x = 150; - - var y = 150 - Math.ceil(hsb.b / .67); - if( y < 0 ) y = 0; - if( y > 150 ) y = 150; - - return { x: x - 5, y: y - 5 }; - - } - - - var getHuePositionFromHSB = function(hsb) { - - var y = 150 - (hsb.h / 2.4); - if( y < 0 ) h = 0; - if( y > 150 ) h = 150; - - return { y: y - 1 }; - - } - - - var cleanHex = function(hex) { - - // - // Turns a dirty hex string into clean, 6-character hex color - // - - hex = hex.replace(/[^A-Fa-f0-9]/, ''); - - if( hex.length == 3 ) { - hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; - } - - return hex.length === 6 ? hex : null; - - }; - - - var hsb2rgb = function(hsb) { - var rgb = {}; - var h = Math.round(hsb.h); - var s = Math.round(hsb.s*255/100); - var v = Math.round(hsb.b*255/100); - if(s == 0) { - rgb.r = rgb.g = rgb.b = v; - } else { - var t1 = v; - var t2 = (255 - s) * v / 255; - var t3 = (t1 - t2) * (h % 60) / 60; - if( h == 360 ) h = 0; - if( h < 60 ) { rgb.r = t1; rgb.b = t2; rgb.g = t2 + t3; } - else if( h<120 ) {rgb.g = t1; rgb.b = t2; rgb.r = t1 - t3; } - else if( h<180 ) {rgb.g = t1; rgb.r = t2; rgb.b = t2 + t3; } - else if( h<240 ) {rgb.b = t1; rgb.r = t2; rgb.g = t1 - t3; } - else if( h<300 ) {rgb.b = t1; rgb.g = t2; rgb.r = t2 + t3; } - else if( h<360 ) {rgb.r = t1; rgb.g = t2; rgb.b = t1 - t3; } - else { rgb.r = 0; rgb.g = 0; rgb.b = 0; } - } - return { - r: Math.round(rgb.r), - g: Math.round(rgb.g), - b: Math.round(rgb.b) - }; - }; - - - var rgb2hex = function(rgb) { - - var hex = [ - rgb.r.toString(16), - rgb.g.toString(16), - rgb.b.toString(16) - ]; - $.each(hex, function(nr, val) { - if (val.length == 1) hex[nr] = '0' + val; - }); - - return hex.join(''); - }; - - - var hex2rgb = function(hex) { - var hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16); - - return { - r: hex >> 16, - g: (hex & 0x00FF00) >> 8, - b: (hex & 0x0000FF) - }; - }; - - - var rgb2hsb = function(rgb) { - var hsb = { h: 0, s: 0, b: 0 }; - var min = Math.min(rgb.r, rgb.g, rgb.b); - var max = Math.max(rgb.r, rgb.g, rgb.b); - var delta = max - min; - hsb.b = max; - hsb.s = max != 0 ? 255 * delta / max : 0; - if( hsb.s != 0 ) { - if( rgb.r == max ) { - hsb.h = (rgb.g - rgb.b) / delta; - } else if( rgb.g == max ) { - hsb.h = 2 + (rgb.b - rgb.r) / delta; - } else { - hsb.h = 4 + (rgb.r - rgb.g) / delta; - } - } else { - hsb.h = -1; - } - hsb.h *= 60; - if( hsb.h < 0 ) { - hsb.h += 360; - } - hsb.s *= 100/255; - hsb.b *= 100/255; - return hsb; - }; - - - var hex2hsb = function(hex) { - var hsb = rgb2hsb(hex2rgb(hex)); - // Zero out hue marker for black, white, and grays (saturation === 0) - if( hsb.s === 0 ) hsb.h = 360; - return hsb; - }; - - - var hsb2hex = function(hsb) { - return rgb2hex(hsb2rgb(hsb)); - }; - - - // - // Handle calls to $([selector]).miniColors() - // - switch(o) { - - case 'readonly': - - $(this).each( function() { - $(this).attr('readonly', data); - }); - - return $(this); - - break; - - case 'disabled': - - $(this).each( function() { - if( data ) { - disable($(this)); - } else { - enable($(this)); - } - }); - - return $(this); - - case 'value': - - $(this).each( function() { - $(this).val(data).trigger('keyup'); - }); - - return $(this); - - break; - - case 'destroy': - - $(this).each( function() { - destroy($(this)); - }); - - return $(this); - - default: - - if( !o ) o = {}; - - $(this).each( function() { - - // Must be called on an input element - if( $(this)[0].tagName.toLowerCase() !== 'input' ) return; - - // If a trigger is present, the control was already created - if( $(this).data('trigger') ) return; - - // Create the control - create($(this), o, data); - - }); - - return $(this); - - } - - - } - - - }); - -})(jQuery); - - - +/*! + * jQuery MiniColors 2.3.6 + * https://github.com/claviska/jquery-minicolors + * + * Vendored from @claviska/jquery-minicolors@2.3.6, unchanged apart from this + * note, which records the version the upstream file itself does not carry. + * Copyright Cory LaViska for A Beautiful Site, LLC -- MIT licensed. + */ +// +// jQuery MiniColors: A tiny color picker built on jQuery +// +// Developed by Cory LaViska for A Beautiful Site, LLC +// +// Licensed under the MIT license: http://opensource.org/licenses/MIT +// +(function (factory) { + if(typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory); + } else if(typeof exports === 'object') { + // Node/CommonJS + module.exports = factory(require('jquery')); + } else { + // Browser globals + factory(jQuery); + } +}(function ($) { + 'use strict'; + + // Defaults + $.minicolors = { + defaults: { + animationSpeed: 50, + animationEasing: 'swing', + change: null, + changeDelay: 0, + control: 'hue', + defaultValue: '', + format: 'hex', + hide: null, + hideSpeed: 100, + inline: false, + keywords: '', + letterCase: 'lowercase', + opacity: false, + position: 'bottom', + show: null, + showSpeed: 100, + theme: 'default', + swatches: [] + } + }; + + // Public methods + $.extend($.fn, { + minicolors: function(method, data) { + + switch(method) { + // Destroy the control + case 'destroy': + $(this).each(function() { + destroy($(this)); + }); + return $(this); + + // Hide the color picker + case 'hide': + hide(); + return $(this); + + // Get/set opacity + case 'opacity': + // Getter + if(data === undefined) { + // Getter + return $(this).attr('data-opacity'); + } else { + // Setter + $(this).each(function() { + updateFromInput($(this).attr('data-opacity', data)); + }); + } + return $(this); + + // Get an RGB(A) object based on the current color/opacity + case 'rgbObject': + return rgbObject($(this), method === 'rgbaObject'); + + // Get an RGB(A) string based on the current color/opacity + case 'rgbString': + case 'rgbaString': + return rgbString($(this), method === 'rgbaString'); + + // Get/set settings on the fly + case 'settings': + if(data === undefined) { + return $(this).data('minicolors-settings'); + } else { + // Setter + $(this).each(function() { + var settings = $(this).data('minicolors-settings') || {}; + destroy($(this)); + $(this).minicolors($.extend(true, settings, data)); + }); + } + return $(this); + + // Show the color picker + case 'show': + show($(this).eq(0)); + return $(this); + + // Get/set the hex color value + case 'value': + if(data === undefined) { + // Getter + return $(this).val(); + } else { + // Setter + $(this).each(function() { + if(typeof(data) === 'object' && data !== null) { + if(data.opacity !== undefined) { + $(this).attr('data-opacity', keepWithin(data.opacity, 0, 1)); + } + if(data.color) { + $(this).val(data.color); + } + } else { + $(this).val(data); + } + updateFromInput($(this)); + }); + } + return $(this); + + // Initializes the control + default: + if(method !== 'create') data = method; + $(this).each(function() { + init($(this), data); + }); + return $(this); + + } + + } + }); + + // Initialize input elements + function init(input, settings) { + var minicolors = $('
'); + var defaults = $.minicolors.defaults; + var name; + var size; + var swatches; + var swatch; + var swatchString; + var panel; + var i; + + // Do nothing if already initialized + if(input.data('minicolors-initialized')) return; + + // Handle settings + settings = $.extend(true, {}, defaults, settings); + + // The wrapper + minicolors + .addClass('minicolors-theme-' + settings.theme) + .toggleClass('minicolors-with-opacity', settings.opacity); + + // Custom positioning + if(settings.position !== undefined) { + $.each(settings.position.split(' '), function() { + minicolors.addClass('minicolors-position-' + this); + }); + } + + // Input size + if(settings.format === 'rgb') { + size = settings.opacity ? '25' : '20'; + } else { + size = settings.keywords ? '11' : '7'; + } + + // The input + input + .addClass('minicolors-input') + .data('minicolors-initialized', false) + .data('minicolors-settings', settings) + .prop('size', size) + .wrap(minicolors) + .after( + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + ); + + // The swatch + if(!settings.inline) { + input.after(''); + input.next('.minicolors-input-swatch').on('click', function(event) { + event.preventDefault(); + input.trigger('focus'); + }); + } + + // Prevent text selection in IE + panel = input.parent().find('.minicolors-panel'); + panel.on('selectstart', function() { return false; }).end(); + + // Swatches + if(settings.swatches && settings.swatches.length !== 0) { + panel.addClass('minicolors-with-swatches'); + swatches = $('') + .appendTo(panel); + for(i = 0; i < settings.swatches.length; ++i) { + // allow for custom objects as swatches + if(typeof settings.swatches[i] === 'object') { + name = settings.swatches[i].name; + swatch = settings.swatches[i].color; + } else { + name = ''; + swatch = settings.swatches[i]; + } + swatchString = swatch; + swatch = isRgb(swatch) ? parseRgb(swatch, true) : hex2rgb(parseHex(swatch, true)); + $('
  • ') + .attr("title", name) + .appendTo(swatches) + .data('swatch-color', swatchString) + .find('.minicolors-swatch-color') + .css({ + backgroundColor: ((swatchString !== 'transparent') ? rgb2hex(swatch) : 'transparent'), + opacity: String(swatch.a) + }); + settings.swatches[i] = swatch; + } + } + + // Inline controls + if(settings.inline) input.parent().addClass('minicolors-inline'); + + updateFromInput(input, false); + + input.data('minicolors-initialized', true); + } + + // Returns the input back to its original state + function destroy(input) { + var minicolors = input.parent(); + + // Revert the input element + input + .removeData('minicolors-initialized') + .removeData('minicolors-settings') + .removeProp('size') + .removeClass('minicolors-input'); + + // Remove the wrap and destroy whatever remains + minicolors.before(input).remove(); + } + + // Shows the specified dropdown panel + function show(input) { + var minicolors = input.parent(); + var panel = minicolors.find('.minicolors-panel'); + var settings = input.data('minicolors-settings'); + + // Do nothing if uninitialized, disabled, inline, or already open + if( + !input.data('minicolors-initialized') || + input.prop('disabled') || + minicolors.hasClass('minicolors-inline') || + minicolors.hasClass('minicolors-focus') + ) return; + + hide(); + + minicolors.addClass('minicolors-focus'); + if (panel.animate) { + panel + .stop(true, true) + .fadeIn(settings.showSpeed, function () { + if (settings.show) settings.show.call(input.get(0)); + }); + } else { + panel.show(); + if (settings.show) settings.show.call(input.get(0)); + } + } + + // Hides all dropdown panels + function hide() { + $('.minicolors-focus').each(function() { + var minicolors = $(this); + var input = minicolors.find('.minicolors-input'); + var panel = minicolors.find('.minicolors-panel'); + var settings = input.data('minicolors-settings'); + + if (panel.animate) { + panel.fadeOut(settings.hideSpeed, function () { + if (settings.hide) settings.hide.call(input.get(0)); + minicolors.removeClass('minicolors-focus'); + }); + } else { + panel.hide(); + if (settings.hide) settings.hide.call(input.get(0)); + minicolors.removeClass('minicolors-focus'); + } + }); + } + + // Moves the selected picker + function move(target, event, animate) { + var input = target.parents('.minicolors').find('.minicolors-input'); + var settings = input.data('minicolors-settings'); + var picker = target.find('[class$=-picker]'); + var offsetX = target.offset().left; + var offsetY = target.offset().top; + var x = Math.round(event.pageX - offsetX); + var y = Math.round(event.pageY - offsetY); + var duration = animate ? settings.animationSpeed : 0; + var wx, wy, r, phi, styles; + + // Touch support + if(event.originalEvent.changedTouches) { + x = event.originalEvent.changedTouches[0].pageX - offsetX; + y = event.originalEvent.changedTouches[0].pageY - offsetY; + } + + // Constrain picker to its container + if(x < 0) x = 0; + if(y < 0) y = 0; + if(x > target.width()) x = target.width(); + if(y > target.height()) y = target.height(); + + // Constrain color wheel values to the wheel + if(target.parent().is('.minicolors-slider-wheel') && picker.parent().is('.minicolors-grid')) { + wx = 75 - x; + wy = 75 - y; + r = Math.sqrt(wx * wx + wy * wy); + phi = Math.atan2(wy, wx); + if(phi < 0) phi += Math.PI * 2; + if(r > 75) { + r = 75; + x = 75 - (75 * Math.cos(phi)); + y = 75 - (75 * Math.sin(phi)); + } + x = Math.round(x); + y = Math.round(y); + } + + // Move the picker + styles = { + top: y + 'px' + }; + if(target.is('.minicolors-grid')) { + styles.left = x + 'px'; + } + if (picker.animate) { + picker + .stop(true) + .animate(styles, duration, settings.animationEasing, function() { + updateFromControl(input, target); + }); + } else { + picker + .css(styles); + updateFromControl(input, target); + } + } + + // Sets the input based on the color picker values + function updateFromControl(input, target) { + + function getCoords(picker, container) { + var left, top; + if(!picker.length || !container) return null; + left = picker.offset().left; + top = picker.offset().top; + + return { + x: left - container.offset().left + (picker.outerWidth() / 2), + y: top - container.offset().top + (picker.outerHeight() / 2) + }; + } + + var hue, saturation, brightness, x, y, r, phi; + var hex = input.val(); + var opacity = input.attr('data-opacity'); + + // Helpful references + var minicolors = input.parent(); + var settings = input.data('minicolors-settings'); + var swatch = minicolors.find('.minicolors-input-swatch'); + + // Panel objects + var grid = minicolors.find('.minicolors-grid'); + var slider = minicolors.find('.minicolors-slider'); + var opacitySlider = minicolors.find('.minicolors-opacity-slider'); + + // Picker objects + var gridPicker = grid.find('[class$=-picker]'); + var sliderPicker = slider.find('[class$=-picker]'); + var opacityPicker = opacitySlider.find('[class$=-picker]'); + + // Picker positions + var gridPos = getCoords(gridPicker, grid); + var sliderPos = getCoords(sliderPicker, slider); + var opacityPos = getCoords(opacityPicker, opacitySlider); + + // Handle colors + if(target.is('.minicolors-grid, .minicolors-slider, .minicolors-opacity-slider')) { + + // Determine HSB values + switch(settings.control) { + case 'wheel': + // Calculate hue, saturation, and brightness + x = (grid.width() / 2) - gridPos.x; + y = (grid.height() / 2) - gridPos.y; + r = Math.sqrt(x * x + y * y); + phi = Math.atan2(y, x); + if(phi < 0) phi += Math.PI * 2; + if(r > 75) { + r = 75; + gridPos.x = 69 - (75 * Math.cos(phi)); + gridPos.y = 69 - (75 * Math.sin(phi)); + } + saturation = keepWithin(r / 0.75, 0, 100); + hue = keepWithin(phi * 180 / Math.PI, 0, 360); + brightness = keepWithin(100 - Math.floor(sliderPos.y * (100 / slider.height())), 0, 100); + hex = hsb2hex({ + h: hue, + s: saturation, + b: brightness + }); + + // Update UI + slider.css('backgroundColor', hsb2hex({ h: hue, s: saturation, b: 100 })); + break; + + case 'saturation': + // Calculate hue, saturation, and brightness + hue = keepWithin(parseInt(gridPos.x * (360 / grid.width()), 10), 0, 360); + saturation = keepWithin(100 - Math.floor(sliderPos.y * (100 / slider.height())), 0, 100); + brightness = keepWithin(100 - Math.floor(gridPos.y * (100 / grid.height())), 0, 100); + hex = hsb2hex({ + h: hue, + s: saturation, + b: brightness + }); + + // Update UI + slider.css('backgroundColor', hsb2hex({ h: hue, s: 100, b: brightness })); + minicolors.find('.minicolors-grid-inner').css('opacity', saturation / 100); + break; + + case 'brightness': + // Calculate hue, saturation, and brightness + hue = keepWithin(parseInt(gridPos.x * (360 / grid.width()), 10), 0, 360); + saturation = keepWithin(100 - Math.floor(gridPos.y * (100 / grid.height())), 0, 100); + brightness = keepWithin(100 - Math.floor(sliderPos.y * (100 / slider.height())), 0, 100); + hex = hsb2hex({ + h: hue, + s: saturation, + b: brightness + }); + + // Update UI + slider.css('backgroundColor', hsb2hex({ h: hue, s: saturation, b: 100 })); + minicolors.find('.minicolors-grid-inner').css('opacity', 1 - (brightness / 100)); + break; + + default: + // Calculate hue, saturation, and brightness + hue = keepWithin(360 - parseInt(sliderPos.y * (360 / slider.height()), 10), 0, 360); + saturation = keepWithin(Math.floor(gridPos.x * (100 / grid.width())), 0, 100); + brightness = keepWithin(100 - Math.floor(gridPos.y * (100 / grid.height())), 0, 100); + hex = hsb2hex({ + h: hue, + s: saturation, + b: brightness + }); + + // Update UI + grid.css('backgroundColor', hsb2hex({ h: hue, s: 100, b: 100 })); + break; + } + + // Handle opacity + if(settings.opacity) { + opacity = parseFloat(1 - (opacityPos.y / opacitySlider.height())).toFixed(2); + } else { + opacity = 1; + } + + updateInput(input, hex, opacity); + } + else { + // Set swatch color + swatch.find('span').css({ + backgroundColor: hex, + opacity: String(opacity) + }); + + // Handle change event + doChange(input, hex, opacity); + } + } + + // Sets the value of the input and does the appropriate conversions + // to respect settings, also updates the swatch + function updateInput(input, value, opacity) { + var rgb; + + // Helpful references + var minicolors = input.parent(); + var settings = input.data('minicolors-settings'); + var swatch = minicolors.find('.minicolors-input-swatch'); + + if(settings.opacity) input.attr('data-opacity', opacity); + + // Set color string + if(settings.format === 'rgb') { + // Returns RGB(A) string + + // Checks for input format and does the conversion + if(isRgb(value)) { + rgb = parseRgb(value, true); + } + else { + rgb = hex2rgb(parseHex(value, true)); + } + + opacity = input.attr('data-opacity') === '' ? 1 : keepWithin(parseFloat(input.attr('data-opacity')).toFixed(2), 0, 1); + if(isNaN(opacity) || !settings.opacity) opacity = 1; + + if(input.minicolors('rgbObject').a <= 1 && rgb && settings.opacity) { + // Set RGBA string if alpha + value = 'rgba(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ', ' + parseFloat(opacity) + ')'; + } else { + // Set RGB string (alpha = 1) + value = 'rgb(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ')'; + } + } else { + // Returns hex color + + // Checks for input format and does the conversion + if(isRgb(value)) { + value = rgbString2hex(value); + } + + value = convertCase(value, settings.letterCase); + } + + // Update value from picker + input.val(value); + + // Set swatch color + swatch.find('span').css({ + backgroundColor: value, + opacity: String(opacity) + }); + + // Handle change event + doChange(input, value, opacity); + } + + // Sets the color picker values from the input + function updateFromInput(input, preserveInputValue) { + var hex, hsb, opacity, keywords, alpha, value, x, y, r, phi; + + // Helpful references + var minicolors = input.parent(); + var settings = input.data('minicolors-settings'); + var swatch = minicolors.find('.minicolors-input-swatch'); + + // Panel objects + var grid = minicolors.find('.minicolors-grid'); + var slider = minicolors.find('.minicolors-slider'); + var opacitySlider = minicolors.find('.minicolors-opacity-slider'); + + // Picker objects + var gridPicker = grid.find('[class$=-picker]'); + var sliderPicker = slider.find('[class$=-picker]'); + var opacityPicker = opacitySlider.find('[class$=-picker]'); + + // Determine hex/HSB values + if(isRgb(input.val())) { + // If input value is a rgb(a) string, convert it to hex color and update opacity + hex = rgbString2hex(input.val()); + alpha = keepWithin(parseFloat(getAlpha(input.val())).toFixed(2), 0, 1); + if(alpha) { + input.attr('data-opacity', alpha); + } + } else { + hex = convertCase(parseHex(input.val(), true), settings.letterCase); + } + + if(!hex){ + hex = convertCase(parseInput(settings.defaultValue, true), settings.letterCase); + } + hsb = hex2hsb(hex); + + // Get array of lowercase keywords + keywords = !settings.keywords ? [] : $.map(settings.keywords.split(','), function(a) { + return a.toLowerCase().trim(); + }); + + // Set color string + if(input.val() !== '' && $.inArray(input.val().toLowerCase(), keywords) > -1) { + value = convertCase(input.val()); + } else { + value = isRgb(input.val()) ? parseRgb(input.val()) : hex; + } + + // Update input value + if(!preserveInputValue) input.val(value); + + // Determine opacity value + if(settings.opacity) { + // Get from data-opacity attribute and keep within 0-1 range + opacity = input.attr('data-opacity') === '' ? 1 : keepWithin(parseFloat(input.attr('data-opacity')).toFixed(2), 0, 1); + if(isNaN(opacity)) opacity = 1; + input.attr('data-opacity', opacity); + swatch.find('span').css('opacity', String(opacity)); + + // Set opacity picker position + y = keepWithin(opacitySlider.height() - (opacitySlider.height() * opacity), 0, opacitySlider.height()); + opacityPicker.css('top', y + 'px'); + } + + // Set opacity to zero if input value is transparent + if(input.val().toLowerCase() === 'transparent') { + swatch.find('span').css('opacity', String(0)); + } + + // Update swatch + swatch.find('span').css('backgroundColor', hex); + + // Determine picker locations + switch(settings.control) { + case 'wheel': + // Set grid position + r = keepWithin(Math.ceil(hsb.s * 0.75), 0, grid.height() / 2); + phi = hsb.h * Math.PI / 180; + x = keepWithin(75 - Math.cos(phi) * r, 0, grid.width()); + y = keepWithin(75 - Math.sin(phi) * r, 0, grid.height()); + gridPicker.css({ + top: y + 'px', + left: x + 'px' + }); + + // Set slider position + y = 150 - (hsb.b / (100 / grid.height())); + if(hex === '') y = 0; + sliderPicker.css('top', y + 'px'); + + // Update panel color + slider.css('backgroundColor', hsb2hex({ h: hsb.h, s: hsb.s, b: 100 })); + break; + + case 'saturation': + // Set grid position + x = keepWithin((5 * hsb.h) / 12, 0, 150); + y = keepWithin(grid.height() - Math.ceil(hsb.b / (100 / grid.height())), 0, grid.height()); + gridPicker.css({ + top: y + 'px', + left: x + 'px' + }); + + // Set slider position + y = keepWithin(slider.height() - (hsb.s * (slider.height() / 100)), 0, slider.height()); + sliderPicker.css('top', y + 'px'); + + // Update UI + slider.css('backgroundColor', hsb2hex({ h: hsb.h, s: 100, b: hsb.b })); + minicolors.find('.minicolors-grid-inner').css('opacity', hsb.s / 100); + break; + + case 'brightness': + // Set grid position + x = keepWithin((5 * hsb.h) / 12, 0, 150); + y = keepWithin(grid.height() - Math.ceil(hsb.s / (100 / grid.height())), 0, grid.height()); + gridPicker.css({ + top: y + 'px', + left: x + 'px' + }); + + // Set slider position + y = keepWithin(slider.height() - (hsb.b * (slider.height() / 100)), 0, slider.height()); + sliderPicker.css('top', y + 'px'); + + // Update UI + slider.css('backgroundColor', hsb2hex({ h: hsb.h, s: hsb.s, b: 100 })); + minicolors.find('.minicolors-grid-inner').css('opacity', 1 - (hsb.b / 100)); + break; + + default: + // Set grid position + x = keepWithin(Math.ceil(hsb.s / (100 / grid.width())), 0, grid.width()); + y = keepWithin(grid.height() - Math.ceil(hsb.b / (100 / grid.height())), 0, grid.height()); + gridPicker.css({ + top: y + 'px', + left: x + 'px' + }); + + // Set slider position + y = keepWithin(slider.height() - (hsb.h / (360 / slider.height())), 0, slider.height()); + sliderPicker.css('top', y + 'px'); + + // Update panel color + grid.css('backgroundColor', hsb2hex({ h: hsb.h, s: 100, b: 100 })); + break; + } + + // Fire change event, but only if minicolors is fully initialized + if(input.data('minicolors-initialized')) { + doChange(input, value, opacity); + } + } + + // Runs the change and changeDelay callbacks + function doChange(input, value, opacity) { + var settings = input.data('minicolors-settings'); + var lastChange = input.data('minicolors-lastChange'); + var obj, sel, i; + + // Only run if it actually changed + if(!lastChange || lastChange.value !== value || lastChange.opacity !== opacity) { + + // Remember last-changed value + input.data('minicolors-lastChange', { + value: value, + opacity: opacity + }); + + // Check and select applicable swatch + if(settings.swatches && settings.swatches.length !== 0) { + if(!isRgb(value)) { + obj = hex2rgb(value); + } + else { + obj = parseRgb(value, true); + } + sel = -1; + for(i = 0; i < settings.swatches.length; ++i) { + if(obj.r === settings.swatches[i].r && obj.g === settings.swatches[i].g && obj.b === settings.swatches[i].b && obj.a === settings.swatches[i].a) { + sel = i; + break; + } + } + + input.parent().find('.minicolors-swatches .minicolors-swatch').removeClass('selected'); + if(sel !== -1) { + input.parent().find('.minicolors-swatches .minicolors-swatch').eq(i).addClass('selected'); + } + } + + // Fire change event + if(settings.change) { + if(settings.changeDelay) { + // Call after a delay + clearTimeout(input.data('minicolors-changeTimeout')); + input.data('minicolors-changeTimeout', setTimeout(function() { + settings.change.call(input.get(0), value, opacity); + }, settings.changeDelay)); + } else { + // Call immediately + settings.change.call(input.get(0), value, opacity); + } + } + input.trigger('change').trigger('input'); + } + } + + // Generates an RGB(A) object based on the input's value + function rgbObject(input) { + var rgb, + opacity = $(input).attr('data-opacity'); + if( isRgb($(input).val()) ) { + rgb = parseRgb($(input).val(), true); + } else { + var hex = parseHex($(input).val(), true); + rgb = hex2rgb(hex); + } + if( !rgb ) return null; + if( opacity !== undefined ) $.extend(rgb, { a: parseFloat(opacity) }); + return rgb; + } + + // Generates an RGB(A) string based on the input's value + function rgbString(input, alpha) { + var rgb, + opacity = $(input).attr('data-opacity'); + if( isRgb($(input).val()) ) { + rgb = parseRgb($(input).val(), true); + } else { + var hex = parseHex($(input).val(), true); + rgb = hex2rgb(hex); + } + if( !rgb ) return null; + if( opacity === undefined ) opacity = 1; + if( alpha ) { + return 'rgba(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ', ' + parseFloat(opacity) + ')'; + } else { + return 'rgb(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ')'; + } + } + + // Converts to the letter case specified in settings + function convertCase(string, letterCase) { + return letterCase === 'uppercase' ? string.toUpperCase() : string.toLowerCase(); + } + + // Parses a string and returns a valid hex string when possible + function parseHex(string, expand) { + string = string.replace(/^#/g, ''); + if(!string.match(/^[A-F0-9]{3,6}/ig)) return ''; + if(string.length !== 3 && string.length !== 6) return ''; + if(string.length === 3 && expand) { + string = string[0] + string[0] + string[1] + string[1] + string[2] + string[2]; + } + return '#' + string; + } + + // Parses a string and returns a valid RGB(A) string when possible + function parseRgb(string, obj) { + var values = string.replace(/[^\d,.]/g, ''); + var rgba = values.split(','); + + rgba[0] = keepWithin(parseInt(rgba[0], 10), 0, 255); + rgba[1] = keepWithin(parseInt(rgba[1], 10), 0, 255); + rgba[2] = keepWithin(parseInt(rgba[2], 10), 0, 255); + if(rgba[3] !== undefined) { + rgba[3] = keepWithin(parseFloat(rgba[3], 10), 0, 1); + } + + // Return RGBA object + if( obj ) { + if (rgba[3] !== undefined) { + return { + r: rgba[0], + g: rgba[1], + b: rgba[2], + a: rgba[3] + }; + } else { + return { + r: rgba[0], + g: rgba[1], + b: rgba[2] + }; + } + } + + // Return RGBA string + if(typeof(rgba[3]) !== 'undefined' && rgba[3] <= 1) { + return 'rgba(' + rgba[0] + ', ' + rgba[1] + ', ' + rgba[2] + ', ' + rgba[3] + ')'; + } else { + return 'rgb(' + rgba[0] + ', ' + rgba[1] + ', ' + rgba[2] + ')'; + } + + } + + // Parses a string and returns a valid color string when possible + function parseInput(string, expand) { + if(isRgb(string)) { + // Returns a valid rgb(a) string + return parseRgb(string); + } else { + return parseHex(string, expand); + } + } + + // Keeps value within min and max + function keepWithin(value, min, max) { + if(value < min) value = min; + if(value > max) value = max; + return value; + } + + // Checks if a string is a valid RGB(A) string + function isRgb(string) { + var rgb = string.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i); + return (rgb && rgb.length === 4) ? true : false; + } + + // Function to get alpha from a RGB(A) string + function getAlpha(rgba) { + rgba = rgba.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+(\.\d{1,2})?|\.\d{1,2})[\s+]?/i); + return (rgba && rgba.length === 6) ? rgba[4] : '1'; + } + + // Converts an HSB object to an RGB object + function hsb2rgb(hsb) { + var rgb = {}; + var h = Math.round(hsb.h); + var s = Math.round(hsb.s * 255 / 100); + var v = Math.round(hsb.b * 255 / 100); + if(s === 0) { + rgb.r = rgb.g = rgb.b = v; + } else { + var t1 = v; + var t2 = (255 - s) * v / 255; + var t3 = (t1 - t2) * (h % 60) / 60; + if(h === 360) h = 0; + if(h < 60) { rgb.r = t1; rgb.b = t2; rgb.g = t2 + t3; } + else if(h < 120) {rgb.g = t1; rgb.b = t2; rgb.r = t1 - t3; } + else if(h < 180) {rgb.g = t1; rgb.r = t2; rgb.b = t2 + t3; } + else if(h < 240) {rgb.b = t1; rgb.r = t2; rgb.g = t1 - t3; } + else if(h < 300) {rgb.b = t1; rgb.g = t2; rgb.r = t2 + t3; } + else if(h < 360) {rgb.r = t1; rgb.g = t2; rgb.b = t1 - t3; } + else { rgb.r = 0; rgb.g = 0; rgb.b = 0; } + } + return { + r: Math.round(rgb.r), + g: Math.round(rgb.g), + b: Math.round(rgb.b) + }; + } + + // Converts an RGB string to a hex string + function rgbString2hex(rgb){ + rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i); + return (rgb && rgb.length === 4) ? '#' + + ('0' + parseInt(rgb[1],10).toString(16)).slice(-2) + + ('0' + parseInt(rgb[2],10).toString(16)).slice(-2) + + ('0' + parseInt(rgb[3],10).toString(16)).slice(-2) : ''; + } + + // Converts an RGB object to a hex string + function rgb2hex(rgb) { + var hex = [ + rgb.r.toString(16), + rgb.g.toString(16), + rgb.b.toString(16) + ]; + $.each(hex, function(nr, val) { + if(val.length === 1) hex[nr] = '0' + val; + }); + return '#' + hex.join(''); + } + + // Converts an HSB object to a hex string + function hsb2hex(hsb) { + return rgb2hex(hsb2rgb(hsb)); + } + + // Converts a hex string to an HSB object + function hex2hsb(hex) { + var hsb = rgb2hsb(hex2rgb(hex)); + if(hsb.s === 0) hsb.h = 360; + return hsb; + } + + // Converts an RGB object to an HSB object + function rgb2hsb(rgb) { + var hsb = { h: 0, s: 0, b: 0 }; + var min = Math.min(rgb.r, rgb.g, rgb.b); + var max = Math.max(rgb.r, rgb.g, rgb.b); + var delta = max - min; + hsb.b = max; + hsb.s = max !== 0 ? 255 * delta / max : 0; + if(hsb.s !== 0) { + if(rgb.r === max) { + hsb.h = (rgb.g - rgb.b) / delta; + } else if(rgb.g === max) { + hsb.h = 2 + (rgb.b - rgb.r) / delta; + } else { + hsb.h = 4 + (rgb.r - rgb.g) / delta; + } + } else { + hsb.h = -1; + } + hsb.h *= 60; + if(hsb.h < 0) { + hsb.h += 360; + } + hsb.s *= 100/255; + hsb.b *= 100/255; + return hsb; + } + + // Converts a hex string to an RGB object + function hex2rgb(hex) { + hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16); + return { + r: hex >> 16, + g: (hex & 0x00FF00) >> 8, + b: (hex & 0x0000FF) + }; + } + + // Handle events + $([document]) + // Hide on clicks outside of the control + .on('mousedown.minicolors touchstart.minicolors', function(event) { + if(!$(event.target).parents().add(event.target).hasClass('minicolors')) { + hide(); + } + }) + // Start moving + .on('mousedown.minicolors touchstart.minicolors', '.minicolors-grid, .minicolors-slider, .minicolors-opacity-slider', function(event) { + var target = $(this); + event.preventDefault(); + $(event.delegateTarget).data('minicolors-target', target); + move(target, event, true); + }) + // Move pickers + .on('mousemove.minicolors touchmove.minicolors', function(event) { + var target = $(event.delegateTarget).data('minicolors-target'); + if(target) move(target, event); + }) + // Stop moving + .on('mouseup.minicolors touchend.minicolors', function() { + $(this).removeData('minicolors-target'); + }) + // Selected a swatch + .on('click.minicolors', '.minicolors-swatches li', function(event) { + event.preventDefault(); + var target = $(this), input = target.parents('.minicolors').find('.minicolors-input'), color = target.data('swatch-color'); + updateInput(input, color, getAlpha(color)); + updateFromInput(input); + }) + // Show panel when swatch is clicked + .on('mousedown.minicolors touchstart.minicolors', '.minicolors-input-swatch', function(event) { + var input = $(this).parent().find('.minicolors-input'); + event.preventDefault(); + show(input); + }) + // Show on focus + .on('focus.minicolors', '.minicolors-input', function() { + var input = $(this); + if(!input.data('minicolors-initialized')) return; + show(input); + }) + // Update value on blur + .on('blur.minicolors', '.minicolors-input', function() { + var input = $(this); + var settings = input.data('minicolors-settings'); + var keywords; + var hex; + var rgba; + var swatchOpacity; + var value; + + if(!input.data('minicolors-initialized')) return; + + // Get array of lowercase keywords + keywords = !settings.keywords ? [] : $.map(settings.keywords.split(','), function(a) { + return a.toLowerCase().trim(); + }); + + // Set color string + if(input.val() !== '' && $.inArray(input.val().toLowerCase(), keywords) > -1) { + value = input.val(); + } else { + // Get RGBA values for easy conversion + if(isRgb(input.val())) { + rgba = parseRgb(input.val(), true); + } else { + hex = parseHex(input.val(), true); + rgba = hex ? hex2rgb(hex) : null; + } + + // Convert to format + if(rgba === null) { + value = settings.defaultValue; + } else if(settings.format === 'rgb') { + value = settings.opacity ? + parseRgb('rgba(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ',' + input.attr('data-opacity') + ')') : + parseRgb('rgb(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ')'); + } else { + value = rgb2hex(rgba); + } + } + + // Update swatch opacity + swatchOpacity = settings.opacity ? input.attr('data-opacity') : 1; + if(value.toLowerCase() === 'transparent') swatchOpacity = 0; + input + .closest('.minicolors') + .find('.minicolors-input-swatch > span') + .css('opacity', String(swatchOpacity)); + + // Set input value + input.val(value); + + // Is it blank? + if(input.val() === '') input.val(parseInput(settings.defaultValue, true)); + + // Adjust case + input.val(convertCase(input.val(), settings.letterCase)); + + }) + // Handle keypresses + .on('keydown.minicolors', '.minicolors-input', function(event) { + var input = $(this); + if(!input.data('minicolors-initialized')) return; + switch(event.which) { + case 9: // tab + hide(); + break; + case 13: // enter + case 27: // esc + hide(); + input.blur(); + break; + } + }) + // Update on keyup + .on('keyup.minicolors', '.minicolors-input', function() { + var input = $(this); + if(!input.data('minicolors-initialized')) return; + updateFromInput(input, true); + }) + // Update on paste + .on('paste.minicolors', '.minicolors-input', function() { + var input = $(this); + if(!input.data('minicolors-initialized')) return; + setTimeout(function() { + updateFromInput(input, true); + }, 1); + }); +})); diff --git a/test/i18n-hooks.test.js b/test/i18n-hooks.test.js index 776a01e1..197fb9a7 100644 --- a/test/i18n-hooks.test.js +++ b/test/i18n-hooks.test.js @@ -146,7 +146,7 @@ test("host-provided dependencies stay out of the bundle", function() { // The mindmaps-owned plugins are bundled, because nothing else provides them. assert.ok(/jQuery Mousewheel/.test(bundle), "mousewheel is bundled"); assert.ok(/dragscrollable/i.test(bundle), "dragscrollable is bundled"); - assert.ok(/miniColors/i.test(bundle), "minicolors is bundled"); + assert.ok(/minicolors/i.test(bundle), "minicolors is bundled"); }); test("the page loads its dependencies from disk, never from a CDN", function() { @@ -168,6 +168,22 @@ test("the page loads its dependencies from disk, never from a CDN", function() { assert.ok(!/api\.filestackapi\.com|filepicker\.io\/v\d|]+filestack/i.test(html), "no Filestack loader"); }); +test("the colour picker is the maintained MiniColors release", function() { + var minicolors = fs.readFileSync(path.join(srcDir, "libs", "jquery.minicolors.js"), "utf8"); + + // @claviska/jquery-minicolors, not the unscoped package, which is stale and not the + // author's. 2.x renamed the plugin and its callbacks, so the call sites moved with it. + assert.ok(/jQuery MiniColors: A tiny color picker built on jQuery/.test(minicolors), "is MiniColors"); + assert.ok(/MiniColors 2\.3\.6/.test(minicolors), "pinned to 2.3.6"); + assert.ok(/minicolors:\s*function/.test(minicolors), "registers the lowercase plugin method"); + + var inspector = read("Inspector.js"); + assert.ok(!/\.miniColors\(/.test(inspector), "no call site left on the 1.5 name"); + assert.ok(/\.minicolors\(/.test(inspector), "call sites use the 2.x name"); + assert.ok(/change\s*:/.test(inspector), "live preview uses the change callback"); + assert.ok(!/move\s*:/.test(inspector), "the removed move callback is gone"); +}); + test("the vendored host dependencies are the expected versions", function() { var vendor = path.join(__dirname, "..", "src", "js", "vendor"); var jquery = fs.readFileSync(path.join(vendor, "jquery.min.js"), "utf8");