Devtools: Add force element state menu to the elements toolbar
[chromium-blink-merge.git] / third_party / WebKit / Source / devtools / front_end / elements / Spectrum.js
blob03a2ab2112190cd1fc1aec4256441be08c6193ea
1 /*
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
6 * are met:
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.
29 /**
30 * @constructor
31 * @extends {WebInspector.VBox}
33 WebInspector.Spectrum = function()
35 /**
36 * @param {!Element} parentElement
38 function appendSwitcherIcon(parentElement)
40 var icon = parentElement.createSVGChild("svg");
41 icon.setAttribute("height", 16);
42 icon.setAttribute("width", 16);
43 var path = icon.createSVGChild("path");
44 path.setAttribute("d", "M5,6 L11,6 L8,2 Z M5,10 L11,10 L8,14 Z");
45 return icon;
48 WebInspector.VBox.call(this, true);
49 this.registerRequiredCSS("elements/spectrum.css");
50 this.contentElement.tabIndex = 0;
52 this._colorElement = this.contentElement.createChild("div", "spectrum-color");
53 this._colorDragElement = this._colorElement.createChild("div", "spectrum-sat fill").createChild("div", "spectrum-val fill").createChild("div", "spectrum-dragger");
54 var contrastRatioSVG = this._colorElement.createSVGChild("svg", "spectrum-contrast-container fill");
55 this._contrastRatioLine = contrastRatioSVG.createSVGChild("path", "spectrum-contrast-line");
57 var toolbar = new WebInspector.Toolbar(this.contentElement);
58 toolbar.element.classList.add("spectrum-eye-dropper");
59 this._colorPickerButton = new WebInspector.ToolbarButton(WebInspector.UIString("Toggle color picker"), "eyedropper-toolbar-item");
60 this._colorPickerButton.setToggled(true);
61 this._colorPickerButton.addEventListener("click", this._toggleColorPicker.bind(this, undefined));
62 toolbar.appendToolbarItem(this._colorPickerButton);
64 var swatchElement = this.contentElement.createChild("span", "swatch");
65 this._swatchInnerElement = swatchElement.createChild("span", "swatch-inner");
67 this._hueElement = this.contentElement.createChild("div", "spectrum-hue");
68 this._hueSlider = this._hueElement.createChild("div", "spectrum-slider");
69 this._alphaElement = this.contentElement.createChild("div", "spectrum-alpha");
70 this._alphaElementBackground = this._alphaElement.createChild("div", "spectrum-alpha-background");
71 this._alphaSlider = this._alphaElement.createChild("div", "spectrum-slider");
73 var displaySwitcher = this.contentElement.createChild("div", "spectrum-display-switcher spectrum-switcher");
74 appendSwitcherIcon(displaySwitcher);
75 displaySwitcher.addEventListener("click", this._formatViewSwitch.bind(this));
77 // RGBA/HSLA display.
78 this._displayContainer = this.contentElement.createChild("div", "spectrum-text source-code");
79 this._textValues = [];
80 for (var i = 0; i < 4; ++i) {
81 var inputValue = this._displayContainer.createChild("input", "spectrum-text-value");
82 inputValue.maxLength = 4;
83 this._textValues.push(inputValue);
84 inputValue.addEventListener("keydown", this._inputChanged.bind(this), false);
85 inputValue.addEventListener("input", this._inputChanged.bind(this), false);
86 inputValue.addEventListener("mousewheel", this._inputChanged.bind(this), false);
89 this._textLabels = this._displayContainer.createChild("div", "spectrum-text-label");
91 // HEX display.
92 this._hexContainer = this.contentElement.createChild("div", "spectrum-text spectrum-text-hex source-code");
93 this._hexValue = this._hexContainer.createChild("input", "spectrum-text-value");
94 this._hexValue.maxLength = 7;
95 this._hexValue.addEventListener("keydown", this._inputChanged.bind(this), false);
96 this._hexValue.addEventListener("mousewheel", this._inputChanged.bind(this), false);
98 var label = this._hexContainer.createChild("div", "spectrum-text-label");
99 label.textContent = "HEX";
101 WebInspector.installDragHandle(this._hueElement, dragStart.bind(this, positionHue.bind(this)), positionHue.bind(this), null, "default");
102 WebInspector.installDragHandle(this._alphaElement, dragStart.bind(this, positionAlpha.bind(this)), positionAlpha.bind(this), null, "default");
103 WebInspector.installDragHandle(this._colorElement, dragStart.bind(this, positionColor.bind(this)), positionColor.bind(this), null, "default");
105 if (Runtime.experiments.isEnabled("colorPalettes")) {
106 this.element.classList.add("palettes-enabled");
107 /** @type {!Array.<!WebInspector.Spectrum.Palette>} */
108 this._palettes = [];
109 this._palettePanel = this._createPalettePanel();
110 this._palettePanelShowing = false;
111 this._paletteContainer = this.contentElement.createChild("div", "spectrum-palette");
112 var paletteSwitcher = this.contentElement.createChild("div", "spectrum-palette-switcher spectrum-switcher");
113 appendSwitcherIcon(paletteSwitcher);
114 paletteSwitcher.addEventListener("click", this._togglePalettePanel.bind(this, true));
115 new WebInspector.Spectrum.PaletteGenerator(this._generatedPaletteLoaded.bind(this));
119 * @param {function(!Event)} callback
120 * @param {!Event} event
121 * @return {boolean}
122 * @this {WebInspector.Spectrum}
124 function dragStart(callback, event)
126 this._hueAlphaLeft = this._hueElement.totalOffsetLeft();
127 this._colorOffset = this._colorElement.totalOffset();
128 callback(event);
129 return true;
133 * @param {!Event} event
134 * @this {WebInspector.Spectrum}
136 function positionHue(event)
138 var hsva = this._hsv.slice();
139 hsva[0] = Number.constrain(1 - (event.x - this._hueAlphaLeft) / this._hueAlphaWidth, 0, 1);
140 this._innerSetColor(hsva, "", undefined, WebInspector.Spectrum._ChangeSource.Other);
144 * @param {!Event} event
145 * @this {WebInspector.Spectrum}
147 function positionAlpha(event)
149 var newAlpha = Math.round((event.x - this._hueAlphaLeft) / this._hueAlphaWidth * 100) / 100;
150 var hsva = this._hsv.slice();
151 hsva[3] = Number.constrain(newAlpha, 0, 1);
152 var colorFormat = undefined;
153 if (this._color().hasAlpha() && (this._colorFormat === WebInspector.Color.Format.ShortHEX || this._colorFormat === WebInspector.Color.Format.HEX || this._colorFormat === WebInspector.Color.Format.Nickname))
154 colorFormat = WebInspector.Color.Format.RGB;
155 this._innerSetColor(hsva, "", colorFormat, WebInspector.Spectrum._ChangeSource.Other);
159 * @param {!Event} event
160 * @this {WebInspector.Spectrum}
162 function positionColor(event)
164 var hsva = this._hsv.slice();
165 hsva[1] = Number.constrain((event.x - this._colorOffset.left) / this.dragWidth, 0, 1);
166 hsva[2] = Number.constrain(1 - (event.y - this._colorOffset.top) / this.dragHeight, 0, 1);
167 this._innerSetColor(hsva, "", undefined, WebInspector.Spectrum._ChangeSource.Other);
171 WebInspector.Spectrum._ChangeSource = {
172 Input: "Input",
173 Model: "Model",
174 Other: "Other"
177 WebInspector.Spectrum.Events = {
178 ColorChanged: "ColorChanged",
179 SizeChanged: "SizeChanged"
182 WebInspector.Spectrum.prototype = {
184 * @return {!Element}
186 _createPalettePanel: function()
188 var panel = this.contentElement.createChild("div", "palette-panel");
189 var title = panel.createChild("div", "palette-title");
190 title.textContent = WebInspector.UIString("Color Palettes");
191 var toolbar = new WebInspector.Toolbar(panel);
192 var closeButton = new WebInspector.ToolbarButton("Return to color picker", "delete-toolbar-item");
193 closeButton.addEventListener("click", this._togglePalettePanel.bind(this, false));
194 toolbar.appendToolbarItem(closeButton);
195 return panel;
199 * @param {boolean} show
201 _togglePalettePanel: function(show)
203 if (this._palettePanelShowing === show)
204 return;
205 this._palettePanelShowing = show;
206 this._palettePanel.classList.toggle("palette-panel-showing", show);
210 * @param {string} colorText
211 * @param {number=} animationDelay
212 * @return {!Element}
214 _createPaletteColor: function(colorText, animationDelay)
216 var element = createElementWithClass("div", "spectrum-palette-color");
217 element.style.background = String.sprintf("linear-gradient(%s, %s), url(Images/checker.png)", colorText, colorText);
218 if (animationDelay)
219 element.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 100, delay: animationDelay, fill: "backwards" });
220 WebInspector.Tooltip.install(element, colorText);
221 return element;
225 * @param {!WebInspector.Spectrum.Palette} palette
226 * @param {!Event=} event
228 _showPalette: function(palette, event)
230 this._paletteContainer.removeChildren();
231 for (var i = 0; i < palette.colors.length; i++) {
232 var colorElement = this._createPaletteColor(palette.colors[i], i * 100 / palette.colors.length);
233 colorElement.addEventListener("click", this._paletteColorSelected.bind(this, palette.colors[i]));
234 colorElement.addEventListener("mouseover", this._liveApplyStart.bind(this, palette.colors[i]));
235 colorElement.addEventListener("mouseout", this._liveApplyEnd.bind(this));
236 this._paletteContainer.appendChild(colorElement);
238 this._togglePalettePanel(false);
240 var rowsNeeded = Math.max(1, Math.ceil(palette.colors.length / 8));
241 var paletteColorHeight = 12;
242 var paletteMargin = 12;
243 this.element.style.height = (this._paletteContainer.offsetTop + paletteMargin + (paletteColorHeight + paletteMargin) * rowsNeeded) + "px";
244 this.dispatchEventToListeners(WebInspector.Spectrum.Events.SizeChanged);
248 * @param {string} colorText
250 _liveApplyStart: function(colorText)
252 this._underlyingHSV = this._hsv;
253 this._underlyingFormat = this._colorFormat;
254 this._underlyingColorString = this._colorString;
255 var color = WebInspector.Color.parse(colorText);
256 if (!color)
257 return;
258 this._innerSetColor(color.hsva(), colorText, color.format(), WebInspector.Spectrum._ChangeSource.Other);
261 _liveApplyEnd: function()
263 if (!this._underlyingHSV)
264 return;
265 this._innerSetColor(this._underlyingHSV, this._underlyingColorString, this._underlyingFormat, WebInspector.Spectrum._ChangeSource.Other);
266 delete this._underlyingHSV;
267 delete this._underlyingFormat;
268 delete this._underlyingColorString;
272 * @param {!WebInspector.Spectrum.Palette} generatedPalette
274 _generatedPaletteLoaded: function(generatedPalette)
276 this._palettes.push(generatedPalette);
277 this._palettes.push(WebInspector.Spectrum.MaterialPalette);
278 // TODO(samli): Load custom palettes.
279 for (var palette of this._palettes)
280 this._palettePanel.appendChild(this._createPreviewPaletteElement(palette));
281 this._showPalette(this._palettes[0].colors.length ? this._palettes[0] : this._palettes[1]);
285 * @param {!WebInspector.Spectrum.Palette} palette
286 * @return {!Element}
288 _createPreviewPaletteElement: function(palette)
290 var colorsPerPreviewRow = 6;
291 var previewElement = createElementWithClass("div", "palette-preview");
292 var titleElement = previewElement.createChild("div", "palette-preview-title");
293 titleElement.textContent = palette.title;
294 for (var i = 0; i < colorsPerPreviewRow && i < palette.colors.length; i++)
295 previewElement.appendChild(this._createPaletteColor(palette.colors[i]));
296 previewElement.addEventListener("click", this._showPalette.bind(this, palette));
297 return previewElement;
301 * @param {string} colorText
303 _paletteColorSelected: function(colorText)
305 var color = WebInspector.Color.parse(colorText);
306 if (!color)
307 return;
308 this._innerSetColor(color.hsva(), colorText, color.format(), WebInspector.Spectrum._ChangeSource.Other);
309 delete this._underlyingHSV;
313 * @param {!WebInspector.Color} color
314 * @param {string} colorFormat
316 setColor: function(color, colorFormat)
318 this._originalFormat = colorFormat;
319 this._innerSetColor(color.hsva(), "", colorFormat, WebInspector.Spectrum._ChangeSource.Model);
323 * @param {!Array<number>|undefined} hsva
324 * @param {string|undefined} colorString
325 * @param {string|undefined} colorFormat
326 * @param {string} changeSource
328 _innerSetColor: function(hsva, colorString, colorFormat, changeSource)
330 if (hsva !== undefined)
331 this._hsv = hsva;
332 if (colorString !== undefined)
333 this._colorString = colorString;
334 if (colorFormat !== undefined) {
335 console.assert(colorFormat !== WebInspector.Color.Format.Original, "Spectrum's color format cannot be Original");
336 if (colorFormat === WebInspector.Color.Format.RGBA)
337 colorFormat = WebInspector.Color.Format.RGB;
338 else if (colorFormat === WebInspector.Color.Format.HSLA)
339 colorFormat = WebInspector.Color.Format.HSL;
340 this._colorFormat = colorFormat;
343 this._updateHelperLocations();
344 this._updateUI();
346 if (changeSource !== WebInspector.Spectrum._ChangeSource.Input)
347 this._updateInput();
348 if (changeSource !== WebInspector.Spectrum._ChangeSource.Model)
349 this.dispatchEventToListeners(WebInspector.Spectrum.Events.ColorChanged, this.colorString());
353 * @param {!WebInspector.Color} color
355 setContrastColor: function(color)
357 this._contrastColor = color;
358 this._updateUI();
362 * @return {!WebInspector.Color}
364 _color: function()
366 return WebInspector.Color.fromHSVA(this._hsv);
370 * @return {string}
372 colorString: function()
374 if (this._colorString)
375 return this._colorString;
376 var cf = WebInspector.Color.Format;
377 var color = this._color();
378 var colorString = color.asString(this._colorFormat);
379 if (colorString)
380 return colorString;
382 if (this._colorFormat === cf.Nickname || this._colorFormat === cf.ShortHEX) {
383 colorString = color.asString(cf.HEX);
384 if (colorString)
385 return colorString;
388 console.assert(color.hasAlpha());
389 return this._colorFormat === cf.HSL ? /** @type {string} */(color.asString(cf.HSLA)) : /** @type {string} */(color.asString(cf.RGBA));
392 _updateHelperLocations: function()
394 var h = this._hsv[0];
395 var s = this._hsv[1];
396 var v = this._hsv[2];
397 var alpha = this._hsv[3];
399 // Where to show the little circle that displays your current selected color.
400 var dragX = s * this.dragWidth;
401 var dragY = this.dragHeight - (v * this.dragHeight);
403 dragX = Math.max(-this._colorDragElementHeight,
404 Math.min(this.dragWidth - this._colorDragElementHeight, dragX - this._colorDragElementHeight));
405 dragY = Math.max(-this._colorDragElementHeight,
406 Math.min(this.dragHeight - this._colorDragElementHeight, dragY - this._colorDragElementHeight));
408 this._colorDragElement.positionAt(dragX, dragY);
410 // Where to show the bar that displays your current selected hue.
411 var hueSlideX = (1 - h) * this._hueAlphaWidth - this.slideHelperWidth;
412 this._hueSlider.style.left = hueSlideX + "px";
413 var alphaSlideX = alpha * this._hueAlphaWidth - this.slideHelperWidth;
414 this._alphaSlider.style.left = alphaSlideX + "px";
417 _updateInput: function()
419 var cf = WebInspector.Color.Format;
420 if (this._colorFormat === cf.HEX || this._colorFormat === cf.ShortHEX || this._colorFormat === cf.Nickname) {
421 this._hexContainer.hidden = false;
422 this._displayContainer.hidden = true;
423 if (this._colorFormat === cf.ShortHEX && this._color().canBeShortHex())
424 this._hexValue.value = this._color().asString(cf.ShortHEX);
425 else
426 this._hexValue.value = this._color().asString(cf.HEX);
427 } else {
428 // RGBA, HSLA display.
429 this._hexContainer.hidden = true;
430 this._displayContainer.hidden = false;
431 var isRgb = this._colorFormat === cf.RGB;
432 this._textLabels.textContent = isRgb ? "RGBA" : "HSLA";
433 var colorValues = isRgb ? this._color().canonicalRGBA() : this._color().canonicalHSLA();
434 for (var i = 0; i < 3; ++i) {
435 this._textValues[i].value = colorValues[i];
436 if (!isRgb && (i === 1 || i === 2))
437 this._textValues[i].value += "%";
439 this._textValues[3].value= Math.round(colorValues[3] * 100) / 100;
444 * @param {number} requiredContrast
446 _drawContrastRatioLine: function(requiredContrast)
448 if (!this._contrastColor || !this.dragWidth || !this.dragHeight)
449 return;
451 /** const */ var width = this.dragWidth;
452 /** const */ var height = this.dragHeight;
453 /** const */ var dS = 0.02;
454 /** const */ var epsilon = 0.01;
456 var fgRGBA = [];
457 WebInspector.Color.hsva2rgba(this._hsv, fgRGBA);
458 var fgLuminance = WebInspector.Color.luminance(fgRGBA);
459 var bgRGBA = this._contrastColor.rgba();
460 var bgLuminance = WebInspector.Color.luminance(bgRGBA);
461 var delta = fgLuminance < bgLuminance ? 1 : -1;
463 var lastV = this._hsv[2];
464 var currentSlope = 0;
465 var candidateHSVA = [this._hsv[0], 0, 0, this._hsv[3]];
466 var pathBuilder = [];
467 var candidateRGBA = [];
468 WebInspector.Color.hsva2rgba(candidateHSVA, candidateRGBA);
469 var flattenedRGBA = [];
470 WebInspector.Color.flattenColors(candidateRGBA, bgRGBA, flattenedRGBA);
473 * Approach the desired contrast ratio by modifying the given component
474 * from the given starting value.
475 * @param {number} index
476 * @param {number} x
477 * @return {?number}
479 function approach(index, x)
481 while (0 <= x && x <= 1) {
482 candidateHSVA[index] = x;
483 WebInspector.Color.hsva2rgba(candidateHSVA, candidateRGBA);
484 WebInspector.Color.flattenColors(candidateRGBA, bgRGBA, flattenedRGBA);
485 var contrast = WebInspector.Color.calculateContrastRatio(flattenedRGBA, bgRGBA);
486 var dContrast = contrast - requiredContrast;
487 if (Math.abs(dContrast) < epsilon) {
488 return x;
489 } else {
490 // 21 is the maximum possible value for contrast ratio:
491 // http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html#contrast-ratiodef
492 x += delta * (dContrast / 21);
495 return null;
498 for (var s = 0; s < 1 + dS; s += dS) {
499 s = Math.min(1, s);
500 candidateHSVA[1] = s;
501 var v = lastV;
502 v = lastV + currentSlope * dS;
504 v = approach(2, v);
505 if (v === null)
506 break;
508 currentSlope = (v - lastV) / dS;
510 pathBuilder.push(pathBuilder.length ? "L" : "M");
511 pathBuilder.push(s * width);
512 pathBuilder.push((1 - v) * height);
515 if (s < 1 + dS) {
516 s -= dS;
517 delta = -delta;
518 candidateHSVA[2] = 1;
519 s = approach(1, s);
520 if (s !== null)
521 pathBuilder = pathBuilder.concat(["L", s * width, 0])
524 this._contrastRatioLine.setAttribute("d", pathBuilder.join(" "));
527 _updateUI: function()
529 var h = WebInspector.Color.fromHSVA([this._hsv[0], 1, 1, 1]);
530 this._colorElement.style.backgroundColor = /** @type {string} */ (h.asString(WebInspector.Color.Format.RGB));
531 if (Runtime.experiments.isEnabled("colorContrastRatio")) {
532 // TODO(samli): Determine size of text and switch between AA/AAA ratings.
533 this._drawContrastRatioLine(4.5);
535 this._swatchInnerElement.style.backgroundColor = /** @type {string} */ (this._color().asString(WebInspector.Color.Format.RGBA));
536 // Show border if the swatch is white.
537 this._swatchInnerElement.classList.toggle("swatch-inner-white", this._color().hsla()[2] > 0.9);
538 this._colorDragElement.style.backgroundColor = /** @type {string} */ (this._color().asString(WebInspector.Color.Format.RGBA));
539 var noAlpha = WebInspector.Color.fromHSVA(this._hsv.slice(0,3).concat(1));
540 this._alphaElementBackground.style.backgroundImage = String.sprintf("linear-gradient(to right, rgba(0,0,0,0), %s)", noAlpha.asString(WebInspector.Color.Format.RGB));
543 _formatViewSwitch: function()
545 var cf = WebInspector.Color.Format;
546 var format = cf.RGB;
547 if (this._colorFormat === cf.RGB)
548 format = cf.HSL;
549 else if (this._colorFormat === cf.HSL && !this._color().hasAlpha())
550 format = this._originalFormat === cf.ShortHEX ? cf.ShortHEX : cf.HEX;
551 this._innerSetColor(undefined, "", format, WebInspector.Spectrum._ChangeSource.Other);
555 * @param {!Event} event
557 _inputChanged: function(event)
560 * @param {!Element} element
561 * @return {string}
563 function elementValue(element)
565 return element.value;
568 var inputElement = /** @type {!Element} */(event.currentTarget);
569 var arrowKeyOrMouseWheelEvent = (event.keyIdentifier === "Up" || event.keyIdentifier === "Down" || event.type === "mousewheel");
570 var pageKeyPressed = (event.keyIdentifier === "PageUp" || event.keyIdentifier === "PageDown");
571 if (arrowKeyOrMouseWheelEvent || pageKeyPressed) {
572 var newValue = WebInspector.createReplacementString(inputElement.value, event);
573 if (newValue) {
574 inputElement.value = newValue;
575 inputElement.selectionStart = 0;
576 inputElement.selectionEnd = newValue.length;
578 event.consume(true);
581 const cf = WebInspector.Color.Format;
582 var colorString;
583 if (this._colorFormat === cf.HEX || this._colorFormat === cf.ShortHEX) {
584 colorString = this._hexValue.value;
585 } else {
586 var format = this._colorFormat === cf.RGB ? "rgba" : "hsla";
587 var values = this._textValues.map(elementValue).join(",");
588 colorString = String.sprintf("%s(%s)", format, values);
591 var color = WebInspector.Color.parse(colorString);
592 if (!color)
593 return;
594 var hsv = color.hsva();
595 if (this._colorFormat === cf.HEX || this._colorFormat === cf.ShortHEX)
596 this._colorFormat = color.canBeShortHex() ? cf.ShortHEX : cf.HEX;
597 this._innerSetColor(hsv, colorString, undefined, WebInspector.Spectrum._ChangeSource.Input);
600 wasShown: function()
602 this._hueAlphaWidth = this._hueElement.offsetWidth;
603 this.slideHelperWidth = this._hueSlider.offsetWidth / 2;
604 this.dragWidth = this._colorElement.offsetWidth;
605 this.dragHeight = this._colorElement.offsetHeight;
606 this._colorDragElementHeight = this._colorDragElement.offsetHeight / 2;
607 this._innerSetColor(undefined, undefined, undefined, WebInspector.Spectrum._ChangeSource.Model);
608 this._toggleColorPicker(true);
609 WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.ColorPicked, this._colorPicked, this);
612 willHide: function()
614 this._toggleColorPicker(false);
615 WebInspector.targetManager.removeModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.ColorPicked, this._colorPicked, this);
619 * @param {boolean=} enabled
620 * @param {!WebInspector.Event=} event
622 _toggleColorPicker: function(enabled, event)
624 if (enabled === undefined)
625 enabled = !this._colorPickerButton.toggled();
626 this._colorPickerButton.setToggled(enabled);
627 for (var target of WebInspector.targetManager.targets())
628 target.pageAgent().setColorPickerEnabled(enabled);
632 * @param {!WebInspector.Event} event
634 _colorPicked: function(event)
636 var rgbColor = /** @type {!DOMAgent.RGBA} */ (event.data);
637 var rgba = [rgbColor.r, rgbColor.g, rgbColor.b, (rgbColor.a / 2.55 | 0) / 100];
638 var color = WebInspector.Color.fromRGBA(rgba);
639 this._innerSetColor(color.hsva(), "", undefined, WebInspector.Spectrum._ChangeSource.Other);
640 InspectorFrontendHost.bringToFront();
644 __proto__: WebInspector.VBox.prototype
647 /** @typedef {{ title: string, colors: !Array.<string> }} */
648 WebInspector.Spectrum.Palette;
651 * @constructor
652 * @param {function(!WebInspector.Spectrum.Palette)} callback
654 WebInspector.Spectrum.PaletteGenerator = function(callback)
656 this._callback = callback;
657 var target = WebInspector.targetManager.mainTarget();
658 if (!target)
659 return;
660 var cssModel = WebInspector.CSSStyleModel.fromTarget(target);
661 /** @type {!Map.<string, number>} */
662 this._frequencyMap = new Map();
663 var stylesheetPromises = [];
664 for (var stylesheet of cssModel.allStyleSheets())
665 stylesheetPromises.push(new Promise(this._processStylesheet.bind(this, stylesheet)));
666 Promise.all(stylesheetPromises)
667 .catchException(null)
668 .then(this._finish.bind(this));
671 WebInspector.Spectrum.PaletteGenerator.prototype = {
673 * @param {string} a
674 * @param {string} b
675 * @return {number}
677 _frequencyComparator: function(a, b)
679 return this._frequencyMap.get(b) - this._frequencyMap.get(a);
682 _finish: function()
684 var colors = this._frequencyMap.keysArray();
685 colors = colors.sort(this._frequencyComparator.bind(this));
686 var paletteColors = [];
687 var colorsPerRow = 8;
688 while (paletteColors.length < colorsPerRow && colors.length) {
689 var colorText = colors.shift();
690 var color = WebInspector.Color.parse(colorText);
691 if (!color || color.nickname() === "white" || color.nickname() === "black")
692 continue;
693 paletteColors.push(colorText);
695 this._callback({ title: "Page colors", colors: paletteColors });
699 * @param {!WebInspector.CSSStyleSheetHeader} stylesheet
700 * @param {function(?)} resolve
701 * @this {WebInspector.Spectrum.PaletteGenerator}
703 _processStylesheet: function(stylesheet, resolve)
706 * @param {?string} text
707 * @this {WebInspector.Spectrum.PaletteGenerator}
709 function parseContent(text)
711 var regexResult = text.match(/((?:rgb|hsl)a?\([^)]+\)|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3})/g) || [];
712 for (var c of regexResult) {
713 var frequency = this._frequencyMap.get(c) || 0;
714 this._frequencyMap.set(c, ++frequency);
716 resolve(null);
719 stylesheet.requestContent(parseContent.bind(this));
723 WebInspector.Spectrum.MaterialPalette = { title: "Material", colors: [
724 "#F44336",
725 "#E91E63",
726 "#9C27B0",
727 "#673AB7",
728 "#3F51B5",
729 "#2196F3",
730 "#03A9F4",
731 "#00BCD4",
732 "#009688",
733 "#4CAF50",
734 "#8BC34A",
735 "#CDDC39",
736 "#FFEB3B",
737 "#FFC107",
738 "#FF9800",
739 "#FF5722",
740 "#795548",
741 "#9E9E9E",
742 "#607D8B"