Subversion-Projekte lars-tiefland.ci

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

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