1
2WebInspector.Spectrum = function(swatch, rgb)
3{
4 this.document = swatch.ownerDocument;
5 this.swatch = swatch;
6 this.element = this.document.createElement('div');
7 this.element.className = "sp-container";
8 this.element.innerHTML = WebInspector.Spectrum.markup;
9 this.element.addEventListener("click", WebInspector.Spectrum.stopPropagation, false);
10
11 this.swatchInner = this.document.createElement('span');
12 this.swatchInner.className = 'swatch-inner';
13 this.swatch.appendChild(this.swatchInner);
14 this.swatch.parentNode.insertBefore( this.element, swatch.nextSibling );
15
16 this.slider = this.element.querySelectorAll(".sp-hue")[0];
17 this.slideHelper = this.element.querySelectorAll(".sp-slider")[0];
18 this.dragger = this.element.querySelectorAll(".sp-color")[0];
19 this.dragHelper = this.element.querySelectorAll(".sp-dragger")[0];
20 this.rangeSlider = this.element.querySelectorAll(".sp-range")[0];
21 this.button = this.element.querySelectorAll(".sp-range-container div")[0];
22
23 WebInspector.Spectrum.draggable(this.slider, hueDrag.bind(this));
24 WebInspector.Spectrum.draggable(this.dragger, colorDrag.bind(this));
25
26 this.rangeSlider.addEventListener("change", alphaDrag.bind(this), false);
27 this.button.addEventListener("click", this.hide.bind(this), false);
28
29 if (rgb) {
30 this.rgb = rgb;
31 this.updateUI();
32 }
33
34 function hueDrag(dragX, dragY)
35 {
36 this.hsv[0] = (dragY / this.slideHeight);
37
38 this.updateUI();
39 this.onchange();
40 }
41
42 function colorDrag(dragX, dragY)
43 {
44 this.hsv[1] = dragX / this.dragWidth;
45 this.hsv[2] = (this.dragHeight - dragY) / this.dragHeight;
46
47 this.updateUI();
48 this.onchange();
49 }
50
51 function alphaDrag()
52 {
53 this.hsv[3] = this.rangeSlider.value / 100;
54
55 this.updateUI();
56 this.onchange();
57 }
58}
59
60WebInspector.Spectrum.markup = [
61 "<div class='sp-top'>",
62 "<div class='sp-fill'></div>",
63 "<div class='sp-top-inner'>",
64 "<div class='sp-color'>",
65 "<div class='sp-sat'>",
66 "<div class='sp-val'>",
67 "<div class='sp-dragger'></div>",
68 "</div>",
69 "</div>",
70 "</div>",
71 "<div class='sp-hue'>",
72 "<div class='sp-slider'></div>",
73 "</div>",
74 "</div>",
75 "</div>",
76 "<div class='sp-range-container'>",
77 "<input type='range' class='sp-range' min='0' max='100' />",
78 "<div></div>",
79 "</div>"
80].join('');
81
82WebInspector.Spectrum.hsvToRgb = function(h, s, v, a) {
83
84 var r, g, b;
85
86 var i = Math.floor(h * 6);
87 var f = h * 6 - i;
88 var p = v * (1 - s);
89 var q = v * (1 - f * s);
90 var t = v * (1 - (1 - f) * s);
91
92 switch(i % 6) {
93 case 0:
94 r = v, g = t, b = p;
95 break;
96 case 1:
97 r = q, g = v, b = p;
98 break;
99 case 2:
100 r = p, g = v, b = t;
101 break;
102 case 3:
103 r = p, g = q, b = v;
104 break;
105 case 4:
106 r = t, g = p, b = v;
107 break;
108 case 5:
109 r = v, g = p, b = q;
110 break;
111 }
112
113 return [r * 255, g * 255, b * 255, a];
114};
115
116WebInspector.Spectrum.rgbToHsv = function(r, g, b, a) {
117
118 r = r / 255;
119 g = g / 255;
120 b = b / 255;
121
122 var max = Math.max(r, g, b), min = Math.min(r, g, b);
123 var h, s, v = max;
124
125 var d = max - min;
126 s = max == 0 ? 0 : d / max;
127
128 if(max == min) {
129 // achromatic
130 h = 0;
131 }
132 else {
133 switch(max) {
134 case r:
135 h = (g - b) / d + (g < b ? 6 : 0);
136 break;
137 case g:
138 h = (b - r) / d + 2;
139 break;
140 case b:
141 h = (r - g) / d + 4;
142 break;
143 }
144 h /= 6;
145 }
146 return [h, s, v, a];
147};
148
149WebInspector.Spectrum.stopPropagation = function(e) {
150 e.stopPropagation();
151};
152
153WebInspector.Spectrum.addEvent = function (el, name, cb) {
154 if (typeof name === "object") {
155 for (var i in name) {
156 el.addEventListener(i, name[i], false);
157 }
158 }
159 else {
160 el.addEventListener(name, cb, false);
161 }
162};
163
164 WebInspector.Spectrum.removeEvent = function(el, name, cb) {
165 if (typeof name === "object") {
166 for (var i in name) {
167 el.removeEventListener(i, name[i], false);
168 }
169 }
170 else {
171 el.removeEventListener(name, cb, false);
172 }
173};
174
175WebInspector.Spectrum.getOffset = function(el) {
176 return { left: el.totalOffsetLeft(), top: el.totalOffsetTop() };
177};
178
179WebInspector.Spectrum.getScrollOffset = function(el) {
180 var curleft = curtop = 0;
181 if (el.offsetParent) {
182 do {
183 curleft += el.scrollLeft;
184 curtop += el.scrollTop;
185 } while (el = el.offsetParent);
186 }
187 return { left: curleft, top: curtop };
188};
189
190WebInspector.Spectrum.draggable = function(element, onmove, onstart, onstop) {
191
192 onmove = onmove || function() { };
193 onstart = onstart || function() { };
194 onstop = onstop || function() { };
195
196 var doc = element.ownerDocument || document;
197 var dragging = false;
198 var offset = { };
199 var maxHeight = 0;
200 var maxWidth = 0;
201
202 var duringDragEvents = { };
203 duringDragEvents["selectstart"] = prevent;
204 duringDragEvents["dragstart"] = prevent;
205 duringDragEvents["mousemove"] = move;
206 duringDragEvents["mouseup"] = stop;
207
208 function prevent(e)
209 {
210 if (e.stopPropagation)
211 e.stopPropagation();
212
213 if (e.preventDefault)
214 e.preventDefault();
215
216 e.returnValue = false;
217 }
218
219 function move(e)
220 {
221 if (dragging) {
222 var dragX = Math.max(0, Math.min(e.pageX - offset.left + scrollOffset.left, maxWidth));
223 var dragY = Math.max(0, Math.min(e.pageY - offset.top + scrollOffset.top, maxHeight));
224 onmove.apply(element, [dragX, dragY]);
225 }
226 }
227
228 function start(e)
229 {
230 var rightclick = (e.which) ? (e.which == 3) : (e.button == 2);
231
232 if (!rightclick && !dragging) {
233 if (onstart.apply(element, arguments) !== false) {
234 dragging = true;
235 maxHeight = element.clientHeight;
236 maxWidth = element.clientWidth;
237
238 scrollOffset = WebInspector.Spectrum.getScrollOffset(element);
239 offset = WebInspector.Spectrum.getOffset(element);
240
241 WebInspector.Spectrum.addEvent(doc, duringDragEvents);
242
243 prevent(e);
244 }
245 }
246 }
247
248 function stop()
249 {
250 if (dragging) {
251 WebInspector.Spectrum.removeEvent(doc, duringDragEvents);
252 onstop.apply(element, arguments);
253 }
254 dragging = false;
255 }
256
257 WebInspector.Spectrum.addEvent(element, "mousedown", start);
258};
259
260WebInspector.Spectrum.prototype = {
261 set rgb(color)
262 {
263 if (color.length < 4) {
264 color[3] = 1;
265 }
266 this.hsv = WebInspector.Spectrum.rgbToHsv(color[0], color[1], color[2], color[3]);
267 },
268
269 get rgb()
270 {
271 var rgb = WebInspector.Spectrum.hsvToRgb(this.hsv[0], this.hsv[1], this.hsv[2], this.hsv[3]);
272 return [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2]), rgb[3]];
273 },
274
275 get rgbNoSatVal()
276 {
277 var rgb = WebInspector.Spectrum.hsvToRgb(this.hsv[0], 1, 1);
278 return [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2]), rgb[3]];
279 },
280
281 onchange: function() {
282 this._onchange(this.rgb);
283 },
284
285 _onchange: function() {
286
287 },
288
289
290 updateHelperLocations: function() {
291
292 var h = this.hsv[0];
293 var s = this.hsv[1];
294 var v = this.hsv[2];
295
296 // Where to show the little circle in that displays your current selected color
297 var dragX = s * this.dragWidth;
298 var dragY = this.dragHeight - (v * this.dragHeight);
299
300 dragX = Math.max(
301 -this.dragHelperHeight,
302 Math.min(this.dragWidth - this.dragHelperHeight, dragX - this.dragHelperHeight)
303 );
304 dragY = Math.max(
305 -this.dragHelperHeight,
306 Math.min(this.dragHeight - this.dragHelperHeight, dragY - this.dragHelperHeight)
307 );
308
309 this.dragHelper.positionAt(dragX, dragY);
310
311 // Where to show the bar that displays your current selected hue
312 var slideY = (h * this.slideHeight) - this.slideHelperHeight;
313 this.slideHelper.style.top = slideY + "px";
314
315
316 this.rangeSlider.value = this.hsv[3] * 100;
317 },
318
319 updateUI: function() {
320
321 this.updateHelperLocations();
322
323 var rgb = this.rgb;
324 var rgbNoSatVal = this.rgbNoSatVal;
325
326 var flatColor = "rgb(" + rgbNoSatVal[0] + ", " + rgbNoSatVal[1] + ", " + rgbNoSatVal[2] + ")";
327 var fullColor = "rgba(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ", " + rgb[3] + ")";
328
329 this.dragger.style.backgroundColor = flatColor;
330 this.swatchInner.style.backgroundColor = fullColor;
331
332 this.rangeSlider.value = this.hsv[3] * 100;
333 },
334
335 toggle: function(e) {
336 var isShown = this.element.hasStyleClass('sp-show');
337 if (isShown)
338 this.hide(e);
339 else
340 this.show(e);
341
342 return !isShown;
343 },
344
345 show: function(e) {
346
347 if (e)
348 e.stopPropagation();
349
350 this.element.addStyleClass('sp-show');
351 this.swatch.addStyleClass('swatch-active');
352
353 var x = Math.min(
354 this.swatch.offsetParent.clientWidth - this.element.clientWidth - 20,
355 Math.max(0, this.swatch.offsetLeft)
356 );
357
358 var y = this.swatch.offsetTop + this.swatch.clientHeight;
359
360 this.element.positionAt(x, y);
361
362 this.slideHeight = this.slider.offsetHeight;
363 this.dragWidth = this.dragger.offsetWidth;
364 this.dragHeight = this.dragger.offsetHeight;
365 this.dragHelperHeight = this.dragHelper.offsetHeight / 2;
366 this.slideHelperHeight = this.slideHelper.offsetHeight / 2;
367
368 this.updateUI();
369 },
370
371 hide: function(e) {
372
373 if (e)
374 e.stopPropagation();
375
376 this.element.removeStyleClass('sp-show');
377 this.swatch.removeStyleClass('swatch-active');
378
379 this._onhide(this.rgb);
380 },
381
382 _onhide: function() {
383
384 },
385
386 addChangeListener: function(listener) {
387 this._onchange = listener;
388 },
389
390 addHideListener: function(listener) {
391 this._onhide = listener
392 }
393
394};