jquery-minicolors-rails 2.1.4.0 → 2.2.6.2

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