492424f5992b5f361ca8e7723ab5d1cddd7bf1cf
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / elements / 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     this._draggerElement = this.element.createChild("div", "spectrum-color");
42     this._dragHelperElement = this._draggerElement.createChild("div", "spectrum-sat fill").createChild("div", "spectrum-val fill").createChild("div", "spectrum-dragger");
43
44     this._sliderElement = this.element.createChild("div", "spectrum-hue");
45     this.slideHelper = this._sliderElement.createChild("div", "spectrum-slider");
46
47     var rangeContainer = this.element.createChild("div", "spectrum-range-container");
48     var alphaLabel = rangeContainer.createChild("label");
49     alphaLabel.textContent = WebInspector.UIString("\u03B1:");
50
51     this._alphaElement = rangeContainer.createChild("input", "spectrum-range");
52     this._alphaElement.setAttribute("type", "range");
53     this._alphaElement.setAttribute("min", "0");
54     this._alphaElement.setAttribute("max", "100");
55     this._alphaElement.addEventListener("input", alphaDrag.bind(this), false);
56     this._alphaElement.addEventListener("change", alphaDrag.bind(this), false);
57
58     var displayContainer = this.element.createChild("div", "spectrum-text");
59     var swatchElement = displayContainer.createChild("span", "swatch");
60     this._swatchInnerElement = swatchElement.createChild("span", "swatch-inner");
61     this._displayElement = displayContainer.createChild("span", "source-code spectrum-display-value");
62
63     WebInspector.Spectrum.draggable(this._sliderElement, hueDrag.bind(this));
64     WebInspector.Spectrum.draggable(this._draggerElement, colorDrag.bind(this), colorDragStart.bind(this));
65
66     /**
67      * @param {!Element} element
68      * @param {number} dragX
69      * @param {number} dragY
70      * @this {WebInspector.Spectrum}
71      */
72     function hueDrag(element, dragX, dragY)
73     {
74         this._hsv[0] = (this.slideHeight - dragY) / this.slideHeight;
75
76         this._onchange();
77     }
78
79     var initialHelperOffset;
80
81     /**
82      * @this {WebInspector.Spectrum}
83      */
84     function colorDragStart()
85     {
86         initialHelperOffset = { x: this._dragHelperElement.offsetLeft, y: this._dragHelperElement.offsetTop };
87     }
88
89     /**
90      * @param {!Element} element
91      * @param {number} dragX
92      * @param {number} dragY
93      * @param {!MouseEvent} event
94      * @this {WebInspector.Spectrum}
95      */
96     function colorDrag(element, dragX, dragY, event)
97     {
98         if (event.shiftKey) {
99             if (Math.abs(dragX - initialHelperOffset.x) >= Math.abs(dragY - initialHelperOffset.y))
100                 dragY = initialHelperOffset.y;
101             else
102                 dragX = initialHelperOffset.x;
103         }
104
105         this._hsv[1] = dragX / this.dragWidth;
106         this._hsv[2] = (this.dragHeight - dragY) / this.dragHeight;
107
108         this._onchange();
109     }
110
111     /**
112      * @this {WebInspector.Spectrum}
113      */
114     function alphaDrag()
115     {
116         this._hsv[3] = this._alphaElement.value / 100;
117
118         this._onchange();
119     }
120 };
121
122 WebInspector.Spectrum.Events = {
123     ColorChanged: "ColorChanged"
124 };
125
126 /**
127  * @param {!Element} element
128  * @param {function(!Element, number, number, !MouseEvent)=} onmove
129  * @param {function(!Element, !MouseEvent)=} onstart
130  * @param {function(!Element, !MouseEvent)=} onstop
131  */
132 WebInspector.Spectrum.draggable = function(element, onmove, onstart, onstop) {
133
134     var doc = document;
135     var dragging;
136     var offset;
137     var scrollOffset;
138     var maxHeight;
139     var maxWidth;
140
141     /**
142      * @param {!Event} e
143      */
144     function consume(e)
145     {
146         e.consume(true);
147     }
148
149     /**
150      * @param {!Event} e
151      */
152     function move(e)
153     {
154         if (dragging) {
155             var dragX = Math.max(0, Math.min(e.pageX - offset.left + scrollOffset.left, maxWidth));
156             var dragY = Math.max(0, Math.min(e.pageY - offset.top + scrollOffset.top, maxHeight));
157
158             if (onmove)
159                 onmove(element, dragX, dragY, /** @type {!MouseEvent} */ (e));
160         }
161     }
162
163     /**
164      * @param {!Event} e
165      */
166     function start(e)
167     {
168         var mouseEvent = /** @type {!MouseEvent} */ (e);
169         var rightClick = mouseEvent.which ? (mouseEvent.which === 3) : (mouseEvent.button === 2);
170
171         if (!rightClick && !dragging) {
172
173             if (onstart)
174                 onstart(element, mouseEvent);
175
176             dragging = true;
177             maxHeight = element.clientHeight;
178             maxWidth = element.clientWidth;
179
180             scrollOffset = element.scrollOffset();
181             offset = element.totalOffset();
182
183             doc.addEventListener("selectstart", consume, false);
184             doc.addEventListener("dragstart", consume, false);
185             doc.addEventListener("mousemove", move, false);
186             doc.addEventListener("mouseup", stop, false);
187
188             move(mouseEvent);
189             consume(mouseEvent);
190         }
191     }
192
193     /**
194      * @param {!Event} e
195      */
196     function stop(e)
197     {
198         if (dragging) {
199             doc.removeEventListener("selectstart", consume, false);
200             doc.removeEventListener("dragstart", consume, false);
201             doc.removeEventListener("mousemove", move, false);
202             doc.removeEventListener("mouseup", stop, false);
203
204             if (onstop)
205                 onstop(element, /** @type {!MouseEvent} */ (e));
206         }
207
208         dragging = false;
209     }
210
211     element.addEventListener("mousedown", start, false);
212 };
213
214 WebInspector.Spectrum.prototype = {
215     /**
216      * @param {!WebInspector.Color} color
217      */
218     setColor: function(color)
219     {
220         this._hsv = color.hsva();
221     },
222
223     /**
224      * @return {!WebInspector.Color}
225      */
226     color: function()
227     {
228         return WebInspector.Color.fromHSVA(this._hsv);
229     },
230
231     _colorString: function()
232     {
233         var cf = WebInspector.Color.Format;
234         var format = this._originalFormat;
235         var color = this.color();
236         var originalFormatString = color.toString(this._originalFormat);
237         if (originalFormatString)
238             return originalFormatString;
239
240         if (color.hasAlpha()) {
241             // Everything except HSL(A) should be returned as RGBA if transparency is involved.
242             if (format === cf.HSLA || format === cf.HSL)
243                 return color.toString(cf.HSLA);
244             else
245                 return color.toString(cf.RGBA);
246         }
247
248         if (format === cf.ShortHEX)
249             return color.toString(cf.HEX);
250         console.assert(format === cf.Nickname);
251         return color.toString(cf.RGB);
252     },
253
254
255     set displayText(text)
256     {
257         this._displayElement.textContent = text;
258     },
259
260     _onchange: function()
261     {
262         this._updateUI();
263         this.dispatchEventToListeners(WebInspector.Spectrum.Events.ColorChanged, this._colorString());
264     },
265
266     _updateHelperLocations: function()
267     {
268         var h = this._hsv[0];
269         var s = this._hsv[1];
270         var v = this._hsv[2];
271
272         // Where to show the little circle that displays your current selected color.
273         var dragX = s * this.dragWidth;
274         var dragY = this.dragHeight - (v * this.dragHeight);
275
276         dragX = Math.max(-this._dragHelperElementHeight,
277                         Math.min(this.dragWidth - this._dragHelperElementHeight, dragX - this._dragHelperElementHeight));
278         dragY = Math.max(-this._dragHelperElementHeight,
279                         Math.min(this.dragHeight - this._dragHelperElementHeight, dragY - this._dragHelperElementHeight));
280
281         this._dragHelperElement.positionAt(dragX, dragY);
282
283         // Where to show the bar that displays your current selected hue.
284         var slideY = this.slideHeight - ((h * this.slideHeight) + this.slideHelperHeight);
285         this.slideHelper.style.top = slideY + "px";
286
287         this._alphaElement.value = this._hsv[3] * 100;
288     },
289
290     _updateUI: function()
291     {
292         this._updateHelperLocations();
293
294         this._draggerElement.style.backgroundColor = /** @type {string} */ (WebInspector.Color.fromHSVA([this._hsv[0], 1, 1, 1]).toString(WebInspector.Color.Format.RGB));
295         this._swatchInnerElement.style.backgroundColor = /** @type {string} */ (this.color().toString(WebInspector.Color.Format.RGBA));
296
297         this._alphaElement.value = this._hsv[3] * 100;
298     },
299
300     wasShown: function()
301     {
302         this.slideHeight = this._sliderElement.offsetHeight;
303         this.dragWidth = this._draggerElement.offsetWidth;
304         this.dragHeight = this._draggerElement.offsetHeight;
305         this._dragHelperElementHeight = this._dragHelperElement.offsetHeight / 2;
306         this.slideHelperHeight = this.slideHelper.offsetHeight / 2;
307         this._updateUI();
308     },
309
310     __proto__: WebInspector.VBox.prototype
311 }
312
313 /**
314  * @constructor
315  * @extends {WebInspector.Object}
316  */
317 WebInspector.SpectrumPopupHelper = function()
318 {
319     this._spectrum = new WebInspector.Spectrum();
320     this._spectrum.element.addEventListener("keydown", this._onKeyDown.bind(this), false);
321
322     this._popover = new WebInspector.Popover();
323     this._popover.setCanShrink(false);
324     this._popover.element.addEventListener("mousedown", consumeEvent, false);
325
326     this._hideProxy = this.hide.bind(this, true);
327 }
328
329 WebInspector.SpectrumPopupHelper.Events = {
330     Hidden: "Hidden"
331 };
332
333 WebInspector.SpectrumPopupHelper.prototype = {
334     /**
335      * @return {!WebInspector.Spectrum}
336      */
337     spectrum: function()
338     {
339         return this._spectrum;
340     },
341
342     /**
343      * @return {boolean}
344      */
345     toggle: function(element, color, format)
346     {
347         if (this._popover.isShowing())
348             this.hide(true);
349         else
350             this.show(element, color, format);
351
352         return this._popover.isShowing();
353     },
354
355     /**
356      * @return {boolean}
357      */
358     show: function(element, color, format)
359     {
360         if (this._popover.isShowing()) {
361             if (this._anchorElement === element)
362                 return false;
363
364             // Reopen the picker for another anchor element.
365             this.hide(true);
366         }
367
368         this._anchorElement = element;
369
370         this._spectrum.setColor(color);
371         this._spectrum._originalFormat = format !== WebInspector.Color.Format.Original ? format : color.format();
372         this.reposition(element);
373
374         document.addEventListener("mousedown", this._hideProxy, false);
375         window.addEventListener("blur", this._hideProxy, false);
376         return true;
377     },
378
379     reposition: function(element)
380     {
381         if (!this._previousFocusElement)
382             this._previousFocusElement = WebInspector.currentFocusElement();
383         this._popover.showView(this._spectrum, element);
384         WebInspector.setCurrentFocusElement(this._spectrum.element);
385     },
386
387     /**
388      * @param {boolean=} commitEdit
389      */
390     hide: function(commitEdit)
391     {
392         if (!this._popover.isShowing())
393             return;
394         this._popover.hide();
395
396         document.removeEventListener("mousedown", this._hideProxy, false);
397         window.removeEventListener("blur", this._hideProxy, false);
398
399         this.dispatchEventToListeners(WebInspector.SpectrumPopupHelper.Events.Hidden, !!commitEdit);
400
401         WebInspector.setCurrentFocusElement(this._previousFocusElement);
402         delete this._previousFocusElement;
403
404         delete this._anchorElement;
405     },
406
407     _onKeyDown: function(event)
408     {
409         if (event.keyIdentifier === "Enter") {
410             this.hide(true);
411             event.consume(true);
412             return;
413         }
414         if (event.keyIdentifier === "U+001B") { // Escape key
415             this.hide(false);
416             event.consume(true);
417         }
418     },
419
420     __proto__: WebInspector.Object.prototype
421 }
422
423 /**
424  * @constructor
425  * @param {boolean=} readOnly
426  */
427 WebInspector.ColorSwatch = function(readOnly)
428 {
429     this.element = document.createElementWithClass("span", "swatch");
430     this._swatchInnerElement = this.element.createChild("span", "swatch-inner");
431     var shiftClickMessage = WebInspector.UIString("Shift-click to change color format.");
432     this.element.title = readOnly ? shiftClickMessage : String.sprintf("%s\n%s", WebInspector.UIString("Click to open a colorpicker."), shiftClickMessage);
433     this.element.addEventListener("mousedown", consumeEvent, false);
434     this.element.addEventListener("dblclick", consumeEvent, false);
435 }
436
437 WebInspector.ColorSwatch.prototype = {
438     /**
439      * @param {string} colorString
440      */
441     setColorString: function(colorString)
442     {
443         this._swatchInnerElement.style.backgroundColor = colorString;
444     }
445 }