Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / ui / Spectrum.js
1 /*
2  * Copyright (C) 2011 Brian Grinstead All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 /**
30  * @constructor
31  * @extends {WebInspector.VBox}
32  */
33 WebInspector.Spectrum = function()
34 {
35     WebInspector.VBox.call(this);
36     this.registerRequiredCSS("spectrum.css");
37
38     this.element.classList.add("spectrum-container");
39     this.element.tabIndex = 0;
40
41     var topElement = this.element.createChild("div", "spectrum-top");
42     topElement.createChild("div", "spectrum-fill");
43
44     var topInnerElement = topElement.createChild("div", "spectrum-top-inner fill");
45     this._draggerElement = topInnerElement.createChild("div", "spectrum-color");
46     this._dragHelperElement = this._draggerElement.createChild("div", "spectrum-sat fill").createChild("div", "spectrum-val fill").createChild("div", "spectrum-dragger");
47
48     this._sliderElement = topInnerElement.createChild("div", "spectrum-hue");
49     this.slideHelper = this._sliderElement.createChild("div", "spectrum-slider");
50
51     var rangeContainer = this.element.createChild("div", "spectrum-range-container");
52     var alphaLabel = rangeContainer.createChild("label");
53     alphaLabel.textContent = WebInspector.UIString("\u03B1:");
54
55     this._alphaElement = rangeContainer.createChild("input", "spectrum-range");
56     this._alphaElement.setAttribute("type", "range");
57     this._alphaElement.setAttribute("min", "0");
58     this._alphaElement.setAttribute("max", "100");
59     this._alphaElement.addEventListener("input", alphaDrag.bind(this), false);
60     this._alphaElement.addEventListener("change", alphaDrag.bind(this), false);
61
62     var swatchElement = document.createElement("span");
63     swatchElement.className = "swatch";
64     this._swatchInnerElement = swatchElement.createChild("span", "swatch-inner");
65
66     var displayContainer = this.element.createChild("div");
67     displayContainer.appendChild(swatchElement);
68     this._displayElement = displayContainer.createChild("span", "source-code spectrum-display-value");
69
70     WebInspector.Spectrum.draggable(this._sliderElement, hueDrag.bind(this));
71     WebInspector.Spectrum.draggable(this._draggerElement, colorDrag.bind(this), colorDragStart.bind(this));
72
73     /**
74      * @param {!Element} element
75      * @param {number} dragX
76      * @param {number} dragY
77      * @this {WebInspector.Spectrum}
78      */
79     function hueDrag(element, dragX, dragY)
80     {
81         this._hsv[0] = (this.slideHeight - dragY) / this.slideHeight;
82
83         this._onchange();
84     }
85
86     var initialHelperOffset;
87
88     /**
89      * @this {WebInspector.Spectrum}
90      */
91     function colorDragStart()
92     {
93         initialHelperOffset = { x: this._dragHelperElement.offsetLeft, y: this._dragHelperElement.offsetTop };
94     }
95
96     /**
97      * @param {!Element} element
98      * @param {number} dragX
99      * @param {number} dragY
100      * @param {!MouseEvent} event
101      * @this {WebInspector.Spectrum}
102      */
103     function colorDrag(element, dragX, dragY, event)
104     {
105         if (event.shiftKey) {
106             if (Math.abs(dragX - initialHelperOffset.x) >= Math.abs(dragY - initialHelperOffset.y))
107                 dragY = initialHelperOffset.y;
108             else
109                 dragX = initialHelperOffset.x;
110         }
111
112         this._hsv[1] = dragX / this.dragWidth;
113         this._hsv[2] = (this.dragHeight - dragY) / this.dragHeight;
114
115         this._onchange();
116     }
117
118     /**
119      * @this {WebInspector.Spectrum}
120      */
121     function alphaDrag()
122     {
123         this._hsv[3] = this._alphaElement.value / 100;
124
125         this._onchange();
126     }
127 };
128
129 WebInspector.Spectrum.Events = {
130     ColorChanged: "ColorChanged"
131 };
132
133 /**
134  * @param {function(!Element, number, number, !MouseEvent)=} onmove
135  * @param {function(!Element, !MouseEvent)=} onstart
136  * @param {function(!Element, !MouseEvent)=} onstop
137  */
138 WebInspector.Spectrum.draggable = function(element, onmove, onstart, onstop) {
139
140     var doc = document;
141     var dragging;
142     var offset;
143     var scrollOffset;
144     var maxHeight;
145     var maxWidth;
146
147     /**
148      * @param {?Event} e
149      */
150     function consume(e)
151     {
152         e.consume(true);
153     }
154
155     /**
156      * @param {?Event} e
157      */
158     function move(e)
159     {
160         if (dragging) {
161             var dragX = Math.max(0, Math.min(e.pageX - offset.left + scrollOffset.left, maxWidth));
162             var dragY = Math.max(0, Math.min(e.pageY - offset.top + scrollOffset.top, maxHeight));
163
164             if (onmove)
165                 onmove(element, dragX, dragY, /** @type {!MouseEvent} */ (e));
166         }
167     }
168
169     /**
170      * @param {?Event} e
171      */
172     function start(e)
173     {
174         var mouseEvent = /** @type {!MouseEvent} */ (e);
175         var rightClick = mouseEvent.which ? (mouseEvent.which === 3) : (mouseEvent.button === 2);
176
177         if (!rightClick && !dragging) {
178
179             if (onstart)
180                 onstart(element, mouseEvent);
181
182             dragging = true;
183             maxHeight = element.clientHeight;
184             maxWidth = element.clientWidth;
185
186             scrollOffset = element.scrollOffset();
187             offset = element.totalOffset();
188
189             doc.addEventListener("selectstart", consume, false);
190             doc.addEventListener("dragstart", consume, false);
191             doc.addEventListener("mousemove", move, false);
192             doc.addEventListener("mouseup", stop, false);
193
194             move(mouseEvent);
195             consume(mouseEvent);
196         }
197     }
198
199     /**
200      * @param {?Event} e
201      */
202     function stop(e)
203     {
204         if (dragging) {
205             doc.removeEventListener("selectstart", consume, false);
206             doc.removeEventListener("dragstart", consume, false);
207             doc.removeEventListener("mousemove", move, false);
208             doc.removeEventListener("mouseup", stop, false);
209
210             if (onstop)
211                 onstop(element, /** @type {!MouseEvent} */ (e));
212         }
213
214         dragging = false;
215     }
216
217     element.addEventListener("mousedown", start, false);
218 };
219
220 WebInspector.Spectrum.prototype = {
221     /**
222      * @param {!WebInspector.Color} color
223      */
224     setColor: function(color)
225     {
226         this._hsv = color.hsva();
227     },
228
229     /**
230      * @return {!WebInspector.Color}
231      */
232     color: function()
233     {
234         return WebInspector.Color.fromHSVA(this._hsv);
235     },
236
237     _colorString: function()
238     {
239         var cf = WebInspector.Color.Format;
240         var format = this._originalFormat;
241         var color = this.color();
242         var originalFormatString = color.toString(this._originalFormat);
243         if (originalFormatString)
244             return originalFormatString;
245
246         if (color.hasAlpha()) {
247             // Everything except HSL(A) should be returned as RGBA if transparency is involved.
248             if (format === cf.HSLA || format === cf.HSL)
249                 return color.toString(cf.HSLA);
250             else
251                 return color.toString(cf.RGBA);
252         }
253
254         if (format === cf.ShortHEX)
255             return color.toString(cf.HEX);
256         console.assert(format === cf.Nickname);
257         return color.toString(cf.RGB);
258     },
259
260
261     set displayText(text)
262     {
263         this._displayElement.textContent = text;
264     },
265
266     _onchange: function()
267     {
268         this._updateUI();
269         this.dispatchEventToListeners(WebInspector.Spectrum.Events.ColorChanged, this._colorString());
270     },
271
272     _updateHelperLocations: function()
273     {
274         var h = this._hsv[0];
275         var s = this._hsv[1];
276         var v = this._hsv[2];
277
278         // Where to show the little circle that displays your current selected color.
279         var dragX = s * this.dragWidth;
280         var dragY = this.dragHeight - (v * this.dragHeight);
281
282         dragX = Math.max(-this._dragHelperElementHeight,
283                         Math.min(this.dragWidth - this._dragHelperElementHeight, dragX - this._dragHelperElementHeight));
284         dragY = Math.max(-this._dragHelperElementHeight,
285                         Math.min(this.dragHeight - this._dragHelperElementHeight, dragY - this._dragHelperElementHeight));
286
287         this._dragHelperElement.positionAt(dragX, dragY);
288
289         // Where to show the bar that displays your current selected hue.
290         var slideY = this.slideHeight - ((h * this.slideHeight) + this.slideHelperHeight);
291         this.slideHelper.style.top = slideY + "px";
292
293         this._alphaElement.value = this._hsv[3] * 100;
294     },
295
296     _updateUI: function()
297     {
298         this._updateHelperLocations();
299
300         this._draggerElement.style.backgroundColor = WebInspector.Color.fromHSVA([this._hsv[0], 1, 1, 1]).toString(WebInspector.Color.Format.RGB);
301         this._swatchInnerElement.style.backgroundColor = this.color().toString(WebInspector.Color.Format.RGBA);
302
303         this._alphaElement.value = this._hsv[3] * 100;
304     },
305
306     wasShown: function()
307     {
308         this.slideHeight = this._sliderElement.offsetHeight;
309         this.dragWidth = this._draggerElement.offsetWidth;
310         this.dragHeight = this._draggerElement.offsetHeight;
311         this._dragHelperElementHeight = this._dragHelperElement.offsetHeight / 2;
312         this.slideHelperHeight = this.slideHelper.offsetHeight / 2;
313         this._updateUI();
314     },
315
316     __proto__: WebInspector.VBox.prototype
317 }
318
319 /**
320  * @constructor
321  * @extends {WebInspector.Object}
322  */
323 WebInspector.SpectrumPopupHelper = function()
324 {
325     this._spectrum = new WebInspector.Spectrum();
326     this._spectrum.element.addEventListener("keydown", this._onKeyDown.bind(this), false);
327
328     this._popover = new WebInspector.Popover();
329     this._popover.setCanShrink(false);
330     this._popover.element.addEventListener("mousedown", consumeEvent, false);
331
332     this._hideProxy = this.hide.bind(this, true);
333 }
334
335 WebInspector.SpectrumPopupHelper.Events = {
336     Hidden: "Hidden"
337 };
338
339 WebInspector.SpectrumPopupHelper.prototype = {
340     /**
341      * @return {!WebInspector.Spectrum}
342      */
343     spectrum: function()
344     {
345         return this._spectrum;
346     },
347
348     /**
349      * @return {boolean}
350      */
351     toggle: function(element, color, format)
352     {
353         if (this._popover.isShowing())
354             this.hide(true);
355         else
356             this.show(element, color, format);
357
358         return this._popover.isShowing();
359     },
360
361     /**
362      * @return {boolean}
363      */
364     show: function(element, color, format)
365     {
366         if (this._popover.isShowing()) {
367             if (this._anchorElement === element)
368                 return false;
369
370             // Reopen the picker for another anchor element.
371             this.hide(true);
372         }
373
374         this._anchorElement = element;
375
376         this._spectrum.setColor(color);
377         this._spectrum._originalFormat = format !== WebInspector.Color.Format.Original ? format : color.format();
378         this.reposition(element);
379
380         document.addEventListener("mousedown", this._hideProxy, false);
381         window.addEventListener("blur", this._hideProxy, false);
382         return true;
383     },
384
385     reposition: function(element)
386     {
387         if (!this._previousFocusElement)
388             this._previousFocusElement = WebInspector.currentFocusElement();
389         this._popover.showView(this._spectrum, element);
390         WebInspector.setCurrentFocusElement(this._spectrum.element);
391     },
392
393     /**
394      * @param {boolean=} commitEdit
395      */
396     hide: function(commitEdit)
397     {
398         if (!this._popover.isShowing())
399             return;
400         this._popover.hide();
401
402         document.removeEventListener("mousedown", this._hideProxy, false);
403         window.removeEventListener("blur", this._hideProxy, false);
404
405         this.dispatchEventToListeners(WebInspector.SpectrumPopupHelper.Events.Hidden, !!commitEdit);
406
407         WebInspector.setCurrentFocusElement(this._previousFocusElement);
408         delete this._previousFocusElement;
409
410         delete this._anchorElement;
411     },
412
413     _onKeyDown: function(event)
414     {
415         if (event.keyIdentifier === "Enter") {
416             this.hide(true);
417             event.consume(true);
418             return;
419         }
420         if (event.keyIdentifier === "U+001B") { // Escape key
421             this.hide(false);
422             event.consume(true);
423         }
424     },
425
426     __proto__: WebInspector.Object.prototype
427 }
428
429 /**
430  * @constructor
431  * @param {boolean=} readOnly
432  */
433 WebInspector.ColorSwatch = function(readOnly)
434 {
435     this.element = document.createElement("span");
436     this._swatchInnerElement = this.element.createChild("span", "swatch-inner");
437     var shiftClickMessage = WebInspector.UIString("Shift-click to change color format.");
438     this.element.title = readOnly ? shiftClickMessage : String.sprintf("%s\n%s", WebInspector.UIString("Click to open a colorpicker."), shiftClickMessage);
439     this.element.className = "swatch";
440     this.element.addEventListener("mousedown", consumeEvent, false);
441     this.element.addEventListener("dblclick", consumeEvent, false);
442 }
443
444 WebInspector.ColorSwatch.prototype = {
445     /**
446      * @param {string} colorString
447      */
448     setColorString: function(colorString)
449     {
450         this._swatchInnerElement.style.backgroundColor = colorString;
451     }
452 }