2 // jQuery MiniColors: A tiny color picker built on jQuery
4 // Developed by Cory LaViska for A Beautiful Site, LLC
6 // Licensed under the MIT license: http://opensource.org/licenses/MIT
9 if(typeof define === 'function' && define.amd) {
10 // AMD. Register as an anonymous module.
11 define(['jquery'], factory);
12 } else if(typeof exports === 'object') {
14 module.exports = factory(require('jquery'));
26 animationEasing: 'swing',
36 letterCase: 'lowercase',
38 position: 'bottom left',
48 minicolors: function(method, data) {
51 // Destroy the control
53 $(this).each(function() {
58 // Hide the color picker
66 if(data === undefined) {
68 return $(this).attr('data-opacity');
71 $(this).each(function() {
72 updateFromInput($(this).attr('data-opacity', data));
77 // Get an RGB(A) object based on the current color/opacity
79 return rgbObject($(this), method === 'rgbaObject');
81 // Get an RGB(A) string based on the current color/opacity
84 return rgbString($(this), method === 'rgbaString');
86 // Get/set settings on the fly
88 if(data === undefined) {
89 return $(this).data('minicolors-settings');
92 $(this).each(function() {
93 var settings = $(this).data('minicolors-settings') || {};
95 $(this).minicolors($.extend(true, settings, data));
100 // Show the color picker
105 // Get/set the hex color value
107 if(data === undefined) {
109 return $(this).val();
112 $(this).each(function() {
113 if(typeof(data) === 'object' && data !== null) {
115 $(this).attr('data-opacity', keepWithin(data.opacity, 0, 1));
118 $(this).val(data.color);
123 updateFromInput($(this));
128 // Initializes the control
130 if(method !== 'create') data = method;
131 $(this).each(function() {
141 // Initialize input elements
142 function init(input, settings) {
143 var minicolors = $('<div class="minicolors" />');
144 var defaults = $.minicolors.defaults;
151 // Do nothing if already initialized
152 if(input.data('minicolors-initialized')) return;
155 settings = $.extend(true, {}, defaults, settings);
159 .addClass('minicolors-theme-' + settings.theme)
160 .toggleClass('minicolors-with-opacity', settings.opacity);
162 // Custom positioning
163 if(settings.position !== undefined) {
164 $.each(settings.position.split(' '), function() {
165 minicolors.addClass('minicolors-position-' + this);
170 if(settings.format === 'rgb') {
171 size = settings.opacity ? '25' : '20';
173 size = settings.keywords ? '11' : '7';
178 .addClass('minicolors-input')
179 .data('minicolors-initialized', false)
180 .data('minicolors-settings', settings)
184 '<div class="minicolors-panel minicolors-slider-' + settings.control + '">' +
185 '<div class="minicolors-slider minicolors-sprite">' +
186 '<div class="minicolors-picker"></div>' +
188 '<div class="minicolors-opacity-slider minicolors-sprite">' +
189 '<div class="minicolors-picker"></div>' +
191 '<div class="minicolors-grid minicolors-sprite">' +
192 '<div class="minicolors-grid-inner"></div>' +
193 '<div class="minicolors-picker"><div></div></div>' +
199 if(!settings.inline) {
200 input.after('<span class="minicolors-swatch minicolors-sprite minicolors-input-swatch"><span class="minicolors-swatch-color"></span></span>');
201 input.next('.minicolors-input-swatch').on('click', function(event) {
202 event.preventDefault();
207 // Prevent text selection in IE
208 panel = input.parent().find('.minicolors-panel');
209 panel.on('selectstart', function() { return false; }).end();
212 if(settings.swatches && settings.swatches.length !== 0) {
213 panel.addClass('minicolors-with-swatches');
214 swatches = $('<ul class="minicolors-swatches"></ul>')
216 for(i = 0; i < settings.swatches.length; ++i) {
217 swatch = settings.swatches[i];
218 swatch = isRgb(swatch) ? parseRgb(swatch, true) : hex2rgb(parseHex(swatch, true));
219 $('<li class="minicolors-swatch minicolors-sprite"><span class="minicolors-swatch-color"></span></li>')
221 .data('swatch-color', settings.swatches[i])
222 .find('.minicolors-swatch-color')
224 backgroundColor: rgb2hex(swatch),
227 settings.swatches[i] = swatch;
232 if(settings.inline) input.parent().addClass('minicolors-inline');
234 updateFromInput(input, false);
236 input.data('minicolors-initialized', true);
239 // Returns the input back to its original state
240 function destroy(input) {
241 var minicolors = input.parent();
243 // Revert the input element
245 .removeData('minicolors-initialized')
246 .removeData('minicolors-settings')
248 .removeClass('minicolors-input');
250 // Remove the wrap and destroy whatever remains
251 minicolors.before(input).remove();
254 // Shows the specified dropdown panel
255 function show(input) {
256 var minicolors = input.parent();
257 var panel = minicolors.find('.minicolors-panel');
258 var settings = input.data('minicolors-settings');
260 // Do nothing if uninitialized, disabled, inline, or already open
262 !input.data('minicolors-initialized') ||
263 input.prop('disabled') ||
264 minicolors.hasClass('minicolors-inline') ||
265 minicolors.hasClass('minicolors-focus')
270 minicolors.addClass('minicolors-focus');
273 .fadeIn(settings.showSpeed, function() {
274 if(settings.show) settings.show.call(input.get(0));
278 // Hides all dropdown panels
280 $('.minicolors-focus').each(function() {
281 var minicolors = $(this);
282 var input = minicolors.find('.minicolors-input');
283 var panel = minicolors.find('.minicolors-panel');
284 var settings = input.data('minicolors-settings');
286 panel.fadeOut(settings.hideSpeed, function() {
287 if(settings.hide) settings.hide.call(input.get(0));
288 minicolors.removeClass('minicolors-focus');
294 // Moves the selected picker
295 function move(target, event, animate) {
296 var input = target.parents('.minicolors').find('.minicolors-input');
297 var settings = input.data('minicolors-settings');
298 var picker = target.find('[class$=-picker]');
299 var offsetX = target.offset().left;
300 var offsetY = target.offset().top;
301 var x = Math.round(event.pageX - offsetX);
302 var y = Math.round(event.pageY - offsetY);
303 var duration = animate ? settings.animationSpeed : 0;
307 if(event.originalEvent.changedTouches) {
308 x = event.originalEvent.changedTouches[0].pageX - offsetX;
309 y = event.originalEvent.changedTouches[0].pageY - offsetY;
312 // Constrain picker to its container
315 if(x > target.width()) x = target.width();
316 if(y > target.height()) y = target.height();
318 // Constrain color wheel values to the wheel
319 if(target.parent().is('.minicolors-slider-wheel') && picker.parent().is('.minicolors-grid')) {
322 r = Math.sqrt(wx * wx + wy * wy);
323 phi = Math.atan2(wy, wx);
324 if(phi < 0) phi += Math.PI * 2;
327 x = 75 - (75 * Math.cos(phi));
328 y = 75 - (75 * Math.sin(phi));
335 if(target.is('.minicolors-grid')) {
341 }, duration, settings.animationEasing, function() {
342 updateFromControl(input, target);
349 }, duration, settings.animationEasing, function() {
350 updateFromControl(input, target);
355 // Sets the input based on the color picker values
356 function updateFromControl(input, target) {
358 function getCoords(picker, container) {
360 if(!picker.length || !container) return null;
361 left = picker.offset().left;
362 top = picker.offset().top;
365 x: left - container.offset().left + (picker.outerWidth() / 2),
366 y: top - container.offset().top + (picker.outerHeight() / 2)
370 var hue, saturation, brightness, x, y, r, phi;
371 var hex = input.val();
372 var opacity = input.attr('data-opacity');
374 // Helpful references
375 var minicolors = input.parent();
376 var settings = input.data('minicolors-settings');
377 var swatch = minicolors.find('.minicolors-input-swatch');
380 var grid = minicolors.find('.minicolors-grid');
381 var slider = minicolors.find('.minicolors-slider');
382 var opacitySlider = minicolors.find('.minicolors-opacity-slider');
385 var gridPicker = grid.find('[class$=-picker]');
386 var sliderPicker = slider.find('[class$=-picker]');
387 var opacityPicker = opacitySlider.find('[class$=-picker]');
390 var gridPos = getCoords(gridPicker, grid);
391 var sliderPos = getCoords(sliderPicker, slider);
392 var opacityPos = getCoords(opacityPicker, opacitySlider);
395 if(target.is('.minicolors-grid, .minicolors-slider, .minicolors-opacity-slider')) {
397 // Determine HSB values
398 switch(settings.control) {
400 // Calculate hue, saturation, and brightness
401 x = (grid.width() / 2) - gridPos.x;
402 y = (grid.height() / 2) - gridPos.y;
403 r = Math.sqrt(x * x + y * y);
404 phi = Math.atan2(y, x);
405 if(phi < 0) phi += Math.PI * 2;
408 gridPos.x = 69 - (75 * Math.cos(phi));
409 gridPos.y = 69 - (75 * Math.sin(phi));
411 saturation = keepWithin(r / 0.75, 0, 100);
412 hue = keepWithin(phi * 180 / Math.PI, 0, 360);
413 brightness = keepWithin(100 - Math.floor(sliderPos.y * (100 / slider.height())), 0, 100);
421 slider.css('backgroundColor', hsb2hex({ h: hue, s: saturation, b: 100 }));
425 // Calculate hue, saturation, and brightness
426 hue = keepWithin(parseInt(gridPos.x * (360 / grid.width()), 10), 0, 360);
427 saturation = keepWithin(100 - Math.floor(sliderPos.y * (100 / slider.height())), 0, 100);
428 brightness = keepWithin(100 - Math.floor(gridPos.y * (100 / grid.height())), 0, 100);
436 slider.css('backgroundColor', hsb2hex({ h: hue, s: 100, b: brightness }));
437 minicolors.find('.minicolors-grid-inner').css('opacity', saturation / 100);
441 // Calculate hue, saturation, and brightness
442 hue = keepWithin(parseInt(gridPos.x * (360 / grid.width()), 10), 0, 360);
443 saturation = keepWithin(100 - Math.floor(gridPos.y * (100 / grid.height())), 0, 100);
444 brightness = keepWithin(100 - Math.floor(sliderPos.y * (100 / slider.height())), 0, 100);
452 slider.css('backgroundColor', hsb2hex({ h: hue, s: saturation, b: 100 }));
453 minicolors.find('.minicolors-grid-inner').css('opacity', 1 - (brightness / 100));
457 // Calculate hue, saturation, and brightness
458 hue = keepWithin(360 - parseInt(sliderPos.y * (360 / slider.height()), 10), 0, 360);
459 saturation = keepWithin(Math.floor(gridPos.x * (100 / grid.width())), 0, 100);
460 brightness = keepWithin(100 - Math.floor(gridPos.y * (100 / grid.height())), 0, 100);
468 grid.css('backgroundColor', hsb2hex({ h: hue, s: 100, b: 100 }));
473 if(settings.opacity) {
474 opacity = parseFloat(1 - (opacityPos.y / opacitySlider.height())).toFixed(2);
479 updateInput(input, hex, opacity);
483 swatch.find('span').css({
484 backgroundColor: hex,
488 // Handle change event
489 doChange(input, hex, opacity);
493 // Sets the value of the input and does the appropriate conversions
494 // to respect settings, also updates the swatch
495 function updateInput(input, value, opacity) {
498 // Helpful references
499 var minicolors = input.parent();
500 var settings = input.data('minicolors-settings');
501 var swatch = minicolors.find('.minicolors-input-swatch');
503 if(settings.opacity) input.attr('data-opacity', opacity);
506 if(settings.format === 'rgb') {
507 // Returns RGB(A) string
509 // Checks for input format and does the conversion
511 rgb = parseRgb(value, true);
514 rgb = hex2rgb(parseHex(value, true));
517 opacity = input.attr('data-opacity') === '' ? 1 : keepWithin(parseFloat(input.attr('data-opacity')).toFixed(2), 0, 1);
518 if(isNaN(opacity) || !settings.opacity) opacity = 1;
520 if(input.minicolors('rgbObject').a <= 1 && rgb && settings.opacity) {
521 // Set RGBA string if alpha
522 value = 'rgba(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ', ' + parseFloat(opacity) + ')';
524 // Set RGB string (alpha = 1)
525 value = 'rgb(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ')';
530 // Checks for input format and does the conversion
532 value = rgbString2hex(value);
535 value = convertCase(value, settings.letterCase);
538 // Update value from picker
542 swatch.find('span').css({
543 backgroundColor: value,
547 // Handle change event
548 doChange(input, value, opacity);
551 // Sets the color picker values from the input
552 function updateFromInput(input, preserveInputValue) {
553 var hex, hsb, opacity, keywords, alpha, value, x, y, r, phi;
555 // Helpful references
556 var minicolors = input.parent();
557 var settings = input.data('minicolors-settings');
558 var swatch = minicolors.find('.minicolors-input-swatch');
561 var grid = minicolors.find('.minicolors-grid');
562 var slider = minicolors.find('.minicolors-slider');
563 var opacitySlider = minicolors.find('.minicolors-opacity-slider');
566 var gridPicker = grid.find('[class$=-picker]');
567 var sliderPicker = slider.find('[class$=-picker]');
568 var opacityPicker = opacitySlider.find('[class$=-picker]');
570 // Determine hex/HSB values
571 if(isRgb(input.val())) {
572 // If input value is a rgb(a) string, convert it to hex color and update opacity
573 hex = rgbString2hex(input.val());
574 alpha = keepWithin(parseFloat(getAlpha(input.val())).toFixed(2), 0, 1);
576 input.attr('data-opacity', alpha);
579 hex = convertCase(parseHex(input.val(), true), settings.letterCase);
583 hex = convertCase(parseInput(settings.defaultValue, true), settings.letterCase);
587 // Get array of lowercase keywords
588 keywords = !settings.keywords ? [] : $.map(settings.keywords.split(','), function(a) {
589 return $.trim(a.toLowerCase());
593 if(input.val() !== '' && $.inArray(input.val().toLowerCase(), keywords) > -1) {
594 value = convertCase(input.val());
596 value = isRgb(input.val()) ? parseRgb(input.val()) : hex;
599 // Update input value
600 if(!preserveInputValue) input.val(value);
602 // Determine opacity value
603 if(settings.opacity) {
604 // Get from data-opacity attribute and keep within 0-1 range
605 opacity = input.attr('data-opacity') === '' ? 1 : keepWithin(parseFloat(input.attr('data-opacity')).toFixed(2), 0, 1);
606 if(isNaN(opacity)) opacity = 1;
607 input.attr('data-opacity', opacity);
608 swatch.find('span').css('opacity', opacity);
610 // Set opacity picker position
611 y = keepWithin(opacitySlider.height() - (opacitySlider.height() * opacity), 0, opacitySlider.height());
612 opacityPicker.css('top', y + 'px');
615 // Set opacity to zero if input value is transparent
616 if(input.val().toLowerCase() === 'transparent') {
617 swatch.find('span').css('opacity', 0);
621 swatch.find('span').css('backgroundColor', hex);
623 // Determine picker locations
624 switch(settings.control) {
627 r = keepWithin(Math.ceil(hsb.s * 0.75), 0, grid.height() / 2);
628 phi = hsb.h * Math.PI / 180;
629 x = keepWithin(75 - Math.cos(phi) * r, 0, grid.width());
630 y = keepWithin(75 - Math.sin(phi) * r, 0, grid.height());
636 // Set slider position
637 y = 150 - (hsb.b / (100 / grid.height()));
638 if(hex === '') y = 0;
639 sliderPicker.css('top', y + 'px');
641 // Update panel color
642 slider.css('backgroundColor', hsb2hex({ h: hsb.h, s: hsb.s, b: 100 }));
647 x = keepWithin((5 * hsb.h) / 12, 0, 150);
648 y = keepWithin(grid.height() - Math.ceil(hsb.b / (100 / grid.height())), 0, grid.height());
654 // Set slider position
655 y = keepWithin(slider.height() - (hsb.s * (slider.height() / 100)), 0, slider.height());
656 sliderPicker.css('top', y + 'px');
659 slider.css('backgroundColor', hsb2hex({ h: hsb.h, s: 100, b: hsb.b }));
660 minicolors.find('.minicolors-grid-inner').css('opacity', hsb.s / 100);
665 x = keepWithin((5 * hsb.h) / 12, 0, 150);
666 y = keepWithin(grid.height() - Math.ceil(hsb.s / (100 / grid.height())), 0, grid.height());
672 // Set slider position
673 y = keepWithin(slider.height() - (hsb.b * (slider.height() / 100)), 0, slider.height());
674 sliderPicker.css('top', y + 'px');
677 slider.css('backgroundColor', hsb2hex({ h: hsb.h, s: hsb.s, b: 100 }));
678 minicolors.find('.minicolors-grid-inner').css('opacity', 1 - (hsb.b / 100));
683 x = keepWithin(Math.ceil(hsb.s / (100 / grid.width())), 0, grid.width());
684 y = keepWithin(grid.height() - Math.ceil(hsb.b / (100 / grid.height())), 0, grid.height());
690 // Set slider position
691 y = keepWithin(slider.height() - (hsb.h / (360 / slider.height())), 0, slider.height());
692 sliderPicker.css('top', y + 'px');
694 // Update panel color
695 grid.css('backgroundColor', hsb2hex({ h: hsb.h, s: 100, b: 100 }));
699 // Fire change event, but only if minicolors is fully initialized
700 if(input.data('minicolors-initialized')) {
701 doChange(input, value, opacity);
705 // Runs the change and changeDelay callbacks
706 function doChange(input, value, opacity) {
707 var settings = input.data('minicolors-settings');
708 var lastChange = input.data('minicolors-lastChange');
711 // Only run if it actually changed
712 if(!lastChange || lastChange.value !== value || lastChange.opacity !== opacity) {
714 // Remember last-changed value
715 input.data('minicolors-lastChange', {
720 // Check and select applicable swatch
721 if(settings.swatches && settings.swatches.length !== 0) {
723 obj = hex2rgb(value);
726 obj = parseRgb(value, true);
729 for(i = 0; i < settings.swatches.length; ++i) {
730 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) {
736 input.parent().find('.minicolors-swatches .minicolors-swatch').removeClass('selected');
738 input.parent().find('.minicolors-swatches .minicolors-swatch').eq(i).addClass('selected');
743 if(settings.change) {
744 if(settings.changeDelay) {
745 // Call after a delay
746 clearTimeout(input.data('minicolors-changeTimeout'));
747 input.data('minicolors-changeTimeout', setTimeout(function() {
748 settings.change.call(input.get(0), value, opacity);
749 }, settings.changeDelay));
752 settings.change.call(input.get(0), value, opacity);
755 input.trigger('change').trigger('input');
759 // Generates an RGB(A) object based on the input's value
760 function rgbObject(input) {
762 opacity = $(input).attr('data-opacity');
763 if( isRgb($(input).val()) ) {
764 rgb = parseRgb($(input).val(), true);
766 var hex = parseHex($(input).val(), true);
769 if( !rgb ) return null;
770 if( opacity !== undefined ) $.extend(rgb, { a: parseFloat(opacity) });
774 // Generates an RGB(A) string based on the input's value
775 function rgbString(input, alpha) {
777 opacity = $(input).attr('data-opacity');
778 if( isRgb($(input).val()) ) {
779 rgb = parseRgb($(input).val(), true);
781 var hex = parseHex($(input).val(), true);
784 if( !rgb ) return null;
785 if( opacity === undefined ) opacity = 1;
787 return 'rgba(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ', ' + parseFloat(opacity) + ')';
789 return 'rgb(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ')';
793 // Converts to the letter case specified in settings
794 function convertCase(string, letterCase) {
795 return letterCase === 'uppercase' ? string.toUpperCase() : string.toLowerCase();
798 // Parses a string and returns a valid hex string when possible
799 function parseHex(string, expand) {
800 string = string.replace(/^#/g, '');
801 if(!string.match(/^[A-F0-9]{3,6}/ig)) return '';
802 if(string.length !== 3 && string.length !== 6) return '';
803 if(string.length === 3 && expand) {
804 string = string[0] + string[0] + string[1] + string[1] + string[2] + string[2];
809 // Parses a string and returns a valid RGB(A) string when possible
810 function parseRgb(string, obj) {
811 var values = string.replace(/[^\d,.]/g, '');
812 var rgba = values.split(',');
814 rgba[0] = keepWithin(parseInt(rgba[0], 10), 0, 255);
815 rgba[1] = keepWithin(parseInt(rgba[1], 10), 0, 255);
816 rgba[2] = keepWithin(parseInt(rgba[2], 10), 0, 255);
818 rgba[3] = keepWithin(parseFloat(rgba[3], 10), 0, 1);
821 // Return RGBA object
839 // Return RGBA string
840 if(typeof(rgba[3]) !== 'undefined' && rgba[3] <= 1) {
841 return 'rgba(' + rgba[0] + ', ' + rgba[1] + ', ' + rgba[2] + ', ' + rgba[3] + ')';
843 return 'rgb(' + rgba[0] + ', ' + rgba[1] + ', ' + rgba[2] + ')';
848 // Parses a string and returns a valid color string when possible
849 function parseInput(string, expand) {
851 // Returns a valid rgb(a) string
852 return parseRgb(string);
854 return parseHex(string, expand);
858 // Keeps value within min and max
859 function keepWithin(value, min, max) {
860 if(value < min) value = min;
861 if(value > max) value = max;
865 // Checks if a string is a valid RGB(A) string
866 function isRgb(string) {
867 var rgb = string.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
868 return (rgb && rgb.length === 4) ? true : false;
871 // Function to get alpha from a RGB(A) string
872 function getAlpha(rgba) {
873 rgba = rgba.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+(\.\d{1,2})?|\.\d{1,2})[\s+]?/i);
874 return (rgba && rgba.length === 6) ? rgba[4] : '1';
877 // Converts an HSB object to an RGB object
878 function hsb2rgb(hsb) {
880 var h = Math.round(hsb.h);
881 var s = Math.round(hsb.s * 255 / 100);
882 var v = Math.round(hsb.b * 255 / 100);
884 rgb.r = rgb.g = rgb.b = v;
887 var t2 = (255 - s) * v / 255;
888 var t3 = (t1 - t2) * (h % 60) / 60;
890 if(h < 60) { rgb.r = t1; rgb.b = t2; rgb.g = t2 + t3; }
891 else if(h < 120) {rgb.g = t1; rgb.b = t2; rgb.r = t1 - t3; }
892 else if(h < 180) {rgb.g = t1; rgb.r = t2; rgb.b = t2 + t3; }
893 else if(h < 240) {rgb.b = t1; rgb.r = t2; rgb.g = t1 - t3; }
894 else if(h < 300) {rgb.b = t1; rgb.g = t2; rgb.r = t2 + t3; }
895 else if(h < 360) {rgb.r = t1; rgb.g = t2; rgb.b = t1 - t3; }
896 else { rgb.r = 0; rgb.g = 0; rgb.b = 0; }
899 r: Math.round(rgb.r),
900 g: Math.round(rgb.g),
905 // Converts an RGB string to a hex string
906 function rgbString2hex(rgb){
907 rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
908 return (rgb && rgb.length === 4) ? '#' +
909 ('0' + parseInt(rgb[1],10).toString(16)).slice(-2) +
910 ('0' + parseInt(rgb[2],10).toString(16)).slice(-2) +
911 ('0' + parseInt(rgb[3],10).toString(16)).slice(-2) : '';
914 // Converts an RGB object to a hex string
915 function rgb2hex(rgb) {
921 $.each(hex, function(nr, val) {
922 if(val.length === 1) hex[nr] = '0' + val;
924 return '#' + hex.join('');
927 // Converts an HSB object to a hex string
928 function hsb2hex(hsb) {
929 return rgb2hex(hsb2rgb(hsb));
932 // Converts a hex string to an HSB object
933 function hex2hsb(hex) {
934 var hsb = rgb2hsb(hex2rgb(hex));
935 if(hsb.s === 0) hsb.h = 360;
939 // Converts an RGB object to an HSB object
940 function rgb2hsb(rgb) {
941 var hsb = { h: 0, s: 0, b: 0 };
942 var min = Math.min(rgb.r, rgb.g, rgb.b);
943 var max = Math.max(rgb.r, rgb.g, rgb.b);
944 var delta = max - min;
946 hsb.s = max !== 0 ? 255 * delta / max : 0;
949 hsb.h = (rgb.g - rgb.b) / delta;
950 } else if(rgb.g === max) {
951 hsb.h = 2 + (rgb.b - rgb.r) / delta;
953 hsb.h = 4 + (rgb.r - rgb.g) / delta;
967 // Converts a hex string to an RGB object
968 function hex2rgb(hex) {
969 hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16);
972 g: (hex & 0x00FF00) >> 8,
979 // Hide on clicks outside of the control
980 .on('mousedown.minicolors touchstart.minicolors', function(event) {
981 if(!$(event.target).parents().add(event.target).hasClass('minicolors')) {
986 .on('mousedown.minicolors touchstart.minicolors', '.minicolors-grid, .minicolors-slider, .minicolors-opacity-slider', function(event) {
987 var target = $(this);
988 event.preventDefault();
989 $(event.delegateTarget).data('minicolors-target', target);
990 move(target, event, true);
993 .on('mousemove.minicolors touchmove.minicolors', function(event) {
994 var target = $(event.delegateTarget).data('minicolors-target');
995 if(target) move(target, event);
998 .on('mouseup.minicolors touchend.minicolors', function() {
999 $(this).removeData('minicolors-target');
1001 // Selected a swatch
1002 .on('click.minicolors', '.minicolors-swatches li', function(event) {
1003 event.preventDefault();
1004 var target = $(this), input = target.parents('.minicolors').find('.minicolors-input'), color = target.data('swatch-color');
1005 updateInput(input, color, getAlpha(color));
1006 updateFromInput(input);
1008 // Show panel when swatch is clicked
1009 .on('mousedown.minicolors touchstart.minicolors', '.minicolors-input-swatch', function(event) {
1010 var input = $(this).parent().find('.minicolors-input');
1011 event.preventDefault();
1015 .on('focus.minicolors', '.minicolors-input', function() {
1016 var input = $(this);
1017 if(!input.data('minicolors-initialized')) return;
1020 // Update value on blur
1021 .on('blur.minicolors', '.minicolors-input', function() {
1022 var input = $(this);
1023 var settings = input.data('minicolors-settings');
1030 if(!input.data('minicolors-initialized')) return;
1032 // Get array of lowercase keywords
1033 keywords = !settings.keywords ? [] : $.map(settings.keywords.split(','), function(a) {
1034 return $.trim(a.toLowerCase());
1038 if(input.val() !== '' && $.inArray(input.val().toLowerCase(), keywords) > -1) {
1039 value = input.val();
1041 // Get RGBA values for easy conversion
1042 if(isRgb(input.val())) {
1043 rgba = parseRgb(input.val(), true);
1045 hex = parseHex(input.val(), true);
1046 rgba = hex ? hex2rgb(hex) : null;
1049 // Convert to format
1051 value = settings.defaultValue;
1052 } else if(settings.format === 'rgb') {
1053 value = settings.opacity ?
1054 parseRgb('rgba(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ',' + input.attr('data-opacity') + ')') :
1055 parseRgb('rgb(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ')');
1057 value = rgb2hex(rgba);
1061 // Update swatch opacity
1062 swatchOpacity = settings.opacity ? input.attr('data-opacity') : 1;
1063 if(value.toLowerCase() === 'transparent') swatchOpacity = 0;
1065 .closest('.minicolors')
1066 .find('.minicolors-input-swatch > span')
1067 .css('opacity', swatchOpacity);
1073 if(input.val() === '') input.val(parseInput(settings.defaultValue, true));
1076 input.val(convertCase(input.val(), settings.letterCase));
1079 // Handle keypresses
1080 .on('keydown.minicolors', '.minicolors-input', function(event) {
1081 var input = $(this);
1082 if(!input.data('minicolors-initialized')) return;
1083 switch(event.keyCode) {
1095 .on('keyup.minicolors', '.minicolors-input', function() {
1096 var input = $(this);
1097 if(!input.data('minicolors-initialized')) return;
1098 updateFromInput(input, true);
1101 .on('paste.minicolors', '.minicolors-input', function() {
1102 var input = $(this);
1103 if(!input.data('minicolors-initialized')) return;
1104 setTimeout(function() {
1105 updateFromInput(input, true);