PluginProbe ʕ •ᴥ•ʔ
SiteOrigin CSS / trunk
SiteOrigin CSS vtrunk
1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.10 1.5.11 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 trunk 1.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.2.0
so-css / lib / minicolors / jquery.minicolors.js
so-css / lib / minicolors Last commit date
jquery.minicolors.css 9 years ago jquery.minicolors.js 9 years ago jquery.minicolors.min.js 6 years ago jquery.minicolors.png 9 years ago readme.md 9 years ago
jquery.minicolors.js
1136 lines
1 /*
2 * jQuery MiniColors: A tiny color picker built on jQuery
3 *
4 * Copyright: Cory LaViska for A Beautiful Site, LLC: http://www.abeautifulsite.net/
5 *
6 * Contribute: https://github.com/claviska/jquery-minicolors
7 *
8 * @license: http://opensource.org/licenses/MIT
9 *
10 */
11 (function (factory) {
12 /* jshint ignore:start */
13 if (typeof define === 'function' && define.amd) {
14 // AMD. Register as an anonymous module.
15 define(['jquery'], factory);
16 } else if (typeof exports === 'object') {
17 // Node/CommonJS
18 module.exports = factory(require('jquery'));
19 } else {
20 // Browser globals
21 factory(jQuery);
22 }
23 /* jshint ignore:end */
24 }(function ($) {
25
26 'use strict';
27
28 // Defaults
29 $.minicolors = {
30 defaults: {
31 animationSpeed: 50,
32 animationEasing: 'swing',
33 change: null,
34 changeDelay: 0,
35 control: 'hue',
36 dataUris: true,
37 defaultValue: '',
38 format: 'hex',
39 hide: null,
40 hideSpeed: 100,
41 inline: false,
42 keywords: '',
43 letterCase: 'lowercase',
44 opacity: false,
45 position: 'bottom left',
46 show: null,
47 showSpeed: 100,
48 theme: 'default',
49 swatches: []
50 }
51 };
52
53 // Public methods
54 $.extend($.fn, {
55 minicolors: function(method, data) {
56
57 switch(method) {
58
59 // Destroy the control
60 case 'destroy':
61 $(this).each( function() {
62 destroy($(this));
63 });
64 return $(this);
65
66 // Hide the color picker
67 case 'hide':
68 hide();
69 return $(this);
70
71 // Get/set opacity
72 case 'opacity':
73 // Getter
74 if( data === undefined ) {
75 // Getter
76 return $(this).attr('data-opacity');
77 } else {
78 // Setter
79 $(this).each( function() {
80 updateFromInput($(this).attr('data-opacity', data));
81 });
82 }
83 return $(this);
84
85 // Get an RGB(A) object based on the current color/opacity
86 case 'rgbObject':
87 return rgbObject($(this), method === 'rgbaObject');
88
89 // Get an RGB(A) string based on the current color/opacity
90 case 'rgbString':
91 case 'rgbaString':
92 return rgbString($(this), method === 'rgbaString');
93
94 // Get/set settings on the fly
95 case 'settings':
96 if( data === undefined ) {
97 return $(this).data('minicolors-settings');
98 } else {
99 // Setter
100 $(this).each( function() {
101 var settings = $(this).data('minicolors-settings') || {};
102 destroy($(this));
103 $(this).minicolors($.extend(true, settings, data));
104 });
105 }
106 return $(this);
107
108 // Show the color picker
109 case 'show':
110 show( $(this).eq(0) );
111 return $(this);
112
113 // Get/set the hex color value
114 case 'value':
115 if( data === undefined ) {
116 // Getter
117 return $(this).val();
118 } else {
119 // Setter
120 $(this).each( function() {
121 if( typeof(data) === 'object' && typeof(data) !== null ) {
122 if( data.opacity ) {
123 $(this).attr('data-opacity', keepWithin(data.opacity, 0, 1));
124 }
125 if( data.color ) {
126 $(this).val(data.color);
127 }
128 } else {
129 $(this).val(data);
130 }
131 updateFromInput($(this));
132 });
133 }
134 return $(this);
135
136 // Initializes the control
137 default:
138 if( method !== 'create' ) data = method;
139 $(this).each( function() {
140 init($(this), data);
141 });
142 return $(this);
143
144 }
145
146 }
147 });
148
149 // Initialize input elements
150 function init(input, settings) {
151
152 var minicolors = $('<div class="minicolors" />'),
153 defaults = $.minicolors.defaults,
154 size,
155 swatches,
156 swatch,
157 panel,
158 i;
159
160 // Do nothing if already initialized
161 if( input.data('minicolors-initialized') ) return;
162
163 // Handle settings
164 settings = $.extend(true, {}, defaults, settings);
165
166 // The wrapper
167 minicolors
168 .addClass('minicolors-theme-' + settings.theme)
169 .toggleClass('minicolors-with-opacity', settings.opacity)
170 .toggleClass('minicolors-no-data-uris', settings.dataUris !== true);
171
172 // Custom positioning
173 if( settings.position !== undefined ) {
174 $.each(settings.position.split(' '), function() {
175 minicolors.addClass('minicolors-position-' + this);
176 });
177 }
178
179 // Input size
180 if( settings.format === 'rgb' ) {
181 size = settings.opacity ? '25' : '20';
182 } else {
183 size = settings.keywords ? '11' : '7';
184 }
185
186 // The input
187 input
188 .addClass('minicolors-input')
189 .data('minicolors-initialized', false)
190 .data('minicolors-settings', settings)
191 .prop('size', size)
192 .wrap(minicolors)
193 .after(
194 '<div class="minicolors-panel minicolors-slider-' + settings.control + '">' +
195 '<div class="minicolors-slider minicolors-sprite">' +
196 '<div class="minicolors-picker"></div>' +
197 '</div>' +
198 '<div class="minicolors-opacity-slider minicolors-sprite">' +
199 '<div class="minicolors-picker"></div>' +
200 '</div>' +
201 '<div class="minicolors-grid minicolors-sprite">' +
202 '<div class="minicolors-grid-inner"></div>' +
203 '<div class="minicolors-picker"><div></div></div>' +
204 '</div>' +
205 '</div>'
206 );
207
208 // The swatch
209 if( !settings.inline ) {
210 input.after('<span class="minicolors-swatch minicolors-sprite minicolors-input-swatch"><span class="minicolors-swatch-color"></span></span>');
211 input.next('.minicolors-input-swatch').on('click', function(event) {
212 event.preventDefault();
213 input.focus();
214 });
215 }
216
217 // Prevent text selection in IE
218 panel = input.parent().find('.minicolors-panel');
219 panel.on('selectstart', function() { return false; }).end();
220
221 // Swatches
222 if (settings.swatches && settings.swatches.length !== 0) {
223 if (settings.swatches.length > 7) {
224 settings.swatches.length = 7;
225 }
226 panel.addClass('minicolors-with-swatches');
227 swatches = $('<ul class="minicolors-swatches"></ul>')
228 .appendTo(panel);
229 for(i = 0; i < settings.swatches.length; ++i) {
230 swatch = settings.swatches[i];
231 swatch = isRgb(swatch) ? parseRgb(swatch, true) : hex2rgb(parseHex(swatch, true));
232 $('<li class="minicolors-swatch minicolors-sprite"><span class="minicolors-swatch-color"></span></li>')
233 .appendTo(swatches)
234 .data('swatch-color', settings.swatches[i])
235 .find('.minicolors-swatch-color')
236 .css({
237 backgroundColor: rgb2hex(swatch),
238 opacity: swatch.a
239 });
240 settings.swatches[i] = swatch;
241 }
242
243 }
244
245 // Inline controls
246 if( settings.inline ) input.parent().addClass('minicolors-inline');
247
248 updateFromInput(input, false);
249
250 input.data('minicolors-initialized', true);
251
252 }
253
254 // Returns the input back to its original state
255 function destroy(input) {
256
257 var minicolors = input.parent();
258
259 // Revert the input element
260 input
261 .removeData('minicolors-initialized')
262 .removeData('minicolors-settings')
263 .removeProp('size')
264 .removeClass('minicolors-input');
265
266 // Remove the wrap and destroy whatever remains
267 minicolors.before(input).remove();
268
269 }
270
271 // Shows the specified dropdown panel
272 function show(input) {
273
274 var minicolors = input.parent(),
275 panel = minicolors.find('.minicolors-panel'),
276 settings = input.data('minicolors-settings');
277
278 // Do nothing if uninitialized, disabled, inline, or already open
279 if( !input.data('minicolors-initialized') ||
280 input.prop('disabled') ||
281 minicolors.hasClass('minicolors-inline') ||
282 minicolors.hasClass('minicolors-focus')
283 ) return;
284
285 hide();
286
287 minicolors.addClass('minicolors-focus');
288 panel
289 .stop(true, true)
290 .fadeIn(settings.showSpeed, function() {
291 if( settings.show ) settings.show.call(input.get(0));
292 });
293
294 }
295
296 // Hides all dropdown panels
297 function hide() {
298
299 $('.minicolors-focus').each( function() {
300
301 var minicolors = $(this),
302 input = minicolors.find('.minicolors-input'),
303 panel = minicolors.find('.minicolors-panel'),
304 settings = input.data('minicolors-settings');
305
306 panel.fadeOut(settings.hideSpeed, function() {
307 if( settings.hide ) settings.hide.call(input.get(0));
308 minicolors.removeClass('minicolors-focus');
309 });
310
311 });
312 }
313
314 // Moves the selected picker
315 function move(target, event, animate) {
316
317 var input = target.parents('.minicolors').find('.minicolors-input'),
318 settings = input.data('minicolors-settings'),
319 picker = target.find('[class$=-picker]'),
320 offsetX = target.offset().left,
321 offsetY = target.offset().top,
322 x = Math.round(event.pageX - offsetX),
323 y = Math.round(event.pageY - offsetY),
324 duration = animate ? settings.animationSpeed : 0,
325 wx, wy, r, phi;
326
327 // Touch support
328 if( event.originalEvent.changedTouches ) {
329 x = event.originalEvent.changedTouches[0].pageX - offsetX;
330 y = event.originalEvent.changedTouches[0].pageY - offsetY;
331 }
332
333 // Constrain picker to its container
334 if( x < 0 ) x = 0;
335 if( y < 0 ) y = 0;
336 if( x > target.width() ) x = target.width();
337 if( y > target.height() ) y = target.height();
338
339 // Constrain color wheel values to the wheel
340 if( target.parent().is('.minicolors-slider-wheel') && picker.parent().is('.minicolors-grid') ) {
341 wx = 75 - x;
342 wy = 75 - y;
343 r = Math.sqrt(wx * wx + wy * wy);
344 phi = Math.atan2(wy, wx);
345 if( phi < 0 ) phi += Math.PI * 2;
346 if( r > 75 ) {
347 r = 75;
348 x = 75 - (75 * Math.cos(phi));
349 y = 75 - (75 * Math.sin(phi));
350 }
351 x = Math.round(x);
352 y = Math.round(y);
353 }
354
355 // Move the picker
356 if( target.is('.minicolors-grid') ) {
357 picker
358 .stop(true)
359 .animate({
360 top: y + 'px',
361 left: x + 'px'
362 }, duration, settings.animationEasing, function() {
363 updateFromControl(input, target);
364 });
365 } else {
366 picker
367 .stop(true)
368 .animate({
369 top: y + 'px'
370 }, duration, settings.animationEasing, function() {
371 updateFromControl(input, target);
372 });
373 }
374
375 }
376
377 // Sets the input based on the color picker values
378 function updateFromControl(input, target) {
379
380 function getCoords(picker, container) {
381
382 var left, top;
383 if( !picker.length || !container ) return null;
384 left = picker.offset().left;
385 top = picker.offset().top;
386
387 return {
388 x: left - container.offset().left + (picker.outerWidth() / 2),
389 y: top - container.offset().top + (picker.outerHeight() / 2)
390 };
391
392 }
393
394 var hue, saturation, brightness, x, y, r, phi,
395
396 hex = input.val(),
397 opacity = input.attr('data-opacity'),
398
399 // Helpful references
400 minicolors = input.parent(),
401 settings = input.data('minicolors-settings'),
402 swatch = minicolors.find('.minicolors-input-swatch'),
403
404 // Panel objects
405 grid = minicolors.find('.minicolors-grid'),
406 slider = minicolors.find('.minicolors-slider'),
407 opacitySlider = minicolors.find('.minicolors-opacity-slider'),
408
409 // Picker objects
410 gridPicker = grid.find('[class$=-picker]'),
411 sliderPicker = slider.find('[class$=-picker]'),
412 opacityPicker = opacitySlider.find('[class$=-picker]'),
413
414 // Picker positions
415 gridPos = getCoords(gridPicker, grid),
416 sliderPos = getCoords(sliderPicker, slider),
417 opacityPos = getCoords(opacityPicker, opacitySlider);
418
419 // Handle colors
420 if( target.is('.minicolors-grid, .minicolors-slider, .minicolors-opacity-slider') ) {
421
422 // Determine HSB values
423 switch(settings.control) {
424
425 case 'wheel':
426 // Calculate hue, saturation, and brightness
427 x = (grid.width() / 2) - gridPos.x;
428 y = (grid.height() / 2) - gridPos.y;
429 r = Math.sqrt(x * x + y * y);
430 phi = Math.atan2(y, x);
431 if( phi < 0 ) phi += Math.PI * 2;
432 if( r > 75 ) {
433 r = 75;
434 gridPos.x = 69 - (75 * Math.cos(phi));
435 gridPos.y = 69 - (75 * Math.sin(phi));
436 }
437 saturation = keepWithin(r / 0.75, 0, 100);
438 hue = keepWithin(phi * 180 / Math.PI, 0, 360);
439 brightness = keepWithin(100 - Math.floor(sliderPos.y * (100 / slider.height())), 0, 100);
440 hex = hsb2hex({
441 h: hue,
442 s: saturation,
443 b: brightness
444 });
445
446 // Update UI
447 slider.css('backgroundColor', hsb2hex({ h: hue, s: saturation, b: 100 }));
448 break;
449
450 case 'saturation':
451 // Calculate hue, saturation, and brightness
452 hue = keepWithin(parseInt(gridPos.x * (360 / grid.width()), 10), 0, 360);
453 saturation = keepWithin(100 - Math.floor(sliderPos.y * (100 / slider.height())), 0, 100);
454 brightness = keepWithin(100 - Math.floor(gridPos.y * (100 / grid.height())), 0, 100);
455 hex = hsb2hex({
456 h: hue,
457 s: saturation,
458 b: brightness
459 });
460
461 // Update UI
462 slider.css('backgroundColor', hsb2hex({ h: hue, s: 100, b: brightness }));
463 minicolors.find('.minicolors-grid-inner').css('opacity', saturation / 100);
464 break;
465
466 case 'brightness':
467 // Calculate hue, saturation, and brightness
468 hue = keepWithin(parseInt(gridPos.x * (360 / grid.width()), 10), 0, 360);
469 saturation = keepWithin(100 - Math.floor(gridPos.y * (100 / grid.height())), 0, 100);
470 brightness = keepWithin(100 - Math.floor(sliderPos.y * (100 / slider.height())), 0, 100);
471 hex = hsb2hex({
472 h: hue,
473 s: saturation,
474 b: brightness
475 });
476
477 // Update UI
478 slider.css('backgroundColor', hsb2hex({ h: hue, s: saturation, b: 100 }));
479 minicolors.find('.minicolors-grid-inner').css('opacity', 1 - (brightness / 100));
480 break;
481
482 default:
483 // Calculate hue, saturation, and brightness
484 hue = keepWithin(360 - parseInt(sliderPos.y * (360 / slider.height()), 10), 0, 360);
485 saturation = keepWithin(Math.floor(gridPos.x * (100 / grid.width())), 0, 100);
486 brightness = keepWithin(100 - Math.floor(gridPos.y * (100 / grid.height())), 0, 100);
487 hex = hsb2hex({
488 h: hue,
489 s: saturation,
490 b: brightness
491 });
492
493 // Update UI
494 grid.css('backgroundColor', hsb2hex({ h: hue, s: 100, b: 100 }));
495 break;
496
497 }
498
499 // Handle opacity
500 if( settings.opacity ) {
501 opacity = parseFloat(1 - (opacityPos.y / opacitySlider.height())).toFixed(2);
502 } else {
503 opacity = 1;
504 }
505
506 updateInput(input, hex, opacity);
507 }
508 else {
509 // Set swatch color
510 swatch.find('span').css({
511 backgroundColor: hex,
512 opacity: opacity
513 });
514
515 // Handle change event
516 doChange(input, hex, opacity);
517 }
518 }
519
520 // Sets the value of the input and does the appropriate conversions
521 // to respect settings, also updates the swatch
522 function updateInput(input, value, opacity) {
523 var rgb,
524
525 // Helpful references
526 minicolors = input.parent(),
527 settings = input.data('minicolors-settings'),
528 swatch = minicolors.find('.minicolors-input-swatch');
529
530 if( settings.opacity ) input.attr('data-opacity', opacity);
531
532 // Set color string
533 if( settings.format === 'rgb' ) {
534 // Returns RGB(A) string
535
536 // Checks for input format and does the conversion
537 if ( isRgb(value) ) {
538 rgb = parseRgb(value, true);
539 }
540 else {
541 rgb = hex2rgb(parseHex(value, true));
542 }
543
544 opacity = input.attr('data-opacity') === '' ? 1 : keepWithin( parseFloat( input.attr('data-opacity') ).toFixed(2), 0, 1 );
545 if( isNaN( opacity ) || !settings.opacity ) opacity = 1;
546
547 if( input.minicolors('rgbObject').a <= 1 && rgb && settings.opacity) {
548 // Set RGBA string if alpha
549 value = 'rgba(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ', ' + parseFloat( opacity ) + ')';
550 } else {
551 // Set RGB string (alpha = 1)
552 value = 'rgb(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ')';
553 }
554 } else {
555 // Returns hex color
556
557 // Checks for input format and does the conversion
558 if ( isRgb(value) ) {
559 value = rgbString2hex(value);
560 }
561
562 value = convertCase( value, settings.letterCase );
563 }
564
565 // Update value from picker
566 input.val( value );
567
568 // Set swatch color
569 swatch.find('span').css({
570 backgroundColor: value,
571 opacity: opacity
572 });
573
574 // Handle change event
575 doChange(input, value, opacity);
576 }
577
578 // Sets the color picker values from the input
579 function updateFromInput(input, preserveInputValue) {
580
581 var hex,
582 hsb,
583 opacity,
584 keywords,
585 alpha,
586 value,
587 x, y, r, phi,
588
589 // Helpful references
590 minicolors = input.parent(),
591 settings = input.data('minicolors-settings'),
592 swatch = minicolors.find('.minicolors-input-swatch'),
593
594 // Panel objects
595 grid = minicolors.find('.minicolors-grid'),
596 slider = minicolors.find('.minicolors-slider'),
597 opacitySlider = minicolors.find('.minicolors-opacity-slider'),
598
599 // Picker objects
600 gridPicker = grid.find('[class$=-picker]'),
601 sliderPicker = slider.find('[class$=-picker]'),
602 opacityPicker = opacitySlider.find('[class$=-picker]');
603
604 // Determine hex/HSB values
605 if( isRgb(input.val()) ) {
606 // If input value is a rgb(a) string, convert it to hex color and update opacity
607 hex = rgbString2hex(input.val());
608 alpha = keepWithin(parseFloat(getAlpha(input.val())).toFixed(2), 0, 1);
609 if( alpha ) {
610 input.attr('data-opacity', alpha);
611 }
612 } else {
613 hex = convertCase(parseHex(input.val(), true), settings.letterCase);
614 }
615
616 if( !hex ){
617 hex = convertCase(parseInput(settings.defaultValue, true), settings.letterCase);
618 }
619 hsb = hex2hsb(hex);
620
621 // Get array of lowercase keywords
622 keywords = !settings.keywords ? [] : $.map(settings.keywords.split(','), function(a) {
623 return $.trim(a.toLowerCase());
624 });
625
626 // Set color string
627 if( input.val() !== '' && $.inArray(input.val().toLowerCase(), keywords) > -1 ) {
628 value = convertCase(input.val());
629 } else {
630 value = isRgb(input.val()) ? parseRgb(input.val()) : hex;
631 }
632
633 // Update input value
634 if( !preserveInputValue ) input.val(value);
635
636 // Determine opacity value
637 if( settings.opacity ) {
638 // Get from data-opacity attribute and keep within 0-1 range
639 opacity = input.attr('data-opacity') === '' ? 1 : keepWithin(parseFloat(input.attr('data-opacity')).toFixed(2), 0, 1);
640 if( isNaN(opacity) ) opacity = 1;
641 input.attr('data-opacity', opacity);
642 swatch.find('span').css('opacity', opacity);
643
644 // Set opacity picker position
645 y = keepWithin(opacitySlider.height() - (opacitySlider.height() * opacity), 0, opacitySlider.height());
646 opacityPicker.css('top', y + 'px');
647 }
648
649 // Set opacity to zero if input value is transparent
650 if( input.val().toLowerCase() === 'transparent' ) {
651 swatch.find('span').css('opacity', 0);
652 }
653
654 // Update swatch
655 swatch.find('span').css('backgroundColor', hex);
656
657 // Determine picker locations
658 switch(settings.control) {
659
660 case 'wheel':
661 // Set grid position
662 r = keepWithin(Math.ceil(hsb.s * 0.75), 0, grid.height() / 2);
663 phi = hsb.h * Math.PI / 180;
664 x = keepWithin(75 - Math.cos(phi) * r, 0, grid.width());
665 y = keepWithin(75 - Math.sin(phi) * r, 0, grid.height());
666 gridPicker.css({
667 top: y + 'px',
668 left: x + 'px'
669 });
670
671 // Set slider position
672 y = 150 - (hsb.b / (100 / grid.height()));
673 if( hex === '' ) y = 0;
674 sliderPicker.css('top', y + 'px');
675
676 // Update panel color
677 slider.css('backgroundColor', hsb2hex({ h: hsb.h, s: hsb.s, b: 100 }));
678 break;
679
680 case 'saturation':
681 // Set grid position
682 x = keepWithin((5 * hsb.h) / 12, 0, 150);
683 y = keepWithin(grid.height() - Math.ceil(hsb.b / (100 / grid.height())), 0, grid.height());
684 gridPicker.css({
685 top: y + 'px',
686 left: x + 'px'
687 });
688
689 // Set slider position
690 y = keepWithin(slider.height() - (hsb.s * (slider.height() / 100)), 0, slider.height());
691 sliderPicker.css('top', y + 'px');
692
693 // Update UI
694 slider.css('backgroundColor', hsb2hex({ h: hsb.h, s: 100, b: hsb.b }));
695 minicolors.find('.minicolors-grid-inner').css('opacity', hsb.s / 100);
696 break;
697
698 case 'brightness':
699 // Set grid position
700 x = keepWithin((5 * hsb.h) / 12, 0, 150);
701 y = keepWithin(grid.height() - Math.ceil(hsb.s / (100 / grid.height())), 0, grid.height());
702 gridPicker.css({
703 top: y + 'px',
704 left: x + 'px'
705 });
706
707 // Set slider position
708 y = keepWithin(slider.height() - (hsb.b * (slider.height() / 100)), 0, slider.height());
709 sliderPicker.css('top', y + 'px');
710
711 // Update UI
712 slider.css('backgroundColor', hsb2hex({ h: hsb.h, s: hsb.s, b: 100 }));
713 minicolors.find('.minicolors-grid-inner').css('opacity', 1 - (hsb.b / 100));
714 break;
715
716 default:
717 // Set grid position
718 x = keepWithin(Math.ceil(hsb.s / (100 / grid.width())), 0, grid.width());
719 y = keepWithin(grid.height() - Math.ceil(hsb.b / (100 / grid.height())), 0, grid.height());
720 gridPicker.css({
721 top: y + 'px',
722 left: x + 'px'
723 });
724
725 // Set slider position
726 y = keepWithin(slider.height() - (hsb.h / (360 / slider.height())), 0, slider.height());
727 sliderPicker.css('top', y + 'px');
728
729 // Update panel color
730 grid.css('backgroundColor', hsb2hex({ h: hsb.h, s: 100, b: 100 }));
731 break;
732
733 }
734
735 // Fire change event, but only if minicolors is fully initialized
736 if( input.data('minicolors-initialized') ) {
737 doChange(input, value, opacity);
738 }
739
740 }
741
742 // Runs the change and changeDelay callbacks
743 function doChange(input, value, opacity) {
744
745 var settings = input.data('minicolors-settings'),
746 lastChange = input.data('minicolors-lastChange'),
747 obj,
748 sel,
749 i;
750
751 // Only run if it actually changed
752 if( !lastChange || lastChange.value !== value || lastChange.opacity !== opacity ) {
753
754 // Remember last-changed value
755 input.data('minicolors-lastChange', {
756 value: value,
757 opacity: opacity
758 });
759
760 // Check and select applicable swatch
761 if (settings.swatches && settings.swatches.length !== 0) {
762 if(!isRgb(value)) {
763 obj = hex2rgb(value);
764 }
765 else {
766 obj = parseRgb(value, true);
767 }
768 sel = -1;
769 for(i = 0; i < settings.swatches.length; ++i) {
770 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) {
771 sel = i;
772 break;
773 }
774 }
775
776 input.parent().find('.minicolors-swatches .minicolors-swatch').removeClass('selected');
777 if (i !== -1) {
778 input.parent().find('.minicolors-swatches .minicolors-swatch').eq(i).addClass('selected');
779 }
780 }
781
782 // Fire change event
783 if( settings.change ) {
784 if( settings.changeDelay ) {
785 // Call after a delay
786 clearTimeout(input.data('minicolors-changeTimeout'));
787 input.data('minicolors-changeTimeout', setTimeout( function() {
788 settings.change.call(input.get(0), value, opacity);
789 }, settings.changeDelay));
790 } else {
791 // Call immediately
792 settings.change.call(input.get(0), value, opacity);
793 }
794 }
795 input.trigger('change').trigger('input');
796 }
797
798 }
799
800 // Generates an RGB(A) object based on the input's value
801 function rgbObject(input) {
802 var hex = parseHex($(input).val(), true),
803 rgb = hex2rgb(hex),
804 opacity = $(input).attr('data-opacity');
805 if( !rgb ) return null;
806 if( opacity !== undefined ) $.extend(rgb, { a: parseFloat(opacity) });
807 return rgb;
808 }
809
810 // Generates an RGB(A) string based on the input's value
811 function rgbString(input, alpha) {
812 var hex = parseHex($(input).val(), true),
813 rgb = hex2rgb(hex),
814 opacity = $(input).attr('data-opacity');
815 if( !rgb ) return null;
816 if( opacity === undefined ) opacity = 1;
817 if( alpha ) {
818 return 'rgba(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ', ' + parseFloat(opacity) + ')';
819 } else {
820 return 'rgb(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ')';
821 }
822 }
823
824 // Converts to the letter case specified in settings
825 function convertCase(string, letterCase) {
826 return letterCase === 'uppercase' ? string.toUpperCase() : string.toLowerCase();
827 }
828
829 // Parses a string and returns a valid hex string when possible
830 function parseHex(string, expand) {
831 string = string.replace(/^#/g, '');
832 if( !string.match(/^[A-F0-9]{3,6}/ig) ) return '';
833 if( string.length !== 3 && string.length !== 6 ) return '';
834 if( string.length === 3 && expand ) {
835 string = string[0] + string[0] + string[1] + string[1] + string[2] + string[2];
836 }
837 return '#' + string;
838 }
839
840 // Parses a string and returns a valid RGB(A) string when possible
841 function parseRgb(string, obj) {
842
843 var values = string.replace(/[^\d,.]/g, ''),
844 rgba = values.split(',');
845
846 rgba[0] = keepWithin(parseInt(rgba[0], 10), 0, 255);
847 rgba[1] = keepWithin(parseInt(rgba[1], 10), 0, 255);
848 rgba[2] = keepWithin(parseInt(rgba[2], 10), 0, 255);
849 if( rgba[3] ) {
850 rgba[3] = keepWithin(parseFloat(rgba[3], 10), 0, 1);
851 }
852
853 // Return RGBA object
854 if( obj ) {
855 return {
856 r: rgba[0],
857 g: rgba[1],
858 b: rgba[2],
859 a: rgba[3] ? rgba[3] : null
860 };
861 }
862
863 // Return RGBA string
864 if( typeof(rgba[3]) !== 'undefined' && rgba[3] <= 1 ) {
865 return 'rgba(' + rgba[0] + ', ' + rgba[1] + ', ' + rgba[2] + ', ' + rgba[3] + ')';
866 } else {
867 return 'rgb(' + rgba[0] + ', ' + rgba[1] + ', ' + rgba[2] + ')';
868 }
869
870 }
871
872 // Parses a string and returns a valid color string when possible
873 function parseInput(string, expand) {
874 if( isRgb(string) ) {
875 // Returns a valid rgb(a) string
876 return parseRgb(string);
877 } else {
878 return parseHex(string, expand);
879 }
880 }
881
882 // Keeps value within min and max
883 function keepWithin(value, min, max) {
884 if( value < min ) value = min;
885 if( value > max ) value = max;
886 return value;
887 }
888
889 // Checks if a string is a valid RGB(A) string
890 function isRgb(string) {
891 var rgb = string.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
892 return (rgb && rgb.length === 4) ? true : false;
893 }
894
895 // Function to get alpha from a RGB(A) string
896 function getAlpha(rgba) {
897 rgba = rgba.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+(\.\d{1,2})?|\.\d{1,2})[\s+]?/i);
898 return (rgba && rgba.length === 6) ? rgba[4] : '1';
899 }
900
901 // Converts an HSB object to an RGB object
902 function hsb2rgb(hsb) {
903 var rgb = {};
904 var h = Math.round(hsb.h);
905 var s = Math.round(hsb.s * 255 / 100);
906 var v = Math.round(hsb.b * 255 / 100);
907 if(s === 0) {
908 rgb.r = rgb.g = rgb.b = v;
909 } else {
910 var t1 = v;
911 var t2 = (255 - s) * v / 255;
912 var t3 = (t1 - t2) * (h % 60) / 60;
913 if( h === 360 ) h = 0;
914 if( h < 60 ) { rgb.r = t1; rgb.b = t2; rgb.g = t2 + t3; }
915 else if( h < 120 ) {rgb.g = t1; rgb.b = t2; rgb.r = t1 - t3; }
916 else if( h < 180 ) {rgb.g = t1; rgb.r = t2; rgb.b = t2 + t3; }
917 else if( h < 240 ) {rgb.b = t1; rgb.r = t2; rgb.g = t1 - t3; }
918 else if( h < 300 ) {rgb.b = t1; rgb.g = t2; rgb.r = t2 + t3; }
919 else if( h < 360 ) {rgb.r = t1; rgb.g = t2; rgb.b = t1 - t3; }
920 else { rgb.r = 0; rgb.g = 0; rgb.b = 0; }
921 }
922 return {
923 r: Math.round(rgb.r),
924 g: Math.round(rgb.g),
925 b: Math.round(rgb.b)
926 };
927 }
928
929 // Converts an RGB string to a hex string
930 function rgbString2hex(rgb){
931 rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
932 return (rgb && rgb.length === 4) ? '#' +
933 ('0' + parseInt(rgb[1],10).toString(16)).slice(-2) +
934 ('0' + parseInt(rgb[2],10).toString(16)).slice(-2) +
935 ('0' + parseInt(rgb[3],10).toString(16)).slice(-2) : '';
936 }
937
938 // Converts an RGB object to a hex string
939 function rgb2hex(rgb) {
940 var hex = [
941 rgb.r.toString(16),
942 rgb.g.toString(16),
943 rgb.b.toString(16)
944 ];
945 $.each(hex, function(nr, val) {
946 if (val.length === 1) hex[nr] = '0' + val;
947 });
948 return '#' + hex.join('');
949 }
950
951 // Converts an HSB object to a hex string
952 function hsb2hex(hsb) {
953 return rgb2hex(hsb2rgb(hsb));
954 }
955
956 // Converts a hex string to an HSB object
957 function hex2hsb(hex) {
958 var hsb = rgb2hsb(hex2rgb(hex));
959 if( hsb.s === 0 ) hsb.h = 360;
960 return hsb;
961 }
962
963 // Converts an RGB object to an HSB object
964 function rgb2hsb(rgb) {
965 var hsb = { h: 0, s: 0, b: 0 };
966 var min = Math.min(rgb.r, rgb.g, rgb.b);
967 var max = Math.max(rgb.r, rgb.g, rgb.b);
968 var delta = max - min;
969 hsb.b = max;
970 hsb.s = max !== 0 ? 255 * delta / max : 0;
971 if( hsb.s !== 0 ) {
972 if( rgb.r === max ) {
973 hsb.h = (rgb.g - rgb.b) / delta;
974 } else if( rgb.g === max ) {
975 hsb.h = 2 + (rgb.b - rgb.r) / delta;
976 } else {
977 hsb.h = 4 + (rgb.r - rgb.g) / delta;
978 }
979 } else {
980 hsb.h = -1;
981 }
982 hsb.h *= 60;
983 if( hsb.h < 0 ) {
984 hsb.h += 360;
985 }
986 hsb.s *= 100/255;
987 hsb.b *= 100/255;
988 return hsb;
989 }
990
991 // Converts a hex string to an RGB object
992 function hex2rgb(hex) {
993 hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16);
994 return {
995 /* jshint ignore:start */
996 r: hex >> 16,
997 g: (hex & 0x00FF00) >> 8,
998 b: (hex & 0x0000FF)
999 /* jshint ignore:end */
1000 };
1001 }
1002
1003 // Handle events
1004 $([document, top.document])
1005 // Hide on clicks outside of the control
1006 .on('mousedown.minicolors touchstart.minicolors', function(event) {
1007 if( !$(event.target).parents().add(event.target).hasClass('minicolors') ) {
1008 hide();
1009 }
1010 })
1011 // Start moving
1012 .on('mousedown.minicolors touchstart.minicolors', '.minicolors-grid, .minicolors-slider, .minicolors-opacity-slider', function(event) {
1013 var target = $(this);
1014 event.preventDefault();
1015 $(event.delegateTarget).data('minicolors-target', target);
1016 move(target, event, true);
1017 })
1018 // Move pickers
1019 .on('mousemove.minicolors touchmove.minicolors', function(event) {
1020 var target = $(event.delegateTarget).data('minicolors-target');
1021 if( target ) move(target, event);
1022 })
1023 // Stop moving
1024 .on('mouseup.minicolors touchend.minicolors', function() {
1025 $(this).removeData('minicolors-target');
1026 })
1027 // Selected a swatch
1028 .on('click.minicolors', '.minicolors-swatches li', function(event) {
1029 event.preventDefault();
1030 var target = $(this), input = target.parents('.minicolors').find('.minicolors-input'), color = target.data('swatch-color');
1031 updateInput(input, color, getAlpha(color));
1032 updateFromInput(input);
1033 })
1034 // Show panel when swatch is clicked
1035 .on('mousedown.minicolors touchstart.minicolors', '.minicolors-input-swatch', function(event) {
1036 var input = $(this).parent().find('.minicolors-input');
1037 event.preventDefault();
1038 show(input);
1039 })
1040 // Show on focus
1041 .on('focus.minicolors', '.minicolors-input', function() {
1042 var input = $(this);
1043 if( !input.data('minicolors-initialized') ) return;
1044 show(input);
1045 })
1046 // Update value on blur
1047 .on('blur.minicolors', '.minicolors-input', function() {
1048 var input = $(this),
1049 settings = input.data('minicolors-settings'),
1050 keywords,
1051 hex,
1052 rgba,
1053 swatchOpacity,
1054 value;
1055
1056 if( !input.data('minicolors-initialized') ) return;
1057
1058 // Get array of lowercase keywords
1059 keywords = !settings.keywords ? [] : $.map(settings.keywords.split(','), function(a) {
1060 return $.trim(a.toLowerCase());
1061 });
1062
1063 // Set color string
1064 if( input.val() !== '' && $.inArray(input.val().toLowerCase(), keywords) > -1 ) {
1065 value = input.val();
1066 } else {
1067 // Get RGBA values for easy conversion
1068 if( isRgb(input.val()) ) {
1069 rgba = parseRgb(input.val(), true);
1070 } else {
1071 hex = parseHex(input.val(), true);
1072 rgba = hex ? hex2rgb(hex) : null;
1073 }
1074
1075 // Convert to format
1076 if( rgba === null ) {
1077 value = settings.defaultValue;
1078 } else if( settings.format === 'rgb' ) {
1079 value = settings.opacity ?
1080 parseRgb('rgba(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ',' + input.attr('data-opacity') + ')') :
1081 parseRgb('rgb(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ')');
1082 } else {
1083 value = rgb2hex(rgba);
1084 }
1085 }
1086
1087 // Update swatch opacity
1088 swatchOpacity = settings.opacity ? input.attr('data-opacity') : 1;
1089 if( value.toLowerCase() === 'transparent' ) swatchOpacity = 0;
1090 input
1091 .closest('.minicolors')
1092 .find('.minicolors-input-swatch > span')
1093 .css('opacity', swatchOpacity);
1094
1095 // Set input value
1096 input.val(value);
1097
1098 // Is it blank?
1099 if( input.val() === '' ) input.val(parseInput(settings.defaultValue, true));
1100
1101 // Adjust case
1102 input.val( convertCase(input.val(), settings.letterCase) );
1103
1104 })
1105 // Handle keypresses
1106 .on('keydown.minicolors', '.minicolors-input', function(event) {
1107 var input = $(this);
1108 if( !input.data('minicolors-initialized') ) return;
1109 switch(event.keyCode) {
1110 case 9: // tab
1111 hide();
1112 break;
1113 case 13: // enter
1114 case 27: // esc
1115 hide();
1116 input.blur();
1117 break;
1118 }
1119 })
1120 // Update on keyup
1121 .on('keyup.minicolors', '.minicolors-input', function() {
1122 var input = $(this);
1123 if( !input.data('minicolors-initialized') ) return;
1124 updateFromInput(input, true);
1125 })
1126 // Update on paste
1127 .on('paste.minicolors', '.minicolors-input', function() {
1128 var input = $(this);
1129 if( !input.data('minicolors-initialized') ) return;
1130 setTimeout( function() {
1131 updateFromInput(input, true);
1132 }, 1);
1133 });
1134
1135 }));
1136