2 * Copyright (C) 2011 Brian Grinstead All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
31 * @extends {WebInspector.VBox}
33 WebInspector.Spectrum = function()
35 WebInspector.VBox.call(this, true);
36 this.contentElement.appendChild(WebInspector.View.createStyleElement("elements/spectrum.css"));
37 this.contentElement.tabIndex = 0;
39 this._draggerElement = this.contentElement.createChild("div", "spectrum-color");
40 this._dragHelperElement = this._draggerElement.createChild("div", "spectrum-sat fill").createChild("div", "spectrum-val fill").createChild("div", "spectrum-dragger");
42 this._sliderElement = this.contentElement.createChild("div", "spectrum-hue");
43 this.slideHelper = this._sliderElement.createChild("div", "spectrum-slider");
45 var rangeContainer = this.contentElement.createChild("div", "spectrum-range-container");
46 var alphaLabel = rangeContainer.createChild("label");
47 alphaLabel.textContent = WebInspector.UIString("\u03B1:");
49 this._alphaElement = rangeContainer.createChild("input", "spectrum-range");
50 this._alphaElement.setAttribute("type", "range");
51 this._alphaElement.setAttribute("min", "0");
52 this._alphaElement.setAttribute("max", "100");
53 this._alphaElement.addEventListener("input", alphaDrag.bind(this), false);
54 this._alphaElement.addEventListener("change", alphaDrag.bind(this), false);
56 var displayContainer = this.contentElement.createChild("div", "spectrum-text");
57 var swatchElement = displayContainer.createChild("span", "swatch");
58 this._swatchInnerElement = swatchElement.createChild("span", "swatch-inner");
59 this._displayElement = displayContainer.createChild("span", "source-code spectrum-display-value");
61 WebInspector.Spectrum.draggable(this._sliderElement, hueDrag.bind(this));
62 WebInspector.Spectrum.draggable(this._draggerElement, colorDrag.bind(this), colorDragStart.bind(this));
65 * @param {!Element} element
66 * @param {number} dragX
67 * @param {number} dragY
68 * @this {WebInspector.Spectrum}
70 function hueDrag(element, dragX, dragY)
72 this._hsv[0] = (this.slideHeight - dragY) / this.slideHeight;
77 var initialHelperOffset;
80 * @this {WebInspector.Spectrum}
82 function colorDragStart()
84 initialHelperOffset = { x: this._dragHelperElement.offsetLeft, y: this._dragHelperElement.offsetTop };
88 * @param {!Element} element
89 * @param {number} dragX
90 * @param {number} dragY
91 * @param {!MouseEvent} event
92 * @this {WebInspector.Spectrum}
94 function colorDrag(element, dragX, dragY, event)
97 if (Math.abs(dragX - initialHelperOffset.x) >= Math.abs(dragY - initialHelperOffset.y))
98 dragY = initialHelperOffset.y;
100 dragX = initialHelperOffset.x;
103 this._hsv[1] = dragX / this.dragWidth;
104 this._hsv[2] = (this.dragHeight - dragY) / this.dragHeight;
110 * @this {WebInspector.Spectrum}
114 this._hsv[3] = this._alphaElement.value / 100;
120 WebInspector.Spectrum.Events = {
121 ColorChanged: "ColorChanged"
125 * @param {!Element} element
126 * @param {function(!Element, number, number, !MouseEvent)=} onmove
127 * @param {function(!Element, !MouseEvent)=} onstart
128 * @param {function(!Element, !MouseEvent)=} onstop
130 WebInspector.Spectrum.draggable = function(element, onmove, onstart, onstop) {
152 var dragX = Math.max(0, Math.min(e.pageX - offset.left + scrollOffset.left, maxWidth));
153 var dragY = Math.max(0, Math.min(e.pageY - offset.top + scrollOffset.top, maxHeight));
156 onmove(element, dragX, dragY, /** @type {!MouseEvent} */ (e));
165 var mouseEvent = /** @type {!MouseEvent} */ (e);
166 var rightClick = mouseEvent.which ? (mouseEvent.which === 3) : (mouseEvent.button === 2);
168 if (!rightClick && !dragging) {
171 onstart(element, mouseEvent);
174 maxHeight = element.clientHeight;
175 maxWidth = element.clientWidth;
177 scrollOffset = element.scrollOffset();
178 offset = element.totalOffset();
180 element.ownerDocument.addEventListener("selectstart", consume, false);
181 element.ownerDocument.addEventListener("dragstart", consume, false);
182 element.ownerDocument.addEventListener("mousemove", move, false);
183 element.ownerDocument.addEventListener("mouseup", stop, false);
196 element.ownerDocument.removeEventListener("selectstart", consume, false);
197 element.ownerDocument.removeEventListener("dragstart", consume, false);
198 element.ownerDocument.removeEventListener("mousemove", move, false);
199 element.ownerDocument.removeEventListener("mouseup", stop, false);
202 onstop(element, /** @type {!MouseEvent} */ (e));
208 element.addEventListener("mousedown", start, false);
211 WebInspector.Spectrum.prototype = {
213 * @param {!WebInspector.Color} color
215 setColor: function(color)
217 this._hsv = color.hsva();
221 * @return {!WebInspector.Color}
225 return WebInspector.Color.fromHSVA(this._hsv);
228 _colorString: function()
230 var cf = WebInspector.Color.Format;
231 var format = this._originalFormat;
232 var color = this.color();
233 var originalFormatString = color.toString(this._originalFormat);
234 if (originalFormatString)
235 return originalFormatString;
237 if (color.hasAlpha()) {
238 // Everything except HSL(A) should be returned as RGBA if transparency is involved.
239 if (format === cf.HSLA || format === cf.HSL)
240 return color.toString(cf.HSLA);
242 return color.toString(cf.RGBA);
245 if (format === cf.ShortHEX)
246 return color.toString(cf.HEX);
247 console.assert(format === cf.Nickname);
248 return color.toString(cf.RGB);
252 set displayText(text)
254 this._displayElement.textContent = text;
257 _onchange: function()
260 this.dispatchEventToListeners(WebInspector.Spectrum.Events.ColorChanged, this._colorString());
263 _updateHelperLocations: function()
265 var h = this._hsv[0];
266 var s = this._hsv[1];
267 var v = this._hsv[2];
269 // Where to show the little circle that displays your current selected color.
270 var dragX = s * this.dragWidth;
271 var dragY = this.dragHeight - (v * this.dragHeight);
273 dragX = Math.max(-this._dragHelperElementHeight,
274 Math.min(this.dragWidth - this._dragHelperElementHeight, dragX - this._dragHelperElementHeight));
275 dragY = Math.max(-this._dragHelperElementHeight,
276 Math.min(this.dragHeight - this._dragHelperElementHeight, dragY - this._dragHelperElementHeight));
278 this._dragHelperElement.positionAt(dragX, dragY);
280 // Where to show the bar that displays your current selected hue.
281 var slideY = this.slideHeight - ((h * this.slideHeight) + this.slideHelperHeight);
282 this.slideHelper.style.top = slideY + "px";
284 this._alphaElement.value = this._hsv[3] * 100;
287 _updateUI: function()
289 this._updateHelperLocations();
291 this._draggerElement.style.backgroundColor = /** @type {string} */ (WebInspector.Color.fromHSVA([this._hsv[0], 1, 1, 1]).toString(WebInspector.Color.Format.RGB));
292 this._swatchInnerElement.style.backgroundColor = /** @type {string} */ (this.color().toString(WebInspector.Color.Format.RGBA));
294 this._alphaElement.value = this._hsv[3] * 100;
299 this.slideHeight = this._sliderElement.offsetHeight;
300 this.dragWidth = this._draggerElement.offsetWidth;
301 this.dragHeight = this._draggerElement.offsetHeight;
302 this._dragHelperElementHeight = this._dragHelperElement.offsetHeight / 2;
303 this.slideHelperHeight = this.slideHelper.offsetHeight / 2;
307 __proto__: WebInspector.VBox.prototype
312 * @extends {WebInspector.Object}
314 WebInspector.SpectrumPopupHelper = function()
316 this._spectrum = new WebInspector.Spectrum();
317 this._spectrum.contentElement.addEventListener("keydown", this._onKeyDown.bind(this), false);
319 this._popover = new WebInspector.Popover();
320 this._popover.setCanShrink(false);
321 this._popover.element.addEventListener("mousedown", consumeEvent, false);
323 this._hideProxy = this.hide.bind(this, true);
326 WebInspector.SpectrumPopupHelper.Events = {
330 WebInspector.SpectrumPopupHelper.prototype = {
332 * @return {!WebInspector.Spectrum}
336 return this._spectrum;
342 toggle: function(element, color, format)
344 if (this._popover.isShowing())
347 this.show(element, color, format);
349 return this._popover.isShowing();
355 show: function(element, color, format)
357 if (this._popover.isShowing()) {
358 if (this._anchorElement === element)
361 // Reopen the picker for another anchor element.
365 delete this._isHidden;
366 this._anchorElement = element;
368 this._spectrum.setColor(color);
369 this._spectrum._originalFormat = format !== WebInspector.Color.Format.Original ? format : color.format();
370 this.reposition(element);
372 var document = this._popover.element.ownerDocument;
373 document.addEventListener("mousedown", this._hideProxy, false);
374 document.defaultView.addEventListener("resize", this._hideProxy, false);
376 WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.ColorPicked, this._colorPicked, this);
377 PageAgent.setColorPickerEnabled(true);
381 reposition: function(element)
383 if (!this._previousFocusElement)
384 this._previousFocusElement = WebInspector.currentFocusElement();
385 this._popover.showView(this._spectrum, element);
386 WebInspector.setCurrentFocusElement(this._spectrum.contentElement);
390 * @param {boolean=} commitEdit
392 hide: function(commitEdit)
396 var document = this._popover.element.ownerDocument;
397 this._isHidden = true;
398 this._popover.hide();
400 document.removeEventListener("mousedown", this._hideProxy, false);
401 document.defaultView.removeEventListener("resize", this._hideProxy, false);
403 PageAgent.setColorPickerEnabled(false);
404 WebInspector.targetManager.removeModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.ColorPicked, this._colorPicked, this);
406 this.dispatchEventToListeners(WebInspector.SpectrumPopupHelper.Events.Hidden, !!commitEdit);
408 WebInspector.setCurrentFocusElement(this._previousFocusElement);
409 delete this._previousFocusElement;
411 delete this._anchorElement;
414 _onKeyDown: function(event)
416 if (event.keyIdentifier === "Enter") {
421 if (event.keyIdentifier === "U+001B") { // Escape key
428 * @param {!WebInspector.Event} event
430 _colorPicked: function(event)
432 var color = /** @type {!DOMAgent.RGBA} */ (event.data);
433 var rgba = [color.r, color.g, color.b, (color.a / 2.55 | 0) / 100];
434 this._spectrum.setColor(WebInspector.Color.fromRGBA(rgba));
435 this._spectrum._onchange();
436 InspectorFrontendHost.bringToFront();
439 __proto__: WebInspector.Object.prototype
444 * @param {boolean=} readOnly
446 WebInspector.ColorSwatch = function(readOnly)
448 this.element = createElementWithClass("span", "swatch");
449 this._swatchInnerElement = this.element.createChild("span", "swatch-inner");
450 var shiftClickMessage = WebInspector.UIString("Shift-click to change color format.");
451 this.element.title = readOnly ? shiftClickMessage : String.sprintf("%s\n%s", WebInspector.UIString("Click to open a colorpicker."), shiftClickMessage);
452 this.element.addEventListener("mousedown", consumeEvent, false);
453 this.element.addEventListener("dblclick", consumeEvent, false);
456 WebInspector.ColorSwatch.prototype = {
458 * @param {string} colorString
460 setColorString: function(colorString)
462 this._swatchInnerElement.style.backgroundColor = colorString;