spectrum-rails 1.0.9

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.
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Kevin Trotter
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Spectrum::Rails
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'spectrum-rails'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install spectrum-rails
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1,8 @@
1
+ require "spectrum-rails/version"
2
+
3
+ module Spectrum
4
+ module Rails
5
+ class Engine < ::Rails::Engine
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ module Spectrum
2
+ module Rails
3
+ VERSION = "1.0.9"
4
+ end
5
+ end
@@ -0,0 +1,1748 @@
1
+ // Spectrum Colorpicker v1.0.9
2
+ // https://github.com/bgrins/spectrum
3
+ // Author: Brian Grinstead
4
+ // License: MIT
5
+
6
+ (function (window, $, undefined) {
7
+ var defaultOpts = {
8
+
9
+ // Events
10
+ beforeShow: noop,
11
+ move: noop,
12
+ change: noop,
13
+ show: noop,
14
+ hide: noop,
15
+
16
+ // Options
17
+ color: false,
18
+ flat: false,
19
+ showInput: false,
20
+ showButtons: true,
21
+ clickoutFiresChange: false,
22
+ showInitial: false,
23
+ showPalette: false,
24
+ showPaletteOnly: false,
25
+ showSelectionPalette: true,
26
+ localStorageKey: false,
27
+ maxSelectionSize: 7,
28
+ cancelText: "cancel",
29
+ chooseText: "choose",
30
+ preferredFormat: false,
31
+ className: "",
32
+ showAlpha: false,
33
+ theme: "sp-light",
34
+ palette: ['fff', '000'],
35
+ selectionPalette: [],
36
+ disabled: false
37
+ },
38
+ spectrums = [],
39
+ IE = !!/msie/i.exec( window.navigator.userAgent ),
40
+ rgbaSupport = (function() {
41
+ function contains( str, substr ) {
42
+ return !!~('' + str).indexOf(substr);
43
+ }
44
+
45
+ var elem = document.createElement('div');
46
+ var style = elem.style;
47
+ style.cssText = 'background-color:rgba(0,0,0,.5)';
48
+ return contains(style.backgroundColor, 'rgba') || contains(style.backgroundColor, 'hsla');
49
+ })(),
50
+ replaceInput = [
51
+ "<div class='sp-replacer'>",
52
+ "<div class='sp-preview'><div class='sp-preview-inner'></div></div>",
53
+ "<div class='sp-dd'>&#9660;</div>",
54
+ "</div>"
55
+ ].join(''),
56
+ markup = (function () {
57
+
58
+ // IE does not support gradients with multiple stops, so we need to simulate
59
+ // that for the rainbow slider with 8 divs that each have a single gradient
60
+ var gradientFix = "";
61
+ if (IE) {
62
+ for (var i = 1; i <= 6; i++) {
63
+ gradientFix += "<div class='sp-" + i + "'></div>";
64
+ }
65
+ }
66
+
67
+ return [
68
+ "<div class='sp-container'>",
69
+ "<div class='sp-palette-container'>",
70
+ "<div class='sp-palette sp-thumb sp-cf'></div>",
71
+ "</div>",
72
+ "<div class='sp-picker-container'>",
73
+ "<div class='sp-top sp-cf'>",
74
+ "<div class='sp-fill'></div>",
75
+ "<div class='sp-top-inner'>",
76
+ "<div class='sp-color'>",
77
+ "<div class='sp-sat'>",
78
+ "<div class='sp-val'>",
79
+ "<div class='sp-dragger'></div>",
80
+ "</div>",
81
+ "</div>",
82
+ "</div>",
83
+ "<div class='sp-hue'>",
84
+ "<div class='sp-slider'></div>",
85
+ gradientFix,
86
+ "</div>",
87
+ "</div>",
88
+ "<div class='sp-alpha'><div class='sp-alpha-inner'><div class='sp-alpha-handle'></div></div></div>",
89
+ "</div>",
90
+ "<div class='sp-input-container sp-cf'>",
91
+ "<input class='sp-input' type='text' spellcheck='false' />",
92
+ "</div>",
93
+ "<div class='sp-initial sp-thumb sp-cf'></div>",
94
+ "<div class='sp-button-container sp-cf'>",
95
+ "<a class='sp-cancel' href='#'></a>",
96
+ "<button class='sp-choose'></button>",
97
+ "</div>",
98
+ "</div>",
99
+ "</div>"
100
+ ].join("");
101
+ })();
102
+
103
+ function paletteTemplate (p, color, className) {
104
+ var html = [];
105
+ for (var i = 0; i < p.length; i++) {
106
+ var tiny = tinycolor(p[i]);
107
+ var c = tiny.toHsl().l < 0.5 ? "sp-thumb-el sp-thumb-dark" : "sp-thumb-el sp-thumb-light";
108
+ c += (tinycolor.equals(color, p[i])) ? " sp-thumb-active" : "";
109
+
110
+ var swatchStyle = rgbaSupport ? ("background-color:" + tiny.toRgbString()) : "filter:" + tiny.toFilter();
111
+ html.push('<span title="' + tiny.toRgbString() + '" data-color="' + tiny.toRgbString() + '" class="' + c + '"><span class="sp-thumb-inner" style="' + swatchStyle + ';" /></span>');
112
+ }
113
+ return "<div class='sp-cf " + className + "'>" + html.join('') + "</div>";
114
+ }
115
+
116
+ function hideAll() {
117
+ for (var i = 0; i < spectrums.length; i++) {
118
+ if (spectrums[i]) {
119
+ spectrums[i].hide();
120
+ }
121
+ }
122
+ }
123
+
124
+ function instanceOptions(o, callbackContext) {
125
+ var opts = $.extend({}, defaultOpts, o);
126
+ opts.callbacks = {
127
+ 'move': bind(opts.move, callbackContext),
128
+ 'change': bind(opts.change, callbackContext),
129
+ 'show': bind(opts.show, callbackContext),
130
+ 'hide': bind(opts.hide, callbackContext),
131
+ 'beforeShow': bind(opts.beforeShow, callbackContext)
132
+ };
133
+
134
+ return opts;
135
+ }
136
+
137
+ function spectrum(element, o) {
138
+
139
+ var opts = instanceOptions(o, element),
140
+ flat = opts.flat,
141
+ showSelectionPalette = opts.showSelectionPalette,
142
+ localStorageKey = opts.localStorageKey,
143
+ theme = opts.theme,
144
+ callbacks = opts.callbacks,
145
+ resize = throttle(reflow, 10),
146
+ visible = false,
147
+ dragWidth = 0,
148
+ dragHeight = 0,
149
+ dragHelperHeight = 0,
150
+ slideHeight = 0,
151
+ slideWidth = 0,
152
+ alphaWidth = 0,
153
+ alphaSlideHelperWidth = 0,
154
+ slideHelperHeight = 0,
155
+ currentHue = 0,
156
+ currentSaturation = 0,
157
+ currentValue = 0,
158
+ currentAlpha = 1,
159
+ palette = opts.palette.slice(0),
160
+ paletteArray = $.isArray(palette[0]) ? palette : [palette],
161
+ selectionPalette = opts.selectionPalette.slice(0),
162
+ draggingClass = "sp-dragging";
163
+
164
+ var doc = element.ownerDocument,
165
+ body = doc.body,
166
+ boundElement = $(element),
167
+ disabled = false,
168
+ container = $(markup, doc).addClass(theme),
169
+ dragger = container.find(".sp-color"),
170
+ dragHelper = container.find(".sp-dragger"),
171
+ slider = container.find(".sp-hue"),
172
+ slideHelper = container.find(".sp-slider"),
173
+ alphaSliderInner = container.find(".sp-alpha-inner"),
174
+ alphaSlider = container.find(".sp-alpha"),
175
+ alphaSlideHelper = container.find(".sp-alpha-handle"),
176
+ textInput = container.find(".sp-input"),
177
+ paletteContainer = container.find(".sp-palette"),
178
+ initialColorContainer = container.find(".sp-initial"),
179
+ cancelButton = container.find(".sp-cancel"),
180
+ chooseButton = container.find(".sp-choose"),
181
+ isInput = boundElement.is("input"),
182
+ shouldReplace = isInput && !flat,
183
+ replacer = (shouldReplace) ? $(replaceInput).addClass(theme) : $([]),
184
+ offsetElement = (shouldReplace) ? replacer : boundElement,
185
+ previewElement = replacer.find(".sp-preview-inner"),
186
+ initialColor = opts.color || (isInput && boundElement.val()),
187
+ colorOnShow = false,
188
+ preferredFormat = opts.preferredFormat,
189
+ currentPreferredFormat = preferredFormat,
190
+ clickoutFiresChange = !opts.showButtons || opts.clickoutFiresChange;
191
+
192
+
193
+ function applyOptions() {
194
+
195
+ container.toggleClass("sp-flat", flat);
196
+ container.toggleClass("sp-input-disabled", !opts.showInput);
197
+ container.toggleClass("sp-alpha-enabled", opts.showAlpha);
198
+ container.toggleClass("sp-buttons-disabled", !opts.showButtons || flat);
199
+ container.toggleClass("sp-palette-disabled", !opts.showPalette);
200
+ container.toggleClass("sp-palette-only", opts.showPaletteOnly);
201
+ container.toggleClass("sp-initial-disabled", !opts.showInitial);
202
+ container.addClass(opts.className);
203
+
204
+ reflow();
205
+ }
206
+
207
+ function initialize() {
208
+
209
+ if (IE) {
210
+ container.find("*:not(input)").attr("unselectable", "on");
211
+ }
212
+
213
+ applyOptions();
214
+
215
+ if (shouldReplace) {
216
+ boundElement.hide().after(replacer);
217
+ }
218
+
219
+ if (flat) {
220
+ boundElement.after(container).hide();
221
+ }
222
+ else {
223
+ $(body).append(container.hide());
224
+ }
225
+
226
+ if (localStorageKey && window.localStorage) {
227
+
228
+ // Migrate old palettes over to new format. May want to remove this eventually.
229
+ try {
230
+ var oldPalette = window.localStorage[localStorageKey].split(",#");
231
+ if (oldPalette.length > 1) {
232
+ delete window.localStorage[localStorageKey];
233
+ $.each(oldPalette, function(i, c) {
234
+ addColorToSelectionPalette(c);
235
+ });
236
+ }
237
+ }
238
+ catch(e) { }
239
+
240
+ try {
241
+ selectionPalette = window.localStorage[localStorageKey].split(";");
242
+ }
243
+ catch (e) { }
244
+ }
245
+
246
+ offsetElement.bind("click.spectrum touchstart.spectrum", function (e) {
247
+ if (!disabled) {
248
+ toggle();
249
+ }
250
+
251
+ e.stopPropagation();
252
+
253
+ if (!$(e.target).is("input")) {
254
+ e.preventDefault();
255
+ }
256
+ });
257
+
258
+ if(boundElement.is(":disabled") || (opts.disabled === true)) {
259
+ disable();
260
+ }
261
+
262
+ // Prevent clicks from bubbling up to document. This would cause it to be hidden.
263
+ container.click(stopPropagation);
264
+
265
+ // Handle user typed input
266
+ textInput.change(setFromTextInput);
267
+ textInput.bind("paste", function () {
268
+ setTimeout(setFromTextInput, 1);
269
+ });
270
+ textInput.keydown(function (e) { if (e.keyCode == 13) { setFromTextInput(); } });
271
+
272
+ cancelButton.text(opts.cancelText);
273
+ cancelButton.bind("click.spectrum", function (e) {
274
+ e.stopPropagation();
275
+ e.preventDefault();
276
+ hide("cancel");
277
+ });
278
+
279
+ chooseButton.text(opts.chooseText);
280
+ chooseButton.bind("click.spectrum", function (e) {
281
+ e.stopPropagation();
282
+ e.preventDefault();
283
+
284
+ if (isValid()) {
285
+ updateOriginalInput(true);
286
+ hide();
287
+ }
288
+ });
289
+
290
+ draggable(alphaSlider, function (dragX, dragY, e) {
291
+ currentAlpha = (dragX / alphaWidth);
292
+ if (e.shiftKey) {
293
+ currentAlpha = Math.round(currentAlpha * 10) / 10;
294
+ }
295
+
296
+ move();
297
+ });
298
+
299
+ draggable(slider, function (dragX, dragY) {
300
+ currentHue = parseFloat(dragY / slideHeight);
301
+ move();
302
+ }, dragStart, dragStop);
303
+
304
+ draggable(dragger, function (dragX, dragY) {
305
+ currentSaturation = parseFloat(dragX / dragWidth);
306
+ currentValue = parseFloat((dragHeight - dragY) / dragHeight);
307
+ move();
308
+ }, dragStart, dragStop);
309
+
310
+ if (!!initialColor) {
311
+ set(initialColor);
312
+
313
+ // In case color was black - update the preview UI and set the format
314
+ // since the set function will not run (default color is black).
315
+ updateUI();
316
+ currentPreferredFormat = preferredFormat || tinycolor(initialColor).format;
317
+
318
+ addColorToSelectionPalette(initialColor);
319
+ }
320
+ else {
321
+ updateUI();
322
+ }
323
+
324
+ if (flat) {
325
+ show();
326
+ }
327
+
328
+ function palletElementClick(e) {
329
+ if (e.data && e.data.ignore) {
330
+ set($(this).data("color"));
331
+ move();
332
+ }
333
+ else {
334
+ set($(this).data("color"));
335
+ updateOriginalInput(true);
336
+ move();
337
+ hide();
338
+ }
339
+
340
+ return false;
341
+ }
342
+
343
+ var paletteEvent = IE ? "mousedown.spectrum" : "click.spectrum touchstart.spectrum";
344
+ paletteContainer.delegate(".sp-thumb-el", paletteEvent, palletElementClick);
345
+ initialColorContainer.delegate(".sp-thumb-el:nth-child(1)", paletteEvent, { ignore: true }, palletElementClick);
346
+ }
347
+
348
+ function addColorToSelectionPalette(color) {
349
+ if (showSelectionPalette) {
350
+ var colorRgb = tinycolor(color).toRgbString();
351
+ if ($.inArray(colorRgb, selectionPalette) === -1) {
352
+ selectionPalette.push(colorRgb);
353
+ }
354
+
355
+ if (localStorageKey && window.localStorage) {
356
+ try {
357
+ window.localStorage[localStorageKey] = selectionPalette.join(";");
358
+ }
359
+ catch(e) { }
360
+ }
361
+ }
362
+ }
363
+
364
+ function getUniqueSelectionPalette() {
365
+ var unique = [];
366
+ var p = selectionPalette;
367
+ var paletteLookup = {};
368
+ var rgb;
369
+
370
+ if (opts.showPalette) {
371
+
372
+ for (var i = 0; i < paletteArray.length; i++) {
373
+ for (var j = 0; j < paletteArray[i].length; j++) {
374
+ rgb = tinycolor(paletteArray[i][j]).toRgbString();
375
+ paletteLookup[rgb] = true;
376
+ }
377
+ }
378
+
379
+ for (i = 0; i < p.length; i++) {
380
+ rgb = tinycolor(p[i]).toRgbString();
381
+
382
+ if (!paletteLookup.hasOwnProperty(rgb)) {
383
+ unique.push(p[i]);
384
+ paletteLookup[rgb] = true;
385
+ }
386
+ }
387
+ }
388
+
389
+ return unique.reverse().slice(0, opts.maxSelectionSize);
390
+ }
391
+
392
+ function drawPalette() {
393
+
394
+ var currentColor = get();
395
+
396
+ var html = $.map(paletteArray, function (palette, i) {
397
+ return paletteTemplate(palette, currentColor, "sp-palette-row sp-palette-row-" + i);
398
+ });
399
+
400
+ if (selectionPalette) {
401
+ html.push(paletteTemplate(getUniqueSelectionPalette(), currentColor, "sp-palette-row sp-palette-row-selection"));
402
+ }
403
+
404
+ paletteContainer.html(html.join(""));
405
+ }
406
+
407
+ function drawInitial() {
408
+ if (opts.showInitial) {
409
+ var initial = colorOnShow;
410
+ var current = get();
411
+ initialColorContainer.html(paletteTemplate([initial, current], current, "sp-palette-row-initial"));
412
+ }
413
+ }
414
+
415
+ function dragStart() {
416
+ if (dragHeight === 0 || dragWidth === 0 || slideHeight === 0) {
417
+ reflow();
418
+ }
419
+ container.addClass(draggingClass);
420
+ }
421
+
422
+ function dragStop() {
423
+ container.removeClass(draggingClass);
424
+ }
425
+
426
+ function setFromTextInput() {
427
+ var tiny = tinycolor(textInput.val());
428
+ if (tiny.ok) {
429
+ set(tiny);
430
+ }
431
+ else {
432
+ textInput.addClass("sp-validation-error");
433
+ }
434
+ }
435
+
436
+ function toggle() {
437
+ if (visible) {
438
+ hide();
439
+ }
440
+ else {
441
+ show();
442
+ }
443
+ }
444
+
445
+ function show() {
446
+ if (visible) {
447
+ reflow();
448
+ return;
449
+ }
450
+ if (callbacks.beforeShow(get()) === false) return;
451
+
452
+ hideAll();
453
+ visible = true;
454
+
455
+ $(doc).bind("click.spectrum", hide);
456
+ $(window).bind("resize.spectrum", resize);
457
+ replacer.addClass("sp-active");
458
+ container.show();
459
+
460
+ if (opts.showPalette) {
461
+ drawPalette();
462
+ }
463
+ reflow();
464
+ updateUI();
465
+
466
+ colorOnShow = get();
467
+
468
+ drawInitial();
469
+ callbacks.show(colorOnShow);
470
+ }
471
+
472
+ function hide(e) {
473
+
474
+ // Return on right click
475
+ if (e && e.type == "click" && e.button == 2) { return; }
476
+
477
+ // Return if hiding is unnecessary
478
+ if (!visible || flat) { return; }
479
+ visible = false;
480
+
481
+ $(doc).unbind("click.spectrum", hide);
482
+ $(window).unbind("resize.spectrum", resize);
483
+
484
+ replacer.removeClass("sp-active");
485
+ container.hide();
486
+
487
+ var colorHasChanged = !tinycolor.equals(get(), colorOnShow);
488
+
489
+ if (colorHasChanged) {
490
+ if (clickoutFiresChange && e !== "cancel") {
491
+ updateOriginalInput(true);
492
+ }
493
+ else {
494
+ revert();
495
+ }
496
+ }
497
+
498
+ callbacks.hide(get());
499
+ }
500
+
501
+ function revert() {
502
+ set(colorOnShow, true);
503
+ }
504
+
505
+ function set(color, ignoreFormatChange) {
506
+ if (tinycolor.equals(color, get())) {
507
+ return;
508
+ }
509
+
510
+ var newColor = tinycolor(color);
511
+ var newHsv = newColor.toHsv();
512
+
513
+ currentHue = newHsv.h;
514
+ currentSaturation = newHsv.s;
515
+ currentValue = newHsv.v;
516
+ currentAlpha = newHsv.a;
517
+
518
+ updateUI();
519
+
520
+ if (!ignoreFormatChange) {
521
+ currentPreferredFormat = preferredFormat || newColor.format;
522
+ }
523
+ }
524
+
525
+ function get() {
526
+ return tinycolor.fromRatio({ h: currentHue, s: currentSaturation, v: currentValue, a: Math.round(currentAlpha * 100) / 100 });
527
+ }
528
+
529
+ function isValid() {
530
+ return !textInput.hasClass("sp-validation-error");
531
+ }
532
+
533
+ function move() {
534
+ updateUI();
535
+
536
+ callbacks.move(get());
537
+ }
538
+
539
+ function updateUI() {
540
+
541
+ textInput.removeClass("sp-validation-error");
542
+
543
+ updateHelperLocations();
544
+
545
+ // Update dragger background color (gradients take care of saturation and value).
546
+ var flatColor = tinycolor({ h: currentHue, s: "1.0", v: "1.0" });
547
+ dragger.css("background-color", flatColor.toHexString());
548
+
549
+ // Get a format that alpha will be included in (hex and names ignore alpha)
550
+ var format = currentPreferredFormat;
551
+ if (currentAlpha < 1) {
552
+ if (format === "hex" || format === "name") {
553
+ format = "rgb";
554
+ }
555
+ }
556
+
557
+ var realColor = get(),
558
+ realHex = realColor.toHexString(),
559
+ realRgb = realColor.toRgbString();
560
+
561
+
562
+ // Update the replaced elements background color (with actual selected color)
563
+ if (rgbaSupport || realColor.alpha === 1) {
564
+ previewElement.css("background-color", realRgb);
565
+ }
566
+ else {
567
+ previewElement.css("background-color", "transparent");
568
+ previewElement.css("filter", realColor.toFilter());
569
+ }
570
+
571
+ if (opts.showAlpha) {
572
+ var rgb = realColor.toRgb();
573
+ rgb.a = 0;
574
+ var realAlpha = tinycolor(rgb).toRgbString();
575
+ var gradient = "linear-gradient(left, " + realAlpha + ", " + realHex + ")";
576
+
577
+ if (IE) {
578
+ alphaSliderInner.css("filter", tinycolor(realAlpha).toFilter({ gradientType: 1 }, realHex));
579
+ }
580
+ else {
581
+ alphaSliderInner.css("background", "-webkit-" + gradient);
582
+ alphaSliderInner.css("background", "-moz-" + gradient);
583
+ alphaSliderInner.css("background", "-ms-" + gradient);
584
+ alphaSliderInner.css("background", gradient);
585
+ }
586
+ }
587
+
588
+
589
+ // Update the text entry input as it changes happen
590
+ if (opts.showInput) {
591
+ if (currentAlpha < 1) {
592
+ if (format === "hex" || format === "name") {
593
+ format = "rgb";
594
+ }
595
+ }
596
+ textInput.val(realColor.toString(format));
597
+ }
598
+
599
+ if (opts.showPalette) {
600
+ drawPalette();
601
+ }
602
+
603
+ drawInitial();
604
+ }
605
+
606
+ function updateHelperLocations() {
607
+ var s = currentSaturation;
608
+ var v = currentValue;
609
+
610
+ // Where to show the little circle in that displays your current selected color
611
+ var dragX = s * dragWidth;
612
+ var dragY = dragHeight - (v * dragHeight);
613
+ dragX = Math.max(
614
+ -dragHelperHeight,
615
+ Math.min(dragWidth - dragHelperHeight, dragX - dragHelperHeight)
616
+ );
617
+ dragY = Math.max(
618
+ -dragHelperHeight,
619
+ Math.min(dragHeight - dragHelperHeight, dragY - dragHelperHeight)
620
+ );
621
+ dragHelper.css({
622
+ "top": dragY,
623
+ "left": dragX
624
+ });
625
+
626
+ var alphaX = currentAlpha * alphaWidth;
627
+ alphaSlideHelper.css({
628
+ "left": alphaX - (alphaSlideHelperWidth / 2)
629
+ });
630
+
631
+ // Where to show the bar that displays your current selected hue
632
+ var slideY = (currentHue) * slideHeight;
633
+ slideHelper.css({
634
+ "top": slideY - slideHelperHeight
635
+ });
636
+ }
637
+
638
+ function updateOriginalInput(fireCallback) {
639
+ var color = get();
640
+
641
+ if (isInput) {
642
+ boundElement.val(color.toString(currentPreferredFormat)).change();
643
+ }
644
+
645
+ var hasChanged = !tinycolor.equals(color, colorOnShow);
646
+ colorOnShow = color;
647
+
648
+ // Update the selection palette with the current color
649
+ addColorToSelectionPalette(color);
650
+ if (fireCallback && hasChanged) {
651
+ callbacks.change(color);
652
+ }
653
+ }
654
+
655
+ function reflow() {
656
+ dragWidth = dragger.width();
657
+ dragHeight = dragger.height();
658
+ dragHelperHeight = dragHelper.height();
659
+ slideWidth = slider.width();
660
+ slideHeight = slider.height();
661
+ slideHelperHeight = slideHelper.height();
662
+ alphaWidth = alphaSlider.width();
663
+ alphaSlideHelperWidth = alphaSlideHelper.width();
664
+
665
+ if (!flat) {
666
+ container.offset(getOffset(container, offsetElement));
667
+ }
668
+
669
+ updateHelperLocations();
670
+ }
671
+
672
+ function destroy() {
673
+ boundElement.show();
674
+ offsetElement.unbind("click.spectrum touchstart.spectrum");
675
+ container.remove();
676
+ replacer.remove();
677
+ spectrums[spect.id] = null;
678
+ }
679
+
680
+ function option(optionName, optionValue) {
681
+ if (optionName === undefined) {
682
+ return $.extend({}, opts);
683
+ }
684
+ if (optionValue === undefined) {
685
+ return opts[optionName];
686
+ }
687
+
688
+ opts[optionName] = optionValue;
689
+ applyOptions();
690
+ }
691
+
692
+ function enable() {
693
+ disabled = false;
694
+ boundElement.attr("disabled", false);
695
+ offsetElement.removeClass("sp-disabled");
696
+ }
697
+
698
+ function disable() {
699
+ hide();
700
+ disabled = true;
701
+ boundElement.attr("disabled", true);
702
+ offsetElement.addClass("sp-disabled");
703
+ }
704
+
705
+ initialize();
706
+
707
+ var spect = {
708
+ show: show,
709
+ hide: hide,
710
+ toggle: toggle,
711
+ reflow: reflow,
712
+ option: option,
713
+ enable: enable,
714
+ disable: disable,
715
+ set: function (c) {
716
+ set(c);
717
+ updateOriginalInput();
718
+ },
719
+ get: get,
720
+ destroy: destroy,
721
+ container: container
722
+ };
723
+
724
+ spect.id = spectrums.push(spect) - 1;
725
+
726
+ return spect;
727
+ }
728
+
729
+ /**
730
+ * checkOffset - get the offset below/above and left/right element depending on screen position
731
+ * Thanks https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js
732
+ */
733
+ function getOffset(picker, input) {
734
+ var extraY = 0;
735
+ var dpWidth = picker.outerWidth();
736
+ var dpHeight = picker.outerHeight();
737
+ var inputHeight = input.outerHeight();
738
+ var doc = picker[0].ownerDocument;
739
+ var docElem = doc.documentElement;
740
+ var viewWidth = docElem.clientWidth + $(doc).scrollLeft();
741
+ var viewHeight = docElem.clientHeight + $(doc).scrollTop();
742
+ var offset = input.offset();
743
+ offset.top += inputHeight;
744
+
745
+ offset.left -=
746
+ Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
747
+ Math.abs(offset.left + dpWidth - viewWidth) : 0);
748
+
749
+ offset.top -=
750
+ Math.min(offset.top, ((offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
751
+ Math.abs(dpHeight + inputHeight - extraY) : extraY));
752
+
753
+ return offset;
754
+ }
755
+
756
+ /**
757
+ * noop - do nothing
758
+ */
759
+ function noop() {
760
+
761
+ }
762
+
763
+ /**
764
+ * stopPropagation - makes the code only doing this a little easier to read in line
765
+ */
766
+ function stopPropagation(e) {
767
+ e.stopPropagation();
768
+ }
769
+
770
+ /**
771
+ * Create a function bound to a given object
772
+ * Thanks to underscore.js
773
+ */
774
+ function bind(func, obj) {
775
+ var slice = Array.prototype.slice;
776
+ var args = slice.call(arguments, 2);
777
+ return function () {
778
+ return func.apply(obj, args.concat(slice.call(arguments)));
779
+ };
780
+ }
781
+
782
+ /**
783
+ * Lightweight drag helper. Handles containment within the element, so that
784
+ * when dragging, the x is within [0,element.width] and y is within [0,element.height]
785
+ */
786
+ function draggable(element, onmove, onstart, onstop) {
787
+ onmove = onmove || function () { };
788
+ onstart = onstart || function () { };
789
+ onstop = onstop || function () { };
790
+ var doc = element.ownerDocument || document;
791
+ var dragging = false;
792
+ var offset = {};
793
+ var maxHeight = 0;
794
+ var maxWidth = 0;
795
+ var hasTouch = ('ontouchstart' in window);
796
+
797
+ var duringDragEvents = {};
798
+ duringDragEvents["selectstart"] = prevent;
799
+ duringDragEvents["dragstart"] = prevent;
800
+ duringDragEvents[(hasTouch ? "touchmove" : "mousemove")] = move;
801
+ duringDragEvents[(hasTouch ? "touchend" : "mouseup")] = stop;
802
+
803
+ function prevent(e) {
804
+ if (e.stopPropagation) {
805
+ e.stopPropagation();
806
+ }
807
+ if (e.preventDefault) {
808
+ e.preventDefault();
809
+ }
810
+ e.returnValue = false;
811
+ }
812
+
813
+ function move(e) {
814
+ if (dragging) {
815
+ // Mouseup happened outside of window
816
+ if (IE && document.documentMode < 9 && !e.button) {
817
+ return stop();
818
+ }
819
+
820
+ var touches = e.originalEvent.touches;
821
+ var pageX = touches ? touches[0].pageX : e.pageX;
822
+ var pageY = touches ? touches[0].pageY : e.pageY;
823
+
824
+ var dragX = Math.max(0, Math.min(pageX - offset.left, maxWidth));
825
+ var dragY = Math.max(0, Math.min(pageY - offset.top, maxHeight));
826
+
827
+ if (hasTouch) {
828
+ // Stop scrolling in iOS
829
+ prevent(e);
830
+ }
831
+
832
+ onmove.apply(element, [dragX, dragY, e]);
833
+ }
834
+ }
835
+ function start(e) {
836
+ var rightclick = (e.which) ? (e.which == 3) : (e.button == 2);
837
+ var touches = e.originalEvent.touches;
838
+
839
+ if (!rightclick && !dragging) {
840
+ if (onstart.apply(element, arguments) !== false) {
841
+ dragging = true;
842
+ maxHeight = $(element).height();
843
+ maxWidth = $(element).width();
844
+ offset = $(element).offset();
845
+
846
+ $(doc).bind(duringDragEvents);
847
+ $(doc.body).addClass("sp-dragging");
848
+
849
+ if (!hasTouch) {
850
+ move(e);
851
+ }
852
+
853
+ prevent(e);
854
+ }
855
+ }
856
+ }
857
+ function stop() {
858
+ if (dragging) {
859
+ $(doc).unbind(duringDragEvents);
860
+ $(doc.body).removeClass("sp-dragging");
861
+ onstop.apply(element, arguments);
862
+ }
863
+ dragging = false;
864
+ }
865
+
866
+ $(element).bind(hasTouch ? "touchstart" : "mousedown", start);
867
+ }
868
+
869
+ function throttle(func, wait, debounce) {
870
+ var timeout;
871
+ return function () {
872
+ var context = this, args = arguments;
873
+ var throttler = function () {
874
+ timeout = null;
875
+ func.apply(context, args);
876
+ };
877
+ if (debounce) clearTimeout(timeout);
878
+ if (debounce || !timeout) timeout = setTimeout(throttler, wait);
879
+ };
880
+ }
881
+
882
+
883
+ /**
884
+ * Define a jQuery plugin
885
+ */
886
+ var dataID = "spectrum.id";
887
+ $.fn.spectrum = function (opts, extra) {
888
+
889
+ if (typeof opts == "string") {
890
+
891
+ var returnValue = this;
892
+ var args = Array.prototype.slice.call( arguments, 1 );
893
+
894
+ this.each(function () {
895
+ var spect = spectrums[$(this).data(dataID)];
896
+ if (spect) {
897
+
898
+ var method = spect[opts];
899
+ if (!method) {
900
+ throw new Error( "Spectrum: no such method: '" + opts + "'" );
901
+ }
902
+
903
+ if (opts == "get") {
904
+ returnValue = spect.get();
905
+ }
906
+ else if (opts == "container") {
907
+ returnValue = spect.container;
908
+ }
909
+ else if (opts == "option") {
910
+ returnValue = spect.option.apply(spect, args);
911
+ }
912
+ else if (opts == "destroy") {
913
+ spect.destroy();
914
+ $(this).removeData(dataID);
915
+ }
916
+ else {
917
+ method.apply(spect, args);
918
+ }
919
+ }
920
+ });
921
+
922
+ return returnValue;
923
+ }
924
+
925
+ // Initializing a new instance of spectrum
926
+ return this.spectrum("destroy").each(function () {
927
+ var spect = spectrum(this, opts);
928
+ $(this).data(dataID, spect.id);
929
+ });
930
+ };
931
+
932
+ $.fn.spectrum.load = true;
933
+ $.fn.spectrum.loadOpts = {};
934
+ $.fn.spectrum.draggable = draggable;
935
+ $.fn.spectrum.defaults = defaultOpts;
936
+
937
+ $.spectrum = { };
938
+ $.spectrum.localization = { };
939
+ $.spectrum.palettes = { };
940
+
941
+ $.fn.spectrum.processNativeColorInputs = function () {
942
+ var colorInput = $("<input type='color' value='!' />")[0];
943
+ var supportsColor = colorInput.type === "color" && colorInput.value != "!";
944
+
945
+ if (!supportsColor) {
946
+ $("input[type=color]").spectrum({
947
+ preferredFormat: "hex6"
948
+ });
949
+ }
950
+ };
951
+
952
+ // TinyColor.js - <https://github.com/bgrins/TinyColor> - 2011 Brian Grinstead - v0.5
953
+
954
+ (function (window) {
955
+
956
+ var trimLeft = /^[\s,#]+/,
957
+ trimRight = /\s+$/,
958
+ tinyCounter = 0,
959
+ math = Math,
960
+ mathRound = math.round,
961
+ mathMin = math.min,
962
+ mathMax = math.max,
963
+ mathRandom = math.random,
964
+ parseFloat = window.parseFloat;
965
+
966
+ function tinycolor(color, opts) {
967
+
968
+ // If input is already a tinycolor, return itself
969
+ if (typeof color == "object" && color.hasOwnProperty("_tc_id")) {
970
+ return color;
971
+ }
972
+
973
+ var rgb = inputToRGB(color);
974
+ var r = rgb.r, g = rgb.g, b = rgb.b, a = parseFloat(rgb.a), format = rgb.format;
975
+
976
+ return {
977
+ ok: rgb.ok,
978
+ format: format,
979
+ _tc_id: tinyCounter++,
980
+ alpha: a,
981
+ toHsv: function () {
982
+ var hsv = rgbToHsv(r, g, b);
983
+ return { h: hsv.h, s: hsv.s, v: hsv.v, a: a };
984
+ },
985
+ toHsvString: function () {
986
+ var hsv = rgbToHsv(r, g, b);
987
+ var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);
988
+ return (a == 1) ?
989
+ "hsv(" + h + ", " + s + "%, " + v + "%)" :
990
+ "hsva(" + h + ", " + s + "%, " + v + "%, " + a + ")";
991
+ },
992
+ toHsl: function () {
993
+ var hsl = rgbToHsl(r, g, b);
994
+ return { h: hsl.h, s: hsl.s, l: hsl.l, a: a };
995
+ },
996
+ toHslString: function () {
997
+ var hsl = rgbToHsl(r, g, b);
998
+ var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);
999
+ return (a == 1) ?
1000
+ "hsl(" + h + ", " + s + "%, " + l + "%)" :
1001
+ "hsla(" + h + ", " + s + "%, " + l + "%, " + a + ")";
1002
+ },
1003
+ toHex: function () {
1004
+ return rgbToHex(r, g, b);
1005
+ },
1006
+ toHexString: function (force6Char) {
1007
+ return '#' + rgbToHex(r, g, b, force6Char);
1008
+ },
1009
+ toRgb: function () {
1010
+ return { r: mathRound(r), g: mathRound(g), b: mathRound(b), a: a };
1011
+ },
1012
+ toRgbString: function () {
1013
+ return (a == 1) ?
1014
+ "rgb(" + mathRound(r) + ", " + mathRound(g) + ", " + mathRound(b) + ")" :
1015
+ "rgba(" + mathRound(r) + ", " + mathRound(g) + ", " + mathRound(b) + ", " + a + ")";
1016
+ },
1017
+ toName: function () {
1018
+ return hexNames[rgbToHex(r, g, b)] || false;
1019
+ },
1020
+ toFilter: function (opts, secondColor) {
1021
+
1022
+ var hex = rgbToHex(r, g, b, true);
1023
+ var secondHex = hex;
1024
+ var alphaHex = Math.round(parseFloat(a) * 255).toString(16);
1025
+ var secondAlphaHex = alphaHex;
1026
+ var gradientType = opts && opts.gradientType ? "GradientType = 1, " : "";
1027
+
1028
+ if (secondColor) {
1029
+ var s = tinycolor(secondColor);
1030
+ secondHex = s.toHex();
1031
+ secondAlphaHex = Math.round(parseFloat(s.alpha) * 255).toString(16);
1032
+ }
1033
+
1034
+ return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr=#" + pad2(alphaHex) + hex + ",endColorstr=#" + pad2(secondAlphaHex) + secondHex + ")";
1035
+ },
1036
+ toString: function (format) {
1037
+ format = format || this.format;
1038
+ var formattedString = false;
1039
+ if (format === "rgb") {
1040
+ formattedString = this.toRgbString();
1041
+ }
1042
+ if (format === "hex") {
1043
+ formattedString = this.toHexString();
1044
+ }
1045
+ if (format === "hex6") {
1046
+ formattedString = this.toHexString(true);
1047
+ }
1048
+ if (format === "name") {
1049
+ formattedString = this.toName();
1050
+ }
1051
+ if (format === "hsl") {
1052
+ formattedString = this.toHslString();
1053
+ }
1054
+ if (format === "hsv") {
1055
+ formattedString = this.toHsvString();
1056
+ }
1057
+
1058
+ return formattedString || this.toHexString(true);
1059
+ }
1060
+ };
1061
+ }
1062
+
1063
+ // If input is an object, force 1 into "1.0" to handle ratios properly
1064
+ // String input requires "1.0" as input, so 1 will be treated as 1
1065
+ tinycolor.fromRatio = function (color) {
1066
+
1067
+ if (typeof color == "object") {
1068
+ for (var i in color) {
1069
+ if (color[i] === 1) {
1070
+ color[i] = "1.0";
1071
+ }
1072
+ }
1073
+ }
1074
+
1075
+ return tinycolor(color);
1076
+
1077
+ };
1078
+
1079
+ // Given a string or object, convert that input to RGB
1080
+ // Possible string inputs:
1081
+ //
1082
+ // "red"
1083
+ // "#f00" or "f00"
1084
+ // "#ff0000" or "ff0000"
1085
+ // "rgb 255 0 0" or "rgb (255, 0, 0)"
1086
+ // "rgb 1.0 0 0" or "rgb (1, 0, 0)"
1087
+ // "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
1088
+ // "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
1089
+ // "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
1090
+ // "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
1091
+ // "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
1092
+ //
1093
+ function inputToRGB(color) {
1094
+
1095
+ var rgb = { r: 0, g: 0, b: 0 };
1096
+ var a = 1;
1097
+ var ok = false;
1098
+ var format = false;
1099
+
1100
+ if (typeof color == "string") {
1101
+ color = stringInputToObject(color);
1102
+ }
1103
+
1104
+ if (typeof color == "object") {
1105
+ if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) {
1106
+ rgb = rgbToRgb(color.r, color.g, color.b);
1107
+ ok = true;
1108
+ format = "rgb";
1109
+ }
1110
+ else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("v")) {
1111
+ rgb = hsvToRgb(color.h, color.s, color.v);
1112
+ ok = true;
1113
+ format = "hsv";
1114
+ }
1115
+ else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("l")) {
1116
+ rgb = hslToRgb(color.h, color.s, color.l);
1117
+ ok = true;
1118
+ format = "hsl";
1119
+ }
1120
+
1121
+ if (color.hasOwnProperty("a")) {
1122
+ a = color.a;
1123
+ }
1124
+ }
1125
+
1126
+ rgb.r = mathMin(255, mathMax(rgb.r, 0));
1127
+ rgb.g = mathMin(255, mathMax(rgb.g, 0));
1128
+ rgb.b = mathMin(255, mathMax(rgb.b, 0));
1129
+
1130
+
1131
+ // Don't let the range of [0,255] come back in [0,1].
1132
+ // Potentially lose a little bit of precision here, but will fix issues where
1133
+ // .5 gets interpreted as half of the total, instead of half of 1.
1134
+ // If it was supposed to be 128, this was already taken care of in the conversion function
1135
+ if (rgb.r < 1) { rgb.r = mathRound(rgb.r); }
1136
+ if (rgb.g < 1) { rgb.g = mathRound(rgb.g); }
1137
+ if (rgb.b < 1) { rgb.b = mathRound(rgb.b); }
1138
+
1139
+ return {
1140
+ ok: ok,
1141
+ format: (color && color.format) || format,
1142
+ r: rgb.r,
1143
+ g: rgb.g,
1144
+ b: rgb.b,
1145
+ a: a
1146
+ };
1147
+ }
1148
+
1149
+
1150
+
1151
+ // Conversion Functions
1152
+ // --------------------
1153
+
1154
+ // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:
1155
+ // <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>
1156
+
1157
+ // `rgbToRgb`
1158
+ // Handle bounds / percentage checking to conform to CSS color spec
1159
+ // <http://www.w3.org/TR/css3-color/>
1160
+ // *Assumes:* r, g, b in [0, 255] or [0, 1]
1161
+ // *Returns:* { r, g, b } in [0, 255]
1162
+ function rgbToRgb(r, g, b) {
1163
+ return {
1164
+ r: bound01(r, 255) * 255,
1165
+ g: bound01(g, 255) * 255,
1166
+ b: bound01(b, 255) * 255
1167
+ };
1168
+ }
1169
+
1170
+ // `rgbToHsl`
1171
+ // Converts an RGB color value to HSL.
1172
+ // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]
1173
+ // *Returns:* { h, s, l } in [0,1]
1174
+ function rgbToHsl(r, g, b) {
1175
+
1176
+ r = bound01(r, 255);
1177
+ g = bound01(g, 255);
1178
+ b = bound01(b, 255);
1179
+
1180
+ var max = mathMax(r, g, b), min = mathMin(r, g, b);
1181
+ var h, s, l = (max + min) / 2;
1182
+
1183
+ if (max == min) {
1184
+ h = s = 0; // achromatic
1185
+ }
1186
+ else {
1187
+ var d = max - min;
1188
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
1189
+ switch (max) {
1190
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
1191
+ case g: h = (b - r) / d + 2; break;
1192
+ case b: h = (r - g) / d + 4; break;
1193
+ }
1194
+
1195
+ h /= 6;
1196
+ }
1197
+
1198
+ return { h: h, s: s, l: l };
1199
+ }
1200
+
1201
+ // `hslToRgb`
1202
+ // Converts an HSL color value to RGB.
1203
+ // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]
1204
+ // *Returns:* { r, g, b } in the set [0, 255]
1205
+ function hslToRgb(h, s, l) {
1206
+ var r, g, b;
1207
+
1208
+ h = bound01(h, 360);
1209
+ s = bound01(s, 100);
1210
+ l = bound01(l, 100);
1211
+
1212
+ function hue2rgb(p, q, t) {
1213
+ if (t < 0) t += 1;
1214
+ if (t > 1) t -= 1;
1215
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
1216
+ if (t < 1 / 2) return q;
1217
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
1218
+ return p;
1219
+ }
1220
+
1221
+ if (s === 0) {
1222
+ r = g = b = l; // achromatic
1223
+ }
1224
+ else {
1225
+ var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
1226
+ var p = 2 * l - q;
1227
+ r = hue2rgb(p, q, h + 1 / 3);
1228
+ g = hue2rgb(p, q, h);
1229
+ b = hue2rgb(p, q, h - 1 / 3);
1230
+ }
1231
+
1232
+ return { r: r * 255, g: g * 255, b: b * 255 };
1233
+ }
1234
+
1235
+ // `rgbToHsv`
1236
+ // Converts an RGB color value to HSV
1237
+ // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
1238
+ // *Returns:* { h, s, v } in [0,1]
1239
+ function rgbToHsv(r, g, b) {
1240
+
1241
+ r = bound01(r, 255);
1242
+ g = bound01(g, 255);
1243
+ b = bound01(b, 255);
1244
+
1245
+ var max = mathMax(r, g, b), min = mathMin(r, g, b);
1246
+ var h, s, v = max;
1247
+
1248
+ var d = max - min;
1249
+ s = max === 0 ? 0 : d / max;
1250
+
1251
+ if (max == min) {
1252
+ h = 0; // achromatic
1253
+ }
1254
+ else {
1255
+ switch (max) {
1256
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
1257
+ case g: h = (b - r) / d + 2; break;
1258
+ case b: h = (r - g) / d + 4; break;
1259
+ }
1260
+ h /= 6;
1261
+ }
1262
+ return { h: h, s: s, v: v };
1263
+ }
1264
+
1265
+ // `hsvToRgb`
1266
+ // Converts an HSV color value to RGB.
1267
+ // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
1268
+ // *Returns:* { r, g, b } in the set [0, 255]
1269
+ function hsvToRgb(h, s, v) {
1270
+ h = bound01(h, 360) * 6;
1271
+ s = bound01(s, 100);
1272
+ v = bound01(v, 100);
1273
+
1274
+ var i = math.floor(h),
1275
+ f = h - i,
1276
+ p = v * (1 - s),
1277
+ q = v * (1 - f * s),
1278
+ t = v * (1 - (1 - f) * s),
1279
+ mod = i % 6,
1280
+ r = [v, q, p, p, t, v][mod],
1281
+ g = [t, v, v, q, p, p][mod],
1282
+ b = [p, p, t, v, v, q][mod];
1283
+
1284
+ return { r: r * 255, g: g * 255, b: b * 255 };
1285
+ }
1286
+
1287
+ // `rgbToHex`
1288
+ // Converts an RGB color to hex
1289
+ // Assumes r, g, and b are contained in the set [0, 255]
1290
+ // Returns a 3 or 6 character hex
1291
+ function rgbToHex(r, g, b, force6Char) {
1292
+
1293
+ var hex = [
1294
+ pad2(mathRound(r).toString(16)),
1295
+ pad2(mathRound(g).toString(16)),
1296
+ pad2(mathRound(b).toString(16))
1297
+ ];
1298
+
1299
+ // Return a 3 character hex if possible
1300
+ if (!force6Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) {
1301
+ return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
1302
+ }
1303
+
1304
+ return hex.join("");
1305
+ }
1306
+
1307
+ // `equals`
1308
+ // Can be called with any tinycolor input
1309
+ tinycolor.equals = function (color1, color2) {
1310
+ if (!color1 || !color2) { return false; }
1311
+ return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();
1312
+ };
1313
+ tinycolor.random = function () {
1314
+ return tinycolor.fromRatio({
1315
+ r: mathRandom(),
1316
+ g: mathRandom(),
1317
+ b: mathRandom()
1318
+ });
1319
+ };
1320
+
1321
+
1322
+ // Modification Functions
1323
+ // ----------------------
1324
+ // Thanks to less.js for some of the basics here
1325
+ // <https://github.com/cloudhead/less.js/blob/master/lib/less/functions.js>
1326
+
1327
+
1328
+ tinycolor.desaturate = function (color, amount) {
1329
+ var hsl = tinycolor(color).toHsl();
1330
+ hsl.s -= ((amount || 10) / 100);
1331
+ hsl.s = clamp01(hsl.s);
1332
+ return tinycolor(hsl);
1333
+ };
1334
+ tinycolor.saturate = function (color, amount) {
1335
+ var hsl = tinycolor(color).toHsl();
1336
+ hsl.s += ((amount || 10) / 100);
1337
+ hsl.s = clamp01(hsl.s);
1338
+ return tinycolor(hsl);
1339
+ };
1340
+ tinycolor.greyscale = function (color) {
1341
+ return tinycolor.desaturate(color, 100);
1342
+ };
1343
+ tinycolor.lighten = function (color, amount) {
1344
+ var hsl = tinycolor(color).toHsl();
1345
+ hsl.l += ((amount || 10) / 100);
1346
+ hsl.l = clamp01(hsl.l);
1347
+ return tinycolor(hsl);
1348
+ };
1349
+ tinycolor.darken = function (color, amount) {
1350
+ var hsl = tinycolor(color).toHsl();
1351
+ hsl.l -= ((amount || 10) / 100);
1352
+ hsl.l = clamp01(hsl.l);
1353
+ return tinycolor(hsl);
1354
+ };
1355
+ tinycolor.complement = function (color) {
1356
+ var hsl = tinycolor(color).toHsl();
1357
+ hsl.h = (hsl.h + 0.5) % 1;
1358
+ return tinycolor(hsl);
1359
+ };
1360
+
1361
+
1362
+ // Combination Functions
1363
+ // ---------------------
1364
+ // Thanks to jQuery xColor for some of the ideas behind these
1365
+ // <https://github.com/infusion/jQuery-xcolor/blob/master/jquery.xcolor.js>
1366
+
1367
+ tinycolor.triad = function (color) {
1368
+ var hsl = tinycolor(color).toHsl();
1369
+ var h = hsl.h * 360;
1370
+ return [
1371
+ tinycolor(color),
1372
+ tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),
1373
+ tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })
1374
+ ];
1375
+ };
1376
+ tinycolor.tetrad = function (color) {
1377
+ var hsl = tinycolor(color).toHsl();
1378
+ var h = hsl.h * 360;
1379
+ return [
1380
+ tinycolor(color),
1381
+ tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),
1382
+ tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),
1383
+ tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })
1384
+ ];
1385
+ };
1386
+ tinycolor.splitcomplement = function (color) {
1387
+ var hsl = tinycolor(color).toHsl();
1388
+ var h = hsl.h * 360;
1389
+ return [
1390
+ tinycolor(color),
1391
+ tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l }),
1392
+ tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l })
1393
+ ];
1394
+ };
1395
+ tinycolor.analogous = function (color, results, slices) {
1396
+ results = results || 6;
1397
+ slices = slices || 30;
1398
+
1399
+ var hsl = tinycolor(color).toHsl();
1400
+ var part = 360 / slices;
1401
+ var ret = [tinycolor(color)];
1402
+
1403
+ hsl.h *= 360;
1404
+
1405
+ for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {
1406
+ hsl.h = (hsl.h + part) % 360;
1407
+ ret.push(tinycolor(hsl));
1408
+ }
1409
+ return ret;
1410
+ };
1411
+ tinycolor.monochromatic = function (color, results) {
1412
+ results = results || 6;
1413
+ var hsv = tinycolor(color).toHsv();
1414
+ var h = hsv.h, s = hsv.s, v = hsv.v;
1415
+ var ret = [];
1416
+ var modification = 1 / results;
1417
+
1418
+ while (results--) {
1419
+ ret.push(tinycolor({ h: h, s: s, v: v }));
1420
+ v = (v + modification) % 1;
1421
+ }
1422
+
1423
+ return ret;
1424
+ };
1425
+ tinycolor.readable = function (color1, color2) {
1426
+ var a = tinycolor(color1).toRgb(), b = tinycolor(color2).toRgb();
1427
+ return (
1428
+ (b.r - a.r) * (b.r - a.r) +
1429
+ (b.g - a.g) * (b.g - a.g) +
1430
+ (b.b - a.b) * (b.b - a.b)
1431
+ ) > 0x28A4;
1432
+ };
1433
+
1434
+ // Big List of Colors
1435
+ // ---------
1436
+ // <http://www.w3.org/TR/css3-color/#svg-color>
1437
+ var names = tinycolor.names = {
1438
+ aliceblue: "f0f8ff",
1439
+ antiquewhite: "faebd7",
1440
+ aqua: "0ff",
1441
+ aquamarine: "7fffd4",
1442
+ azure: "f0ffff",
1443
+ beige: "f5f5dc",
1444
+ bisque: "ffe4c4",
1445
+ black: "000",
1446
+ blanchedalmond: "ffebcd",
1447
+ blue: "00f",
1448
+ blueviolet: "8a2be2",
1449
+ brown: "a52a2a",
1450
+ burlywood: "deb887",
1451
+ burntsienna: "ea7e5d",
1452
+ cadetblue: "5f9ea0",
1453
+ chartreuse: "7fff00",
1454
+ chocolate: "d2691e",
1455
+ coral: "ff7f50",
1456
+ cornflowerblue: "6495ed",
1457
+ cornsilk: "fff8dc",
1458
+ crimson: "dc143c",
1459
+ cyan: "0ff",
1460
+ darkblue: "00008b",
1461
+ darkcyan: "008b8b",
1462
+ darkgoldenrod: "b8860b",
1463
+ darkgray: "a9a9a9",
1464
+ darkgreen: "006400",
1465
+ darkgrey: "a9a9a9",
1466
+ darkkhaki: "bdb76b",
1467
+ darkmagenta: "8b008b",
1468
+ darkolivegreen: "556b2f",
1469
+ darkorange: "ff8c00",
1470
+ darkorchid: "9932cc",
1471
+ darkred: "8b0000",
1472
+ darksalmon: "e9967a",
1473
+ darkseagreen: "8fbc8f",
1474
+ darkslateblue: "483d8b",
1475
+ darkslategray: "2f4f4f",
1476
+ darkslategrey: "2f4f4f",
1477
+ darkturquoise: "00ced1",
1478
+ darkviolet: "9400d3",
1479
+ deeppink: "ff1493",
1480
+ deepskyblue: "00bfff",
1481
+ dimgray: "696969",
1482
+ dimgrey: "696969",
1483
+ dodgerblue: "1e90ff",
1484
+ firebrick: "b22222",
1485
+ floralwhite: "fffaf0",
1486
+ forestgreen: "228b22",
1487
+ fuchsia: "f0f",
1488
+ gainsboro: "dcdcdc",
1489
+ ghostwhite: "f8f8ff",
1490
+ gold: "ffd700",
1491
+ goldenrod: "daa520",
1492
+ gray: "808080",
1493
+ green: "008000",
1494
+ greenyellow: "adff2f",
1495
+ grey: "808080",
1496
+ honeydew: "f0fff0",
1497
+ hotpink: "ff69b4",
1498
+ indianred: "cd5c5c",
1499
+ indigo: "4b0082",
1500
+ ivory: "fffff0",
1501
+ khaki: "f0e68c",
1502
+ lavender: "e6e6fa",
1503
+ lavenderblush: "fff0f5",
1504
+ lawngreen: "7cfc00",
1505
+ lemonchiffon: "fffacd",
1506
+ lightblue: "add8e6",
1507
+ lightcoral: "f08080",
1508
+ lightcyan: "e0ffff",
1509
+ lightgoldenrodyellow: "fafad2",
1510
+ lightgray: "d3d3d3",
1511
+ lightgreen: "90ee90",
1512
+ lightgrey: "d3d3d3",
1513
+ lightpink: "ffb6c1",
1514
+ lightsalmon: "ffa07a",
1515
+ lightseagreen: "20b2aa",
1516
+ lightskyblue: "87cefa",
1517
+ lightslategray: "789",
1518
+ lightslategrey: "789",
1519
+ lightsteelblue: "b0c4de",
1520
+ lightyellow: "ffffe0",
1521
+ lime: "0f0",
1522
+ limegreen: "32cd32",
1523
+ linen: "faf0e6",
1524
+ magenta: "f0f",
1525
+ maroon: "800000",
1526
+ mediumaquamarine: "66cdaa",
1527
+ mediumblue: "0000cd",
1528
+ mediumorchid: "ba55d3",
1529
+ mediumpurple: "9370db",
1530
+ mediumseagreen: "3cb371",
1531
+ mediumslateblue: "7b68ee",
1532
+ mediumspringgreen: "00fa9a",
1533
+ mediumturquoise: "48d1cc",
1534
+ mediumvioletred: "c71585",
1535
+ midnightblue: "191970",
1536
+ mintcream: "f5fffa",
1537
+ mistyrose: "ffe4e1",
1538
+ moccasin: "ffe4b5",
1539
+ navajowhite: "ffdead",
1540
+ navy: "000080",
1541
+ oldlace: "fdf5e6",
1542
+ olive: "808000",
1543
+ olivedrab: "6b8e23",
1544
+ orange: "ffa500",
1545
+ orangered: "ff4500",
1546
+ orchid: "da70d6",
1547
+ palegoldenrod: "eee8aa",
1548
+ palegreen: "98fb98",
1549
+ paleturquoise: "afeeee",
1550
+ palevioletred: "db7093",
1551
+ papayawhip: "ffefd5",
1552
+ peachpuff: "ffdab9",
1553
+ peru: "cd853f",
1554
+ pink: "ffc0cb",
1555
+ plum: "dda0dd",
1556
+ powderblue: "b0e0e6",
1557
+ purple: "800080",
1558
+ red: "f00",
1559
+ rosybrown: "bc8f8f",
1560
+ royalblue: "4169e1",
1561
+ saddlebrown: "8b4513",
1562
+ salmon: "fa8072",
1563
+ sandybrown: "f4a460",
1564
+ seagreen: "2e8b57",
1565
+ seashell: "fff5ee",
1566
+ sienna: "a0522d",
1567
+ silver: "c0c0c0",
1568
+ skyblue: "87ceeb",
1569
+ slateblue: "6a5acd",
1570
+ slategray: "708090",
1571
+ slategrey: "708090",
1572
+ snow: "fffafa",
1573
+ springgreen: "00ff7f",
1574
+ steelblue: "4682b4",
1575
+ tan: "d2b48c",
1576
+ teal: "008080",
1577
+ thistle: "d8bfd8",
1578
+ tomato: "ff6347",
1579
+ turquoise: "40e0d0",
1580
+ violet: "ee82ee",
1581
+ wheat: "f5deb3",
1582
+ white: "fff",
1583
+ whitesmoke: "f5f5f5",
1584
+ yellow: "ff0",
1585
+ yellowgreen: "9acd32"
1586
+ };
1587
+
1588
+ // Make it easy to access colors via `hexNames[hex]`
1589
+ var hexNames = tinycolor.hexNames = flip(names);
1590
+
1591
+
1592
+ // Utilities
1593
+ // ---------
1594
+
1595
+ // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`
1596
+ function flip(o) {
1597
+ var flipped = {};
1598
+ for (var i in o) {
1599
+ if (o.hasOwnProperty(i)) {
1600
+ flipped[o[i]] = i;
1601
+ }
1602
+ }
1603
+ return flipped;
1604
+ }
1605
+
1606
+ // Take input from [0, n] and return it as [0, 1]
1607
+ function bound01(n, max) {
1608
+ if (isOnePointZero(n)) { n = "100%"; }
1609
+
1610
+ var processPercent = isPercentage(n);
1611
+ n = mathMin(max, mathMax(0, parseFloat(n)));
1612
+
1613
+ // Automatically convert percentage into number
1614
+ if (processPercent) {
1615
+ n = n * (max / 100);
1616
+ }
1617
+
1618
+ // Handle floating point rounding errors
1619
+ if (math.abs(n - max) < 0.000001) {
1620
+ return 1;
1621
+ }
1622
+ else if (n >= 1) {
1623
+ return (n % max) / parseFloat(max);
1624
+ }
1625
+ return n;
1626
+ }
1627
+
1628
+ // Force a number between 0 and 1
1629
+ function clamp01(val) {
1630
+ return mathMin(1, mathMax(0, val));
1631
+ }
1632
+
1633
+ // Parse an integer into hex
1634
+ function parseHex(val) {
1635
+ return parseInt(val, 16);
1636
+ }
1637
+
1638
+ // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
1639
+ // <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
1640
+ function isOnePointZero(n) {
1641
+ return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1;
1642
+ }
1643
+
1644
+ // Check to see if string passed in is a percentage
1645
+ function isPercentage(n) {
1646
+ return typeof n === "string" && n.indexOf('%') != -1;
1647
+ }
1648
+
1649
+ // Force a hex value to have 2 characters
1650
+ function pad2(c) {
1651
+ return c.length == 1 ? '0' + c : '' + c;
1652
+ }
1653
+
1654
+ var matchers = (function () {
1655
+
1656
+ // <http://www.w3.org/TR/css3-values/#integers>
1657
+ var CSS_INTEGER = "[-\\+]?\\d+%?";
1658
+
1659
+ // <http://www.w3.org/TR/css3-values/#number-value>
1660
+ var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?";
1661
+
1662
+ // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
1663
+ var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")";
1664
+
1665
+ // Actual matching.
1666
+ // Parentheses and commas are optional, but not required.
1667
+ // Whitespace can take the place of commas or opening paren
1668
+ var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
1669
+ var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
1670
+
1671
+ return {
1672
+ rgb: new RegExp("rgb" + PERMISSIVE_MATCH3),
1673
+ rgba: new RegExp("rgba" + PERMISSIVE_MATCH4),
1674
+ hsl: new RegExp("hsl" + PERMISSIVE_MATCH3),
1675
+ hsla: new RegExp("hsla" + PERMISSIVE_MATCH4),
1676
+ hsv: new RegExp("hsv" + PERMISSIVE_MATCH3),
1677
+ hex3: /^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
1678
+ hex6: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/
1679
+ };
1680
+ })();
1681
+
1682
+ // `stringInputToObject`
1683
+ // Permissive string parsing. Take in a number of formats, and output an object
1684
+ // based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`
1685
+ function stringInputToObject(color) {
1686
+
1687
+ color = color.replace(trimLeft, '').replace(trimRight, '').toLowerCase();
1688
+ var named = false;
1689
+ if (names[color]) {
1690
+ color = names[color];
1691
+ named = true;
1692
+ }
1693
+ else if (color == 'transparent') {
1694
+ return { r: 0, g: 0, b: 0, a: 0 };
1695
+ }
1696
+
1697
+ // Try to match string input using regular expressions.
1698
+ // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]
1699
+ // Just return an object and let the conversion functions handle that.
1700
+ // This way the result will be the same whether the tinycolor is initialized with string or object.
1701
+ var match;
1702
+ if ((match = matchers.rgb.exec(color))) {
1703
+ return { r: match[1], g: match[2], b: match[3] };
1704
+ }
1705
+ if ((match = matchers.rgba.exec(color))) {
1706
+ return { r: match[1], g: match[2], b: match[3], a: match[4] };
1707
+ }
1708
+ if ((match = matchers.hsl.exec(color))) {
1709
+ return { h: match[1], s: match[2], l: match[3] };
1710
+ }
1711
+ if ((match = matchers.hsla.exec(color))) {
1712
+ return { h: match[1], s: match[2], l: match[3], a: match[4] };
1713
+ }
1714
+ if ((match = matchers.hsv.exec(color))) {
1715
+ return { h: match[1], s: match[2], v: match[3] };
1716
+ }
1717
+ if ((match = matchers.hex6.exec(color))) {
1718
+ return {
1719
+ r: parseHex(match[1]),
1720
+ g: parseHex(match[2]),
1721
+ b: parseHex(match[3]),
1722
+ format: named ? "name" : "hex"
1723
+ };
1724
+ }
1725
+ if ((match = matchers.hex3.exec(color))) {
1726
+ return {
1727
+ r: parseHex(match[1] + '' + match[1]),
1728
+ g: parseHex(match[2] + '' + match[2]),
1729
+ b: parseHex(match[3] + '' + match[3]),
1730
+ format: named ? "name" : "hex"
1731
+ };
1732
+ }
1733
+
1734
+ return false;
1735
+ }
1736
+
1737
+ // Everything is ready, expose to window
1738
+ window.tinycolor = tinycolor;
1739
+
1740
+ })(this);
1741
+
1742
+ $(function () {
1743
+ if ($.fn.spectrum.load) {
1744
+ $.fn.spectrum.processNativeColorInputs();
1745
+ }
1746
+ });
1747
+
1748
+ })(window, jQuery);