js/colorpicker/bootstrap-colorpicker.js   F
last analyzed

Complexity

Total Complexity 183
Complexity/F 3.39

Size

Lines of Code 1014
Function Count 54

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 0
eloc 783
nc 0
dl 0
loc 1014
rs 1.817
c 0
b 0
f 0
wmc 183
mnd 4
bc 148
fnc 54
bpm 2.7407
cpm 3.3888
noi 15

1 Function

Rating   Name   Duplication   Size   Complexity  
B bootstrap-colorpicker.js ➔ ?!? 0 1006 1

How to fix   Complexity   

Complexity

Complex classes like js/colorpicker/bootstrap-colorpicker.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*!
2
 * Bootstrap Colorpicker
3
 * http://mjolnic.github.io/bootstrap-colorpicker/
4
 *
5
 * Originally written by (c) 2012 Stefan Petre
6
 * Licensed under the Apache License v2.0
7
 * http://www.apache.org/licenses/LICENSE-2.0.txt
8
 *
9
 * @todo Update DOCS
10
 */
11
12
(function(factory) {
13
        "use strict";
14
        if (typeof define === 'function' && define.amd) {
15
            define(['jquery'], factory);
16
        } else if (window.jQuery && !window.jQuery.fn.colorpicker) {
17
            factory(window.jQuery);
18
        }
19
    }
20
    (function($) {
21
        'use strict';
22
23
        // Color object
24
        var Color = function(val) {
25
            this.value = {
26
                h: 0,
27
                s: 0,
28
                b: 0,
29
                a: 1
30
            };
31
            this.origFormat = null; // original string format
32
            if (val) {
33
                if (val.toLowerCase !== undefined) {
34
                    // cast to string
35
                    val = val + '';
36
                    this.setColor(val);
37
                } else if (val.h !== undefined) {
38
                    this.value = val;
39
                }
40
            }
41
        };
42
43
        Color.prototype = {
44
            constructor: Color,
45
            // 140 predefined colors from the HTML Colors spec
46
            colors: {
47
                "aliceblue": "#f0f8ff",
48
                "antiquewhite": "#faebd7",
49
                "aqua": "#00ffff",
50
                "aquamarine": "#7fffd4",
51
                "azure": "#f0ffff",
52
                "beige": "#f5f5dc",
53
                "bisque": "#ffe4c4",
54
                "black": "#000000",
55
                "blanchedalmond": "#ffebcd",
56
                "blue": "#0000ff",
57
                "blueviolet": "#8a2be2",
58
                "brown": "#a52a2a",
59
                "burlywood": "#deb887",
60
                "cadetblue": "#5f9ea0",
61
                "chartreuse": "#7fff00",
62
                "chocolate": "#d2691e",
63
                "coral": "#ff7f50",
64
                "cornflowerblue": "#6495ed",
65
                "cornsilk": "#fff8dc",
66
                "crimson": "#dc143c",
67
                "cyan": "#00ffff",
68
                "darkblue": "#00008b",
69
                "darkcyan": "#008b8b",
70
                "darkgoldenrod": "#b8860b",
71
                "darkgray": "#a9a9a9",
72
                "darkgreen": "#006400",
73
                "darkkhaki": "#bdb76b",
74
                "darkmagenta": "#8b008b",
75
                "darkolivegreen": "#556b2f",
76
                "darkorange": "#ff8c00",
77
                "darkorchid": "#9932cc",
78
                "darkred": "#8b0000",
79
                "darksalmon": "#e9967a",
80
                "darkseagreen": "#8fbc8f",
81
                "darkslateblue": "#483d8b",
82
                "darkslategray": "#2f4f4f",
83
                "darkturquoise": "#00ced1",
84
                "darkviolet": "#9400d3",
85
                "deeppink": "#ff1493",
86
                "deepskyblue": "#00bfff",
87
                "dimgray": "#696969",
88
                "dodgerblue": "#1e90ff",
89
                "firebrick": "#b22222",
90
                "floralwhite": "#fffaf0",
91
                "forestgreen": "#228b22",
92
                "fuchsia": "#ff00ff",
93
                "gainsboro": "#dcdcdc",
94
                "ghostwhite": "#f8f8ff",
95
                "gold": "#ffd700",
96
                "goldenrod": "#daa520",
97
                "gray": "#808080",
98
                "green": "#008000",
99
                "greenyellow": "#adff2f",
100
                "honeydew": "#f0fff0",
101
                "hotpink": "#ff69b4",
102
                "indianred ": "#cd5c5c",
103
                "indigo ": "#4b0082",
104
                "ivory": "#fffff0",
105
                "khaki": "#f0e68c",
106
                "lavender": "#e6e6fa",
107
                "lavenderblush": "#fff0f5",
108
                "lawngreen": "#7cfc00",
109
                "lemonchiffon": "#fffacd",
110
                "lightblue": "#add8e6",
111
                "lightcoral": "#f08080",
112
                "lightcyan": "#e0ffff",
113
                "lightgoldenrodyellow": "#fafad2",
114
                "lightgrey": "#d3d3d3",
115
                "lightgreen": "#90ee90",
116
                "lightpink": "#ffb6c1",
117
                "lightsalmon": "#ffa07a",
118
                "lightseagreen": "#20b2aa",
119
                "lightskyblue": "#87cefa",
120
                "lightslategray": "#778899",
121
                "lightsteelblue": "#b0c4de",
122
                "lightyellow": "#ffffe0",
123
                "lime": "#00ff00",
124
                "limegreen": "#32cd32",
125
                "linen": "#faf0e6",
126
                "magenta": "#ff00ff",
127
                "maroon": "#800000",
128
                "mediumaquamarine": "#66cdaa",
129
                "mediumblue": "#0000cd",
130
                "mediumorchid": "#ba55d3",
131
                "mediumpurple": "#9370d8",
132
                "mediumseagreen": "#3cb371",
133
                "mediumslateblue": "#7b68ee",
134
                "mediumspringgreen": "#00fa9a",
135
                "mediumturquoise": "#48d1cc",
136
                "mediumvioletred": "#c71585",
137
                "midnightblue": "#191970",
138
                "mintcream": "#f5fffa",
139
                "mistyrose": "#ffe4e1",
140
                "moccasin": "#ffe4b5",
141
                "navajowhite": "#ffdead",
142
                "navy": "#000080",
143
                "oldlace": "#fdf5e6",
144
                "olive": "#808000",
145
                "olivedrab": "#6b8e23",
146
                "orange": "#ffa500",
147
                "orangered": "#ff4500",
148
                "orchid": "#da70d6",
149
                "palegoldenrod": "#eee8aa",
150
                "palegreen": "#98fb98",
151
                "paleturquoise": "#afeeee",
152
                "palevioletred": "#d87093",
153
                "papayawhip": "#ffefd5",
154
                "peachpuff": "#ffdab9",
155
                "peru": "#cd853f",
156
                "pink": "#ffc0cb",
157
                "plum": "#dda0dd",
158
                "powderblue": "#b0e0e6",
159
                "purple": "#800080",
160
                "red": "#ff0000",
161
                "rosybrown": "#bc8f8f",
162
                "royalblue": "#4169e1",
163
                "saddlebrown": "#8b4513",
164
                "salmon": "#fa8072",
165
                "sandybrown": "#f4a460",
166
                "seagreen": "#2e8b57",
167
                "seashell": "#fff5ee",
168
                "sienna": "#a0522d",
169
                "silver": "#c0c0c0",
170
                "skyblue": "#87ceeb",
171
                "slateblue": "#6a5acd",
172
                "slategray": "#708090",
173
                "snow": "#fffafa",
174
                "springgreen": "#00ff7f",
175
                "steelblue": "#4682b4",
176
                "tan": "#d2b48c",
177
                "teal": "#008080",
178
                "thistle": "#d8bfd8",
179
                "tomato": "#ff6347",
180
                "turquoise": "#40e0d0",
181
                "violet": "#ee82ee",
182
                "wheat": "#f5deb3",
183
                "white": "#ffffff",
184
                "whitesmoke": "#f5f5f5",
185
                "yellow": "#ffff00",
186
                "yellowgreen": "#9acd32",
187
                "transparent": "transparent"
188
            },
189
            _sanitizeNumber: function(val) {
190
                if (typeof val === 'number') {
191
                    return val;
192
                }
193
                if (isNaN(val) || (val === null) || (val === '') || (val === undefined)) {
194
                    return 1;
195
                }
196
                if (val.toLowerCase !== undefined) {
197
                    return parseFloat(val);
198
                }
199
                return 1;
200
            },
201
            isTransparent: function(strVal) {
202
                if (!strVal) {
203
                    return false;
204
                }
205
                strVal = strVal.toLowerCase().trim();
206
                return (strVal == 'transparent') || (strVal.match(/#?00000000/)) || (strVal.match(/(rgba|hsla)\(0,0,0,0?\.?0\)/));
207
            },
208
            rgbaIsTransparent: function(rgba) {
209
                return ((rgba.r == 0) && (rgba.g == 0) && (rgba.b == 0) && (rgba.a == 0));
210
            },
211
            //parse a string to HSB
212
            setColor: function(strVal) {
213
                strVal = strVal.toLowerCase().trim();
214
                if (strVal) {
215
                    if (this.isTransparent(strVal)) {
216
                        this.value = {
217
                            h: 0,
218
                            s: 0,
219
                            b: 0,
220
                            a: 0
221
                        }
222
                    } else {
223
                        this.value = this.stringToHSB(strVal) || {
224
                            h: 0,
225
                            s: 0,
226
                            b: 0,
227
                            a: 1
228
                        }; // if parser fails, defaults to black
229
                    }
230
                }
231
            },
232
            stringToHSB: function(strVal) {
233
                strVal = strVal.toLowerCase();
234
                var that = this,
235
                    result = false;
236
                $.each(this.stringParsers, function(i, parser) {
237
                    var match = parser.re.exec(strVal),
238
                        values = match && parser.parse.apply(that, [match]),
239
                        format = parser.format || 'rgba';
240
                    if (values) {
241
                        if (format.match(/hsla?/)) {
242
                            result = that.RGBtoHSB.apply(that, that.HSLtoRGB.apply(that, values));
243
                        } else {
244
                            result = that.RGBtoHSB.apply(that, values);
245
                        }
246
                        that.origFormat = format;
247
                        return false;
248
                    }
249
                    return true;
250
                });
251
                return result;
252
            },
253
            setHue: function(h) {
254
                this.value.h = 1 - h;
255
            },
256
            setSaturation: function(s) {
257
                this.value.s = s;
258
            },
259
            setBrightness: function(b) {
260
                this.value.b = 1 - b;
261
            },
262
            setAlpha: function(a) {
263
                this.value.a = parseInt((1 - a) * 100, 10) / 100;
264
            },
265
            toRGB: function(h, s, b, a) {
266
                if (!h) {
267
                    h = this.value.h;
268
                    s = this.value.s;
269
                    b = this.value.b;
270
                }
271
                h *= 360;
272
                var R, G, B, X, C;
273
                h = (h % 360) / 60;
274
                C = b * s;
275
                X = C * (1 - Math.abs(h % 2 - 1));
276
                R = G = B = b - C;
277
278
                h = ~~h;
279
                R += [C, X, 0, 0, X, C][h];
280
                G += [X, C, C, X, 0, 0][h];
281
                B += [0, 0, X, C, C, X][h];
282
                return {
283
                    r: Math.round(R * 255),
284
                    g: Math.round(G * 255),
285
                    b: Math.round(B * 255),
286
                    a: a || this.value.a
287
                };
288
            },
289
            toHex: function(h, s, b, a) {
290
                var rgb = this.toRGB(h, s, b, a);
291
                if (this.rgbaIsTransparent(rgb)) {
292
                    return 'transparent';
293
                }
294
                return '#' + ((1 << 24) | (parseInt(rgb.r) << 16) | (parseInt(rgb.g) << 8) | parseInt(rgb.b)).toString(16).substr(1);
295
            },
296
            toHSL: function(h, s, b, a) {
297
                h = h || this.value.h;
298
                s = s || this.value.s;
299
                b = b || this.value.b;
300
                a = a || this.value.a;
301
302
                var H = h,
303
                    L = (2 - s) * b,
304
                    S = s * b;
305
                if (L > 0 && L <= 1) {
306
                    S /= L;
307
                } else {
308
                    S /= 2 - L;
309
                }
310
                L /= 2;
311
                if (S > 1) {
312
                    S = 1;
313
                }
314
                return {
315
                    h: isNaN(H) ? 0 : H,
316
                    s: isNaN(S) ? 0 : S,
317
                    l: isNaN(L) ? 0 : L,
318
                    a: isNaN(a) ? 0 : a
319
                };
320
            },
321
            toAlias: function(r, g, b, a) {
322
                var rgb = this.toHex(r, g, b, a);
323
                for (var alias in this.colors) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
324
                    if (this.colors[alias] == rgb) {
325
                        return alias;
326
                    }
327
                }
328
                return false;
329
            },
330
            RGBtoHSB: function(r, g, b, a) {
331
                r /= 255;
332
                g /= 255;
333
                b /= 255;
334
335
                var H, S, V, C;
336
                V = Math.max(r, g, b);
337
                C = V - Math.min(r, g, b);
338
                H = (C === 0 ? null :
339
                    V === r ? (g - b) / C :
340
                    V === g ? (b - r) / C + 2 :
341
                    (r - g) / C + 4
342
                );
343
                H = ((H + 360) % 6) * 60 / 360;
344
                S = C === 0 ? 0 : C / V;
345
                return {
346
                    h: this._sanitizeNumber(H),
347
                    s: S,
348
                    b: V,
349
                    a: this._sanitizeNumber(a)
350
                };
351
            },
352
            HueToRGB: function(p, q, h) {
353
                if (h < 0) {
354
                    h += 1;
355
                } else if (h > 1) {
356
                    h -= 1;
357
                }
358
                if ((h * 6) < 1) {
359
                    return p + (q - p) * h * 6;
360
                } else if ((h * 2) < 1) {
361
                    return q;
362
                } else if ((h * 3) < 2) {
363
                    return p + (q - p) * ((2 / 3) - h) * 6;
364
                } else {
365
                    return p;
366
                }
367
            },
368
            HSLtoRGB: function(h, s, l, a) {
369
                if (s < 0) {
370
                    s = 0;
371
                }
372
                var q;
373
                if (l <= 0.5) {
374
                    q = l * (1 + s);
375
                } else {
376
                    q = l + s - (l * s);
377
                }
378
379
                var p = 2 * l - q;
380
381
                var tr = h + (1 / 3);
382
                var tg = h;
383
                var tb = h - (1 / 3);
384
385
                var r = Math.round(this.HueToRGB(p, q, tr) * 255);
386
                var g = Math.round(this.HueToRGB(p, q, tg) * 255);
387
                var b = Math.round(this.HueToRGB(p, q, tb) * 255);
388
                return [r, g, b, this._sanitizeNumber(a)];
389
            },
390
            toString: function(format) {
391
                format = format || 'rgba';
392
                switch (format) {
393
                    case 'rgb':
394
                        {
395
                            var rgb = this.toRGB();
396
                            if (this.rgbaIsTransparent(rgb)) {
397
                                return 'transparent';
398
                            }
399
                            return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
400
                        }
401
                        break;
0 ignored issues
show
Unused Code introduced by
This break statement is unnecessary and may be removed.
Loading history...
402
                    case 'rgba':
403
                        {
404
                            var rgb = this.toRGB();
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable rgb already seems to be declared on line 395. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
405
                            return 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
406
                        }
407
                        break;
0 ignored issues
show
Unused Code introduced by
This break statement is unnecessary and may be removed.
Loading history...
408
                    case 'hsl':
409
                        {
410
                            var hsl = this.toHSL();
411
                            return 'hsl(' + Math.round(hsl.h * 360) + ',' + Math.round(hsl.s * 100) + '%,' + Math.round(hsl.l * 100) + '%)';
412
                        }
413
                        break;
0 ignored issues
show
Unused Code introduced by
This break statement is unnecessary and may be removed.
Loading history...
414
                    case 'hsla':
415
                        {
416
                            var hsl = this.toHSL();
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable hsl already seems to be declared on line 410. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
417
                            return 'hsla(' + Math.round(hsl.h * 360) + ',' + Math.round(hsl.s * 100) + '%,' + Math.round(hsl.l * 100) + '%,' + hsl.a + ')';
418
                        }
419
                        break;
0 ignored issues
show
Unused Code introduced by
This break statement is unnecessary and may be removed.
Loading history...
420
                    case 'hex':
421
                        {
422
                            return this.toHex();
423
                        }
424
                        break;
0 ignored issues
show
Unused Code introduced by
This break statement is unnecessary and may be removed.
Loading history...
425
                    case 'alias':
426
                        return this.toAlias() || this.toHex();
427
                    default:
428
                        {
429
                            return false;
430
                        }
431
                        break;
0 ignored issues
show
Unused Code introduced by
This break statement is unnecessary and may be removed.
Loading history...
432
                }
433
            },
434
            // a set of RE's that can match strings and generate color tuples.
435
            // from John Resig color plugin
436
            // https://github.com/jquery/jquery-color/
437
            stringParsers: [{
438
                re: /rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*?\)/,
439
                format: 'rgb',
440
                parse: function(execResult) {
441
                    return [
442
                        execResult[1],
443
                        execResult[2],
444
                        execResult[3],
445
                        1
446
                    ];
447
                }
448
            }, {
449
                re: /rgb\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*?\)/,
450
                format: 'rgb',
451
                parse: function(execResult) {
452
                    return [
453
                        2.55 * execResult[1],
454
                        2.55 * execResult[2],
455
                        2.55 * execResult[3],
456
                        1
457
                    ];
458
                }
459
            }, {
460
                re: /rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
461
                format: 'rgba',
462
                parse: function(execResult) {
463
                    return [
464
                        execResult[1],
465
                        execResult[2],
466
                        execResult[3],
467
                        execResult[4]
468
                    ];
469
                }
470
            }, {
471
                re: /rgba\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
472
                format: 'rgba',
473
                parse: function(execResult) {
474
                    return [
475
                        2.55 * execResult[1],
476
                        2.55 * execResult[2],
477
                        2.55 * execResult[3],
478
                        execResult[4]
479
                    ];
480
                }
481
            }, {
482
                re: /hsl\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*?\)/,
483
                format: 'hsl',
484
                parse: function(execResult) {
485
                    return [
486
                        execResult[1] / 360,
487
                        execResult[2] / 100,
488
                        execResult[3] / 100,
489
                        execResult[4]
490
                    ];
491
                }
492
            }, {
493
                re: /hsla\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
494
                format: 'hsla',
495
                parse: function(execResult) {
496
                    return [
497
                        execResult[1] / 360,
498
                        execResult[2] / 100,
499
                        execResult[3] / 100,
500
                        execResult[4]
501
                    ];
502
                }
503
            }, {
504
                re: /#?([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,
505
                format: 'hex',
506
                parse: function(execResult) {
507
                    return [
508
                        parseInt(execResult[1], 16),
509
                        parseInt(execResult[2], 16),
510
                        parseInt(execResult[3], 16),
511
                        1
512
                    ];
513
                }
514
            }, {
515
                re: /#?([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/,
516
                format: 'hex',
517
                parse: function(execResult) {
518
                    return [
519
                        parseInt(execResult[1] + execResult[1], 16),
520
                        parseInt(execResult[2] + execResult[2], 16),
521
                        parseInt(execResult[3] + execResult[3], 16),
522
                        1
523
                    ];
524
                }
525
            }, {
526
                //predefined color name
527
                re: /^([a-z]{3,})$/,
528
                format: 'alias',
529
                parse: function(execResult) {
530
                    var hexval = this.colorNameToHex(execResult[0]) || '#000000';
531
                    var match = this.stringParsers[6].re.exec(hexval),
532
                        values = match && this.stringParsers[6].parse.apply(this, [match]);
533
                    return values;
534
                }
535
            }],
536
            colorNameToHex: function(name) {
537
                if (typeof this.colors[name.toLowerCase()] !== 'undefined') {
538
                    return this.colors[name.toLowerCase()];
539
                }
540
                return false;
541
            }
542
        };
543
544
545
        var defaults = {
546
            horizontal: false, // horizontal mode layout ?
547
            inline: false, //forces to show the colorpicker as an inline element
548
            color: false, //forces a color
549
            format: false, //forces a format
550
            input: 'input', // children input selector
551
            container: false, // container selector
552
            component: '.add-on, .input-group-addon', // children component selector
553
            sliders: {
554
                saturation: {
555
                    maxLeft: 100,
556
                    maxTop: 100,
557
                    callLeft: 'setSaturation',
558
                    callTop: 'setBrightness'
559
                },
560
                hue: {
561
                    maxLeft: 0,
562
                    maxTop: 100,
563
                    callLeft: false,
564
                    callTop: 'setHue'
565
                },
566
                alpha: {
567
                    maxLeft: 0,
568
                    maxTop: 100,
569
                    callLeft: false,
570
                    callTop: 'setAlpha'
571
                }
572
            },
573
            slidersHorz: {
574
                saturation: {
575
                    maxLeft: 100,
576
                    maxTop: 100,
577
                    callLeft: 'setSaturation',
578
                    callTop: 'setBrightness'
579
                },
580
                hue: {
581
                    maxLeft: 100,
582
                    maxTop: 0,
583
                    callLeft: 'setHue',
584
                    callTop: false
585
                },
586
                alpha: {
587
                    maxLeft: 100,
588
                    maxTop: 0,
589
                    callLeft: 'setAlpha',
590
                    callTop: false
591
                }
592
            },
593
            template: '<div class="colorpicker dropdown-menu">' +
594
                '<div class="colorpicker-saturation"><i><b></b></i></div>' +
595
                '<div class="colorpicker-hue"><i></i></div>' +
596
                '<div class="colorpicker-alpha"><i></i></div>' +
597
                '<div class="colorpicker-color"><div /></div>' +
598
                '</div>'
599
        };
600
601
        var Colorpicker = function(element, options) {
602
            this.element = $(element).addClass('colorpicker-element');
603
            this.options = $.extend({}, defaults, this.element.data(), options);
604
            this.component = this.options.component;
605
            this.component = (this.component !== false) ? this.element.find(this.component) : false;
606
            if (this.component && (this.component.length === 0)) {
607
                this.component = false;
608
            }
609
            this.container = (this.options.container === true) ? this.element : this.options.container;
610
            this.container = (this.container !== false) ? $(this.container) : false;
611
612
            // Is the element an input? Should we search inside for any input?
613
            this.input = this.element.is('input') ? this.element : (this.options.input ?
614
                this.element.find(this.options.input) : false);
615
            if (this.input && (this.input.length === 0)) {
616
                this.input = false;
617
            }
618
            // Set HSB color
619
            this.color = new Color(this.options.color !== false ? this.options.color : this.getValue());
620
            this.format = this.options.format !== false ? this.options.format : this.color.origFormat;
621
622
            // Setup picker
623
            this.picker = $(this.options.template);
624
            if (this.options.inline) {
625
                this.picker.addClass('colorpicker-inline colorpicker-visible');
626
            } else {
627
                this.picker.addClass('colorpicker-hidden');
628
            }
629
            if (this.options.horizontal) {
630
                this.picker.addClass('colorpicker-horizontal');
631
            }
632
            if (this.format === 'rgba' || this.format === 'hsla') {
633
                this.picker.addClass('colorpicker-with-alpha');
634
            }
635
            if (this.options.align === 'right') {
636
                this.picker.addClass('colorpicker-right');
637
            }
638
            this.picker.on('mousedown.colorpicker touchstart.colorpicker', $.proxy(this.mousedown, this));
639
            this.picker.appendTo(this.container ? this.container : $('body'));
640
641
            // Bind events
642
            if (this.input !== false) {
643
                this.input.on({
644
                    'keyup.colorpicker': $.proxy(this.keyup, this)
645
                });
646
                if (this.component === false) {
647
                    this.element.on({
648
                        'focus.colorpicker': $.proxy(this.show, this)
649
                    });
650
                }
651
                if (this.options.inline === false) {
652
                    this.element.on({
653
                        'focusout.colorpicker': $.proxy(this.hide, this)
654
                    });
655
                }
656
            }
657
658
            if (this.component !== false) {
659
                this.component.on({
660
                    'click.colorpicker': $.proxy(this.show, this)
661
                });
662
            }
663
664
            if ((this.input === false) && (this.component === false)) {
665
                this.element.on({
666
                    'click.colorpicker': $.proxy(this.show, this)
667
                });
668
            }
669
670
            // for HTML5 input[type='color']
671
            if ((this.input !== false) && (this.component !== false) && (this.input.attr('type') === 'color')) {
672
673
                this.input.on({
674
                    'click.colorpicker': $.proxy(this.show, this),
675
                    'focus.colorpicker': $.proxy(this.show, this)
676
                });
677
            }
678
            this.update();
679
680
            $($.proxy(function() {
681
                this.element.trigger('create');
682
            }, this));
683
        };
684
685
        Colorpicker.Color = Color;
686
687
        Colorpicker.prototype = {
688
            constructor: Colorpicker,
689
            destroy: function() {
690
                this.picker.remove();
691
                this.element.removeData('colorpicker').off('.colorpicker');
692
                if (this.input !== false) {
693
                    this.input.off('.colorpicker');
694
                }
695
                if (this.component !== false) {
696
                    this.component.off('.colorpicker');
697
                }
698
                this.element.removeClass('colorpicker-element');
699
                this.element.trigger({
700
                    type: 'destroy'
701
                });
702
            },
703
            reposition: function() {
704
                if (this.options.inline !== false || this.options.container) {
705
                    return false;
706
                }
707
                var type = this.container && this.container[0] !== document.body ? 'position' : 'offset';
708
                var element = this.component || this.element;
709
                var offset = element[type]();
710
                if (this.options.align === 'right') {
711
                    offset.left -= this.picker.outerWidth() - element.outerWidth()
712
                }
713
                this.picker.css({
714
                    top: offset.top + element.outerHeight(),
715
                    left: offset.left
716
                });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
717
            },
718
            show: function(e) {
719
                if (this.isDisabled()) {
720
                    return false;
721
                }
722
                this.picker.addClass('colorpicker-visible').removeClass('colorpicker-hidden');
723
                this.reposition();
724
                $(window).on('resize.colorpicker', $.proxy(this.reposition, this));
725
                if (e && (!this.hasInput() || this.input.attr('type') === 'color')) {
726
                    if (e.stopPropagation && e.preventDefault) {
727
                        e.stopPropagation();
728
                        e.preventDefault();
729
                    }
730
                }
731
                if (this.options.inline === false) {
732
                    $(window.document).on({
733
                        'mousedown.colorpicker': $.proxy(this.hide, this)
734
                    });
735
                }
736
                this.element.trigger({
737
                    type: 'showPicker',
738
                    color: this.color
739
                });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
740
            },
741
            hide: function() {
742
                this.picker.addClass('colorpicker-hidden').removeClass('colorpicker-visible');
743
                $(window).off('resize.colorpicker', this.reposition);
744
                $(document).off({
745
                    'mousedown.colorpicker': this.hide
746
                });
747
                this.update();
748
                this.element.trigger({
749
                    type: 'hidePicker',
750
                    color: this.color
751
                });
752
            },
753
            updateData: function(val) {
754
                val = val || this.color.toString(this.format);
755
                this.element.data('color', val);
756
                return val;
757
            },
758
            updateInput: function(val) {
759
                val = val || this.color.toString(this.format);
760
                if (this.input !== false) {
761
                    this.input.prop('value', val);
762
                }
763
                return val;
764
            },
765
            updatePicker: function(val) {
766
                if (val !== undefined) {
767
                    this.color = new Color(val);
768
                }
769
                var sl = (this.options.horizontal === false) ? this.options.sliders : this.options.slidersHorz;
0 ignored issues
show
Unused Code introduced by
The assignment to variable sl seems to be never used. Consider removing it.
Loading history...
770
                var icns = this.picker.find('i');
771
                if (icns.length === 0) {
772
                    return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
773
                }
774
                if (this.options.horizontal === false) {
775
                    sl = this.options.sliders;
776
                    icns.eq(1).css('top', sl.hue.maxTop * (1 - this.color.value.h)).end()
777
                        .eq(2).css('top', sl.alpha.maxTop * (1 - this.color.value.a));
778
                } else {
779
                    sl = this.options.slidersHorz;
780
                    icns.eq(1).css('left', sl.hue.maxLeft * (1 - this.color.value.h)).end()
781
                        .eq(2).css('left', sl.alpha.maxLeft * (1 - this.color.value.a));
782
                }
783
                icns.eq(0).css({
784
                    'top': sl.saturation.maxTop - this.color.value.b * sl.saturation.maxTop,
785
                    'left': this.color.value.s * sl.saturation.maxLeft
786
                });
787
                this.picker.find('.colorpicker-saturation').css('backgroundColor', this.color.toHex(this.color.value.h, 1, 1, 1));
788
                this.picker.find('.colorpicker-alpha').css('backgroundColor', this.color.toHex());
789
                this.picker.find('.colorpicker-color, .colorpicker-color div').css('backgroundColor', this.color.toString(this.format));
790
                return val;
791
            },
792
            updateComponent: function(val) {
793
                val = val || this.color.toString(this.format);
794
                if (this.component !== false) {
795
                    var icn = this.component.find('i').eq(0);
796
                    if (icn.length > 0) {
797
                        icn.css({
798
                            'backgroundColor': val
799
                        });
800
                    } else {
801
                        this.component.css({
802
                            'backgroundColor': val
803
                        });
804
                    }
805
                }
806
                return val;
807
            },
808
            update: function(force) {
809
                var val;
810
                if ((this.getValue(false) !== false) || (force === true)) {
811
                    // Update input/data only if the current value is not empty
812
                    val = this.updateComponent();
813
                    this.updateInput(val);
814
                    this.updateData(val);
815
                    this.updatePicker(); // only update picker if value is not empty
816
                }
817
                return val;
0 ignored issues
show
Bug introduced by
The variable val does not seem to be initialized in case this.getValue(false) !== false || force === true on line 810 is false. Are you sure this can never be the case?
Loading history...
818
819
            },
820
            setValue: function(val) { // set color manually
821
                this.color = new Color(val);
822
                this.update();
823
                this.element.trigger({
824
                    type: 'changeColor',
825
                    color: this.color,
826
                    value: val
827
                });
828
            },
829
            getValue: function(defaultValue) {
830
                defaultValue = (defaultValue === undefined) ? '#000000' : defaultValue;
831
                var val;
832
                if (this.hasInput()) {
833
                    val = this.input.val();
834
                } else {
835
                    val = this.element.data('color');
836
                }
837
                if ((val === undefined) || (val === '') || (val === null)) {
838
                    // if not defined or empty, return default
839
                    val = defaultValue;
840
                }
841
                return val;
842
            },
843
            hasInput: function() {
844
                return (this.input !== false);
845
            },
846
            isDisabled: function() {
847
                if (this.hasInput()) {
848
                    return (this.input.prop('disabled') === true);
849
                }
850
                return false;
851
            },
852
            disable: function() {
853
                if (this.hasInput()) {
854
                    this.input.prop('disabled', true);
855
                    this.element.trigger({
856
                        type: 'disable',
857
                        color: this.color,
858
                        value: this.getValue()
859
                    });
860
                    return true;
861
                }
862
                return false;
863
            },
864
            enable: function() {
865
                if (this.hasInput()) {
866
                    this.input.prop('disabled', false);
867
                    this.element.trigger({
868
                        type: 'enable',
869
                        color: this.color,
870
                        value: this.getValue()
871
                    });
872
                    return true;
873
                }
874
                return false;
875
            },
876
            currentSlider: null,
877
            mousePointer: {
878
                left: 0,
879
                top: 0
880
            },
881
            mousedown: function(e) {
882
                if (!e.pageX && !e.pageY && e.originalEvent) {
883
                    e.pageX = e.originalEvent.touches[0].pageX;
884
                    e.pageY = e.originalEvent.touches[0].pageY;
885
                }
886
                e.stopPropagation();
887
                e.preventDefault();
888
889
                var target = $(e.target);
890
891
                //detect the slider and set the limits and callbacks
892
                var zone = target.closest('div');
893
                var sl = this.options.horizontal ? this.options.slidersHorz : this.options.sliders;
894
                if (!zone.is('.colorpicker')) {
895
                    if (zone.is('.colorpicker-saturation')) {
896
                        this.currentSlider = $.extend({}, sl.saturation);
897
                    } else if (zone.is('.colorpicker-hue')) {
898
                        this.currentSlider = $.extend({}, sl.hue);
899
                    } else if (zone.is('.colorpicker-alpha')) {
900
                        this.currentSlider = $.extend({}, sl.alpha);
901
                    } else {
902
                        return false;
903
                    }
904
                    var offset = zone.offset();
905
                    //reference to guide's style
906
                    this.currentSlider.guide = zone.find('i')[0].style;
907
                    this.currentSlider.left = e.pageX - offset.left;
908
                    this.currentSlider.top = e.pageY - offset.top;
909
                    this.mousePointer = {
910
                        left: e.pageX,
911
                        top: e.pageY
912
                    };
913
                    //trigger mousemove to move the guide to the current position
914
                    $(document).on({
915
                        'mousemove.colorpicker': $.proxy(this.mousemove, this),
916
                        'touchmove.colorpicker': $.proxy(this.mousemove, this),
917
                        'mouseup.colorpicker': $.proxy(this.mouseup, this),
918
                        'touchend.colorpicker': $.proxy(this.mouseup, this)
919
                    }).trigger('mousemove');
920
                }
921
                return false;
922
            },
923
            mousemove: function(e) {
924
                if (!e.pageX && !e.pageY && e.originalEvent) {
925
                    e.pageX = e.originalEvent.touches[0].pageX;
926
                    e.pageY = e.originalEvent.touches[0].pageY;
927
                }
928
                e.stopPropagation();
929
                e.preventDefault();
930
                var left = Math.max(
931
                    0,
932
                    Math.min(
933
                        this.currentSlider.maxLeft,
934
                        this.currentSlider.left + ((e.pageX || this.mousePointer.left) - this.mousePointer.left)
935
                    )
936
                );
937
                var top = Math.max(
938
                    0,
939
                    Math.min(
940
                        this.currentSlider.maxTop,
941
                        this.currentSlider.top + ((e.pageY || this.mousePointer.top) - this.mousePointer.top)
942
                    )
943
                );
944
                this.currentSlider.guide.left = left + 'px';
945
                this.currentSlider.guide.top = top + 'px';
946
                if (this.currentSlider.callLeft) {
947
                    this.color[this.currentSlider.callLeft].call(this.color, left / this.currentSlider.maxLeft);
948
                }
949
                if (this.currentSlider.callTop) {
950
                    this.color[this.currentSlider.callTop].call(this.color, top / this.currentSlider.maxTop);
951
                }
952
                this.update(true);
953
954
                this.element.trigger({
955
                    type: 'changeColor',
956
                    color: this.color
957
                });
958
                return false;
959
            },
960
            mouseup: function(e) {
961
                e.stopPropagation();
962
                e.preventDefault();
963
                $(document).off({
964
                    'mousemove.colorpicker': this.mousemove,
965
                    'touchmove.colorpicker': this.mousemove,
966
                    'mouseup.colorpicker': this.mouseup,
967
                    'touchend.colorpicker': this.mouseup
968
                });
969
                return false;
970
            },
971
            keyup: function(e) {
972
                if ((e.keyCode === 38)) {
973
                    if (this.color.value.a < 1) {
974
                        this.color.value.a = Math.round((this.color.value.a + 0.01) * 100) / 100;
975
                    }
976
                    this.update(true);
977
                } else if ((e.keyCode === 40)) {
978
                    if (this.color.value.a > 0) {
979
                        this.color.value.a = Math.round((this.color.value.a - 0.01) * 100) / 100;
980
                    }
981
                    this.update(true);
982
                } else {
983
                    var val = this.input.val();
984
                    this.color = new Color(val);
985
                    if (this.getValue(false) !== false) {
986
                        this.updateData();
987
                        this.updateComponent();
988
                        this.updatePicker();
989
                    }
990
                }
991
                this.element.trigger({
992
                    type: 'changeColor',
993
                    color: this.color,
994
                    value: val
0 ignored issues
show
Bug introduced by
The variable val seems to not be initialized for all possible execution paths.
Loading history...
995
                });
996
            }
997
        };
998
999
        $.colorpicker = Colorpicker;
1000
1001
        $.fn.colorpicker = function(option) {
1002
            var pickerArgs = arguments,
1003
                rv;
1004
1005
            var $returnValue = this.each(function() {
1006
                var $this = $(this),
1007
                    inst = $this.data('colorpicker'),
1008
                    options = ((typeof option === 'object') ? option : {});
1009
                if ((!inst) && (typeof option !== 'string')) {
1010
                    $this.data('colorpicker', new Colorpicker(this, options));
1011
                } else {
1012
                    if (typeof option === 'string') {
1013
                        rv = inst[option].apply(inst, Array.prototype.slice.call(pickerArgs, 1));
1014
                    }
1015
                }
1016
            });
1017
            if (option === 'getValue') {
1018
                return rv;
1019
            }
1020
            return $returnValue;
1021
        };
1022
1023
        $.fn.colorpicker.constructor = Colorpicker;
1024
1025
    }));
1026