Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(129)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/elements/Spectrum.js

Issue 2142133002: DevTools: Move Spectrum to components/ (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 /**
36 * @param {!Element} parentElement
37 */
38 function appendSwitcherIcon(parentElement)
39 {
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;
46 }
47
48 WebInspector.VBox.call(this, true);
49 this.registerRequiredCSS("elements/spectrum.css");
50 this.contentElement.tabIndex = 0;
51
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-dra gger");
54 var contrastRatioSVG = this._colorElement.createSVGChild("svg", "spectrum-co ntrast-container fill");
55 this._contrastRatioLine = contrastRatioSVG.createSVGChild("path", "spectrum- contrast-line");
56
57 var toolbar = new WebInspector.Toolbar("spectrum-eye-dropper", this.contentE lement);
58 this._colorPickerButton = new WebInspector.ToolbarToggle(WebInspector.UIStri ng("Toggle color picker"), "eyedropper-toolbar-item");
59 this._colorPickerButton.setToggled(true);
60 this._colorPickerButton.addEventListener("click", this._toggleColorPicker.bi nd(this, undefined));
61 toolbar.appendToolbarItem(this._colorPickerButton);
62
63 var swatchElement = this.contentElement.createChild("span", "swatch");
64 this._swatchInnerElement = swatchElement.createChild("span", "swatch-inner") ;
65
66 this._hueElement = this.contentElement.createChild("div", "spectrum-hue");
67 this._hueSlider = this._hueElement.createChild("div", "spectrum-slider");
68 this._alphaElement = this.contentElement.createChild("div", "spectrum-alpha" );
69 this._alphaElementBackground = this._alphaElement.createChild("div", "spectr um-alpha-background");
70 this._alphaSlider = this._alphaElement.createChild("div", "spectrum-slider") ;
71
72 var displaySwitcher = this.contentElement.createChild("div", "spectrum-displ ay-switcher spectrum-switcher");
73 appendSwitcherIcon(displaySwitcher);
74 displaySwitcher.addEventListener("click", this._formatViewSwitch.bind(this)) ;
75
76 // RGBA/HSLA display.
77 this._displayContainer = this.contentElement.createChild("div", "spectrum-te xt source-code");
78 this._textValues = [];
79 for (var i = 0; i < 4; ++i) {
80 var inputValue = this._displayContainer.createChild("input", "spectrum-t ext-value");
81 inputValue.maxLength = 4;
82 this._textValues.push(inputValue);
83 inputValue.addEventListener("keydown", this._inputChanged.bind(this), fa lse);
84 inputValue.addEventListener("input", this._inputChanged.bind(this), fals e);
85 inputValue.addEventListener("mousewheel", this._inputChanged.bind(this), false);
86 }
87
88 this._textLabels = this._displayContainer.createChild("div", "spectrum-text- label");
89
90 // HEX display.
91 this._hexContainer = this.contentElement.createChild("div", "spectrum-text s pectrum-text-hex source-code");
92 this._hexValue = this._hexContainer.createChild("input", "spectrum-text-valu e");
93 this._hexValue.maxLength = 9;
94 this._hexValue.addEventListener("keydown", this._inputChanged.bind(this), fa lse);
95 this._hexValue.addEventListener("input", this._inputChanged.bind(this), fals e);
96 this._hexValue.addEventListener("mousewheel", this._inputChanged.bind(this), false);
97
98 var label = this._hexContainer.createChild("div", "spectrum-text-label");
99 label.textContent = "HEX";
100
101 WebInspector.installDragHandle(this._hueElement, dragStart.bind(this, positi onHue.bind(this)), positionHue.bind(this), null, "default");
102 WebInspector.installDragHandle(this._alphaElement, dragStart.bind(this, posi tionAlpha.bind(this)), positionAlpha.bind(this), null, "default");
103 WebInspector.installDragHandle(this._colorElement, dragStart.bind(this, posi tionColor.bind(this)), positionColor.bind(this), null, "default");
104
105 this.element.classList.add("palettes-enabled");
106 /** @type {!Map.<string, !WebInspector.Spectrum.Palette>} */
107 this._palettes = new Map();
108 this._palettePanel = this.contentElement.createChild("div", "palette-panel") ;
109 this._palettePanelShowing = false;
110 this._paletteContainer = this.contentElement.createChild("div", "spectrum-pa lette");
111 this._paletteContainer.addEventListener("contextmenu", this._showPaletteColo rContextMenu.bind(this, -1));
112 this._shadesContainer = this.contentElement.createChild("div", "palette-colo r-shades hidden");
113 WebInspector.installDragHandle(this._paletteContainer, this._paletteDragStar t.bind(this), this._paletteDrag.bind(this), this._paletteDragEnd.bind(this), "de fault");
114 var paletteSwitcher = this.contentElement.createChild("div", "spectrum-palet te-switcher spectrum-switcher");
115 appendSwitcherIcon(paletteSwitcher);
116 paletteSwitcher.addEventListener("click", this._togglePalettePanel.bind(this , true));
117
118 this._deleteIconToolbar = new WebInspector.Toolbar("delete-color-toolbar");
119 this._deleteButton = new WebInspector.ToolbarButton("", "garbage-collect-too lbar-item");
120 this._deleteIconToolbar.appendToolbarItem(this._deleteButton);
121
122 var overlay = this.contentElement.createChild("div", "spectrum-overlay fill" );
123 overlay.addEventListener("click", this._togglePalettePanel.bind(this, false) );
124
125 this._addColorToolbar = new WebInspector.Toolbar("add-color-toolbar");
126 var addColorButton = new WebInspector.ToolbarButton(WebInspector.UIString("A dd to palette"), "add-toolbar-item");
127 addColorButton.addEventListener("click", this._addColorToCustomPalette.bind( this));
128 this._addColorToolbar.appendToolbarItem(addColorButton);
129
130 this._loadPalettes();
131 new WebInspector.Spectrum.PaletteGenerator(this._generatedPaletteLoaded.bind (this));
132
133 /**
134 * @param {function(!Event)} callback
135 * @param {!Event} event
136 * @return {boolean}
137 * @this {WebInspector.Spectrum}
138 */
139 function dragStart(callback, event)
140 {
141 this._hueAlphaLeft = this._hueElement.totalOffsetLeft();
142 this._colorOffset = this._colorElement.totalOffset();
143 callback(event);
144 return true;
145 }
146
147 /**
148 * @param {!Event} event
149 * @this {WebInspector.Spectrum}
150 */
151 function positionHue(event)
152 {
153 var hsva = this._hsv.slice();
154 hsva[0] = Number.constrain(1 - (event.x - this._hueAlphaLeft) / this._hu eAlphaWidth, 0, 1);
155 this._innerSetColor(hsva, "", undefined, WebInspector.Spectrum._ChangeS ource.Other);
156 }
157
158 /**
159 * @param {!Event} event
160 * @this {WebInspector.Spectrum}
161 */
162 function positionAlpha(event)
163 {
164 var newAlpha = Math.round((event.x - this._hueAlphaLeft) / this._hueAlph aWidth * 100) / 100;
165 var hsva = this._hsv.slice();
166 hsva[3] = Number.constrain(newAlpha, 0, 1);
167 var colorFormat = undefined;
168 if (hsva[3] !== 1 && this._colorFormat === WebInspector.Color.Format.Nic kname)
169 colorFormat = WebInspector.Color.Format.HEX;
170 this._innerSetColor(hsva, "", colorFormat, WebInspector.Spectrum._Change Source.Other);
171 }
172
173 /**
174 * @param {!Event} event
175 * @this {WebInspector.Spectrum}
176 */
177 function positionColor(event)
178 {
179 var hsva = this._hsv.slice();
180 hsva[1] = Number.constrain((event.x - this._colorOffset.left) / this.dra gWidth, 0, 1);
181 hsva[2] = Number.constrain(1 - (event.y - this._colorOffset.top) / this. dragHeight, 0, 1);
182 this._innerSetColor(hsva, "", undefined, WebInspector.Spectrum._ChangeS ource.Other);
183 }
184 }
185
186 WebInspector.Spectrum._ChangeSource = {
187 Input: "Input",
188 Model: "Model",
189 Other: "Other"
190 }
191
192 WebInspector.Spectrum.Events = {
193 ColorChanged: "ColorChanged",
194 SizeChanged: "SizeChanged"
195 };
196
197 WebInspector.Spectrum._colorChipSize = 24;
198 WebInspector.Spectrum._itemsPerPaletteRow = 8;
199
200 WebInspector.Spectrum.prototype = {
201 _updatePalettePanel: function()
202 {
203 this._palettePanel.removeChildren();
204 var title = this._palettePanel.createChild("div", "palette-title");
205 title.textContent = WebInspector.UIString("Color Palettes");
206 var toolbar = new WebInspector.Toolbar("", this._palettePanel);
207 var closeButton = new WebInspector.ToolbarButton("Return to color picker ", "delete-toolbar-item");
208 closeButton.addEventListener("click", this._togglePalettePanel.bind(this , false));
209 toolbar.appendToolbarItem(closeButton);
210 for (var palette of this._palettes.values())
211 this._palettePanel.appendChild(this._createPreviewPaletteElement(pal ette));
212 },
213
214 /**
215 * @param {boolean} show
216 */
217 _togglePalettePanel: function(show)
218 {
219 if (this._palettePanelShowing === show)
220 return;
221 if (show)
222 this._updatePalettePanel();
223 this._focus();
224 this._palettePanelShowing = show;
225 this.contentElement.classList.toggle("palette-panel-showing", show);
226 },
227
228 _focus: function()
229 {
230 if (this.isShowing() && WebInspector.currentFocusElement() !== this.cont entElement)
231 WebInspector.setCurrentFocusElement(this.contentElement);
232 },
233
234 /**
235 * @param {string} colorText
236 * @param {number=} animationDelay
237 * @return {!Element}
238 */
239 _createPaletteColor: function(colorText, animationDelay)
240 {
241 var element = createElementWithClass("div", "spectrum-palette-color");
242 element.style.background = String.sprintf("linear-gradient(%s, %s), url( Images/checker.png)", colorText, colorText);
243 if (animationDelay)
244 element.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 100, d elay: animationDelay, fill: "backwards" });
245 element.title = colorText;
246 return element;
247 },
248
249 /**
250 * @param {!WebInspector.Spectrum.Palette} palette
251 * @param {boolean} animate
252 * @param {!Event=} event
253 */
254 _showPalette: function(palette, animate, event)
255 {
256 this._resizeForSelectedPalette();
257 this._paletteContainer.removeChildren();
258 for (var i = 0; i < palette.colors.length; i++) {
259 var animationDelay = animate ? i * 100 / palette.colors.length : 0;
260 var colorElement = this._createPaletteColor(palette.colors[i], anima tionDelay);
261 colorElement.addEventListener("mousedown", this._paletteColorSelecte d.bind(this, palette.colors[i], palette.matchUserFormat));
262 if (palette.mutable) {
263 colorElement.__mutable = true;
264 colorElement.__color = palette.colors[i];
265 colorElement.addEventListener("contextmenu", this._showPaletteCo lorContextMenu.bind(this, i));
266 } else if (palette === WebInspector.Spectrum.MaterialPalette) {
267 colorElement.classList.add("has-material-shades");
268 var shadow = colorElement.createChild("div", "spectrum-palette-c olor spectrum-palette-color-shadow");
269 shadow.style.background = palette.colors[i];
270 shadow = colorElement.createChild("div", "spectrum-palette-color spectrum-palette-color-shadow");
271 shadow.style.background = palette.colors[i];
272 colorElement.title = WebInspector.UIString(palette.colors[i] + " . Long-click to show alternate shades.");
273 new WebInspector.LongClickController(colorElement, this._showLig htnessShades.bind(this, colorElement, palette.colors[i]));
274 }
275 this._paletteContainer.appendChild(colorElement);
276 }
277 this._paletteContainerMutable = palette.mutable;
278
279 var numItems = palette.colors.length;
280 if (palette.mutable)
281 numItems++;
282 if (palette.mutable) {
283 this._paletteContainer.appendChild(this._addColorToolbar.element);
284 this._paletteContainer.appendChild(this._deleteIconToolbar.element);
285 } else {
286 this._addColorToolbar.element.remove();
287 this._deleteIconToolbar.element.remove();
288 }
289
290 this._togglePalettePanel(false);
291 this._focus();
292 },
293
294 /**
295 * @param {!Element} colorElement
296 * @param {string} colorText
297 * @param {!Event} event
298 */
299 _showLightnessShades: function(colorElement, colorText, event)
300 {
301 /**
302 * @param {!Element} element
303 * @this {!WebInspector.Spectrum}
304 */
305 function closeLightnessShades(element)
306 {
307 this._shadesContainer.classList.add("hidden");
308 element.classList.remove("spectrum-shades-shown");
309 this._shadesContainer.ownerDocument.removeEventListener("mousedown", this._shadesCloseHandler, true);
310 delete this._shadesCloseHandler;
311 }
312
313 if (this._shadesCloseHandler)
314 this._shadesCloseHandler();
315
316 this._shadesContainer.classList.remove("hidden");
317 this._shadesContainer.removeChildren();
318 this._shadesContainer.animate([{ transform: "scaleY(0)", opacity: "0" }, { transform: "scaleY(1)", opacity: "1" }], { duration: 200, easing: "cubic-bezi er(0.4, 0, 0.2, 1)" });
319 this._shadesContainer.style.top = colorElement.offsetTop + colorElement. parentElement.offsetTop + "px";
320 this._shadesContainer.style.left = colorElement.offsetLeft + "px";
321 colorElement.classList.add("spectrum-shades-shown");
322
323 var shades = WebInspector.Spectrum.MaterialPaletteShades[colorText];
324 for (var i = shades.length - 1; i >= 0; i--) {
325 var shadeElement = this._createPaletteColor(shades[i], i * 200 / sha des.length + 100);
326 shadeElement.addEventListener("mousedown", this._paletteColorSelecte d.bind(this, shades[i], false));
327 this._shadesContainer.appendChild(shadeElement);
328 }
329
330 WebInspector.setCurrentFocusElement(this._shadesContainer);
331 this._shadesCloseHandler = closeLightnessShades.bind(this, colorElement) ;
332 this._shadesContainer.ownerDocument.addEventListener("mousedown", this._ shadesCloseHandler, true);
333 },
334
335 /**
336 * @param {!Event} e
337 * @return {number}
338 */
339 _slotIndexForEvent: function(e)
340 {
341 var localX = e.pageX - this._paletteContainer.totalOffsetLeft();
342 var localY = e.pageY - this._paletteContainer.totalOffsetTop();
343 var col = Math.min(localX / WebInspector.Spectrum._colorChipSize | 0, We bInspector.Spectrum._itemsPerPaletteRow - 1);
344 var row = (localY / WebInspector.Spectrum._colorChipSize) | 0;
345 return Math.min(row * WebInspector.Spectrum._itemsPerPaletteRow + col, t his._customPaletteSetting.get().colors.length - 1);
346 },
347
348 /**
349 * @param {!Event} e
350 * @return {boolean}
351 */
352 _isDraggingToBin: function(e)
353 {
354 return e.pageX > this._deleteIconToolbar.element.totalOffsetLeft();
355 },
356
357 /**
358 * @param {!Event} e
359 * @return {boolean}
360 */
361 _paletteDragStart: function(e)
362 {
363 var element = e.deepElementFromPoint();
364 if (!element || !element.__mutable)
365 return false;
366
367 var index = this._slotIndexForEvent(e);
368 this._dragElement = element;
369 this._dragHotSpotX = e.pageX - (index % WebInspector.Spectrum._itemsPerP aletteRow) * WebInspector.Spectrum._colorChipSize;
370 this._dragHotSpotY = e.pageY - (index / WebInspector.Spectrum._itemsPerP aletteRow | 0) * WebInspector.Spectrum._colorChipSize;
371 return true;
372 },
373
374 /**
375 * @param {!Event} e
376 */
377 _paletteDrag: function(e)
378 {
379 if (e.pageX < this._paletteContainer.totalOffsetLeft() || e.pageY < this ._paletteContainer.totalOffsetTop())
380 return;
381 var newIndex = this._slotIndexForEvent(e);
382 var offsetX = e.pageX - (newIndex % WebInspector.Spectrum._itemsPerPalet teRow) * WebInspector.Spectrum._colorChipSize;
383 var offsetY = e.pageY - (newIndex / WebInspector.Spectrum._itemsPerPalet teRow | 0) * WebInspector.Spectrum._colorChipSize;
384
385 var isDeleting = this._isDraggingToBin(e);
386 this._deleteIconToolbar.element.classList.add("dragging");
387 this._deleteIconToolbar.element.classList.toggle("delete-color-toolbar-a ctive", isDeleting);
388 var dragElementTransform = "translateX(" + (offsetX - this._dragHotSpotX ) + "px) translateY(" + (offsetY - this._dragHotSpotY) + "px)";
389 this._dragElement.style.transform = isDeleting ? dragElementTransform + " scale(0.8)" : dragElementTransform;
390 var children = Array.prototype.slice.call(this._paletteContainer.childre n);
391 var index = children.indexOf(this._dragElement);
392 /** @type {!Map.<!Element, {left: number, top: number}>} */
393 var swatchOffsets = new Map();
394 for (var swatch of children)
395 swatchOffsets.set(swatch, swatch.totalOffset());
396
397 if (index !== newIndex)
398 this._paletteContainer.insertBefore(this._dragElement, children[newI ndex > index ? newIndex + 1 : newIndex]);
399
400 for (var swatch of children) {
401 if (swatch === this._dragElement)
402 continue;
403 var before = swatchOffsets.get(swatch);
404 var after = swatch.totalOffset();
405 if (before.left !== after.left || before.top !== after.top) {
406 swatch.animate([
407 { transform: "translateX(" + (before.left - after.left) + "p x) translateY(" + (before.top - after.top) + "px)" },
408 { transform: "none" }], { duration: 100, easing: "cubic-bezi er(0, 0, 0.2, 1)" });
409 }
410 }
411 },
412
413 /**
414 * @param {!Event} e
415 */
416 _paletteDragEnd: function(e)
417 {
418 if (this._isDraggingToBin(e))
419 this._dragElement.remove();
420 this._dragElement.style.removeProperty("transform");
421 var children = this._paletteContainer.children;
422 var colors = [];
423 for (var i = 0; i < children.length; ++i) {
424 if (children[i].__color)
425 colors.push(children[i].__color);
426 }
427 var palette = this._customPaletteSetting.get();
428 palette.colors = colors;
429 this._customPaletteSetting.set(palette);
430 this._showPalette(this._customPaletteSetting.get(), false);
431
432 this._deleteIconToolbar.element.classList.remove("dragging");
433 this._deleteIconToolbar.element.classList.remove("delete-color-toolbar-a ctive");
434 },
435
436 _loadPalettes: function()
437 {
438 this._palettes.set(WebInspector.Spectrum.MaterialPalette.title, WebInspe ctor.Spectrum.MaterialPalette);
439 /** @type {!WebInspector.Spectrum.Palette} */
440 var defaultCustomPalette = { title: "Custom", colors: [], mutable: true };
441 this._customPaletteSetting = WebInspector.settings.createSetting("custom ColorPalette", defaultCustomPalette);
442 this._palettes.set(this._customPaletteSetting.get().title, this._customP aletteSetting.get());
443
444 this._selectedColorPalette = WebInspector.settings.createSetting("select edColorPalette", WebInspector.Spectrum.GeneratedPaletteTitle);
445 var palette = this._palettes.get(this._selectedColorPalette.get());
446 if (palette)
447 this._showPalette(palette, true);
448 },
449
450 /**
451 * @param {!WebInspector.Spectrum.Palette} generatedPalette
452 */
453 _generatedPaletteLoaded: function(generatedPalette)
454 {
455 if (generatedPalette.colors.length)
456 this._palettes.set(generatedPalette.title, generatedPalette);
457 if (this._selectedColorPalette.get() !== generatedPalette.title) {
458 return;
459 } else if (!generatedPalette.colors.length) {
460 this._paletteSelected(WebInspector.Spectrum.MaterialPalette);
461 return;
462 }
463 this._showPalette(generatedPalette, true);
464 },
465
466 /**
467 * @param {!WebInspector.Spectrum.Palette} palette
468 * @return {!Element}
469 */
470 _createPreviewPaletteElement: function(palette)
471 {
472 var colorsPerPreviewRow = 5;
473 var previewElement = createElementWithClass("div", "palette-preview");
474 var titleElement = previewElement.createChild("div", "palette-preview-ti tle");
475 titleElement.textContent = palette.title;
476 for (var i = 0; i < colorsPerPreviewRow && i < palette.colors.length; i+ +)
477 previewElement.appendChild(this._createPaletteColor(palette.colors[i ]));
478 for (; i < colorsPerPreviewRow; i++)
479 previewElement.createChild("div", "spectrum-palette-color empty-colo r");
480 previewElement.addEventListener("click", this._paletteSelected.bind(this , palette));
481 return previewElement;
482 },
483
484 /**
485 * @param {!WebInspector.Spectrum.Palette} palette
486 */
487 _paletteSelected: function(palette)
488 {
489 this._selectedColorPalette.set(palette.title);
490 this._showPalette(palette, true);
491 },
492
493 _resizeForSelectedPalette: function()
494 {
495 var palette = this._palettes.get(this._selectedColorPalette.get());
496 if (!palette)
497 return;
498 var numColors = palette.colors.length;
499 if (palette === this._customPaletteSetting.get())
500 numColors++;
501 var rowsNeeded = Math.max(1, Math.ceil(numColors / WebInspector.Spectrum ._itemsPerPaletteRow));
502 if (this._numPaletteRowsShown === rowsNeeded)
503 return;
504 this._numPaletteRowsShown = rowsNeeded;
505 var paletteColorHeight = 12;
506 var paletteMargin = 12;
507 var paletteTop = 235;
508 this.element.style.height = (paletteTop + paletteMargin + (paletteColorH eight + paletteMargin) * rowsNeeded) + "px";
509 this.dispatchEventToListeners(WebInspector.Spectrum.Events.SizeChanged);
510 },
511
512 /**
513 * @param {string} colorText
514 * @param {boolean} matchUserFormat
515 */
516 _paletteColorSelected: function(colorText, matchUserFormat)
517 {
518 var color = WebInspector.Color.parse(colorText);
519 if (!color)
520 return;
521 this._innerSetColor(color.hsva(), colorText, matchUserFormat ? this._col orFormat : color.format(), WebInspector.Spectrum._ChangeSource.Other);
522 },
523
524 _addColorToCustomPalette: function()
525 {
526 var palette = this._customPaletteSetting.get();
527 palette.colors.push(this.colorString());
528 this._customPaletteSetting.set(palette);
529 this._showPalette(this._customPaletteSetting.get(), false);
530 },
531
532 /**
533 * @param {number} colorIndex
534 * @param {!Event} event
535 */
536 _showPaletteColorContextMenu: function(colorIndex, event)
537 {
538 if (!this._paletteContainerMutable)
539 return;
540 var contextMenu = new WebInspector.ContextMenu(event);
541 if (colorIndex !== -1) {
542 contextMenu.appendItem(WebInspector.UIString("Remove color"), this._ deletePaletteColors.bind(this, colorIndex, false));
543 contextMenu.appendItem(WebInspector.UIString("Remove all to the righ t"), this._deletePaletteColors.bind(this, colorIndex, true));
544 }
545 contextMenu.appendItem(WebInspector.UIString("Clear palette"), this._del etePaletteColors.bind(this, -1, true));
546 contextMenu.show();
547 },
548
549 /**
550 * @param {number} colorIndex
551 * @param {boolean} toRight
552 */
553 _deletePaletteColors: function(colorIndex, toRight)
554 {
555 var palette = this._customPaletteSetting.get();
556 if (toRight)
557 palette.colors.splice(colorIndex + 1, palette.colors.length - colorI ndex - 1);
558 else
559 palette.colors.splice(colorIndex, 1);
560 this._customPaletteSetting.set(palette);
561 this._showPalette(this._customPaletteSetting.get(), false);
562 },
563
564 /**
565 * @param {!WebInspector.Color} color
566 * @param {string} colorFormat
567 */
568 setColor: function(color, colorFormat)
569 {
570 this._originalFormat = colorFormat;
571 this._innerSetColor(color.hsva(), "", colorFormat, WebInspector.Spectrum ._ChangeSource.Model);
572 },
573
574 /**
575 * @param {!Array<number>|undefined} hsva
576 * @param {string|undefined} colorString
577 * @param {string|undefined} colorFormat
578 * @param {string} changeSource
579 */
580 _innerSetColor: function(hsva, colorString, colorFormat, changeSource)
581 {
582 if (hsva !== undefined)
583 this._hsv = hsva;
584 if (colorString !== undefined)
585 this._colorString = colorString;
586 if (colorFormat !== undefined) {
587 var cf = WebInspector.Color.Format;
588 console.assert(colorFormat !== cf.Original, "Spectrum's color format cannot be Original");
589 if (colorFormat === cf.RGBA)
590 colorFormat = cf.RGB;
591 else if (colorFormat === cf.HSLA)
592 colorFormat = cf.HSL;
593 else if (colorFormat === cf.HEXA)
594 colorFormat = cf.HEX;
595 else if (colorFormat === cf.ShortHEXA)
596 colorFormat = cf.ShortHEX;
597 this._colorFormat = colorFormat;
598 }
599
600 this._updateHelperLocations();
601 this._updateUI();
602
603 if (changeSource !== WebInspector.Spectrum._ChangeSource.Input)
604 this._updateInput();
605 if (changeSource !== WebInspector.Spectrum._ChangeSource.Model)
606 this.dispatchEventToListeners(WebInspector.Spectrum.Events.ColorChan ged, this.colorString());
607 },
608
609 /**
610 * @param {!WebInspector.Color} color
611 */
612 setContrastColor: function(color)
613 {
614 this._contrastColor = color;
615 this._updateUI();
616 },
617
618 /**
619 * @return {!WebInspector.Color}
620 */
621 _color: function()
622 {
623 return WebInspector.Color.fromHSVA(this._hsv);
624 },
625
626 /**
627 * @return {string}
628 */
629 colorString: function()
630 {
631 if (this._colorString)
632 return this._colorString;
633 var cf = WebInspector.Color.Format;
634 var color = this._color();
635 var colorString = color.asString(this._colorFormat);
636 if (colorString)
637 return colorString;
638
639 if (this._colorFormat === cf.Nickname) {
640 colorString = color.asString(cf.HEX);
641 if (colorString)
642 return colorString;
643 }
644
645 if (this._colorFormat === cf.ShortHEX)
646 colorString = color.asString(color.detectHEXFormat());
647 else if (this._colorFormat === cf.HEX)
648 colorString = color.asString(color.hasAlpha() ? cf.HEXA : cf.HEX);
649 else if (this._colorFormat === cf.HSL)
650 colorString = color.asString(cf.HSLA);
651 else
652 colorString = color.asString(cf.RGBA);
653
654 console.assert(colorString);
655 return colorString || "";
656 },
657
658 _updateHelperLocations: function()
659 {
660 var h = this._hsv[0];
661 var s = this._hsv[1];
662 var v = this._hsv[2];
663 var alpha = this._hsv[3];
664
665 // Where to show the little circle that displays your current selected c olor.
666 var dragX = s * this.dragWidth;
667 var dragY = this.dragHeight - (v * this.dragHeight);
668
669 dragX = Math.max(-this._colorDragElementHeight,
670 Math.min(this.dragWidth - this._colorDragElementHeight, dragX - this._colorDragElementHeight));
671 dragY = Math.max(-this._colorDragElementHeight,
672 Math.min(this.dragHeight - this._colorDragElementHeight, dragY - this._colorDragElementHeight));
673
674 this._colorDragElement.positionAt(dragX, dragY);
675
676 // Where to show the bar that displays your current selected hue.
677 var hueSlideX = (1 - h) * this._hueAlphaWidth - this.slideHelperWidth;
678 this._hueSlider.style.left = hueSlideX + "px";
679 var alphaSlideX = alpha * this._hueAlphaWidth - this.slideHelperWidth;
680 this._alphaSlider.style.left = alphaSlideX + "px";
681 },
682
683 _updateInput: function()
684 {
685 var cf = WebInspector.Color.Format;
686 if (this._colorFormat === cf.HEX || this._colorFormat === cf.ShortHEX || this._colorFormat === cf.Nickname) {
687 this._hexContainer.hidden = false;
688 this._displayContainer.hidden = true;
689 if (this._colorFormat === cf.ShortHEX)
690 this._hexValue.value = this._color().asString(this._color().dete ctHEXFormat());
691 else
692 // Don't use short HEX if original was not in that format.
693 this._hexValue.value = this._color().asString(this._color().hasA lpha() ? cf.HEXA : cf.HEX);
694 } else {
695 // RGBA, HSLA display.
696 this._hexContainer.hidden = true;
697 this._displayContainer.hidden = false;
698 var isRgb = this._colorFormat === cf.RGB;
699 this._textLabels.textContent = isRgb ? "RGBA" : "HSLA";
700 var colorValues = isRgb ? this._color().canonicalRGBA() : this._colo r().canonicalHSLA();
701 for (var i = 0; i < 3; ++i) {
702 this._textValues[i].value = colorValues[i];
703 if (!isRgb && (i === 1 || i === 2))
704 this._textValues[i].value += "%";
705 }
706 this._textValues[3].value = Math.round(colorValues[3] * 100) / 100;
707 }
708 },
709
710 /**
711 * @param {number} requiredContrast
712 */
713 _drawContrastRatioLine: function(requiredContrast)
714 {
715 if (!this._contrastColor || !this.dragWidth || !this.dragHeight)
716 return;
717
718 /** const */ var width = this.dragWidth;
719 /** const */ var height = this.dragHeight;
720 /** const */ var dS = 0.02;
721 /** const */ var epsilon = 0.002;
722 /** const */ var H = 0;
723 /** const */ var S = 1;
724 /** const */ var V = 2;
725 /** const */ var A = 3;
726
727 var fgRGBA = [];
728 WebInspector.Color.hsva2rgba(this._hsv, fgRGBA);
729 var fgLuminance = WebInspector.Color.luminance(fgRGBA);
730 var bgRGBA = this._contrastColor.rgba();
731 var bgLuminance = WebInspector.Color.luminance(bgRGBA);
732 var fgIsLighter = fgLuminance > bgLuminance;
733 var desiredLuminance = WebInspector.Color.desiredLuminance(bgLuminance, requiredContrast, fgIsLighter);
734
735 var lastV = this._hsv[V];
736 var currentSlope = 0;
737 var candidateHSVA = [this._hsv[H], 0, 0, this._hsv[A]];
738 var pathBuilder = [];
739 var candidateRGBA = [];
740 WebInspector.Color.hsva2rgba(candidateHSVA, candidateRGBA);
741 var blendedRGBA = [];
742 WebInspector.Color.blendColors(candidateRGBA, bgRGBA, blendedRGBA);
743
744 /**
745 * Approach the desired contrast ratio by modifying the given component
746 * from the given starting value.
747 * @param {number} index
748 * @param {number} x
749 * @param {boolean} onAxis
750 * @return {?number}
751 */
752 function approach(index, x, onAxis)
753 {
754 while (0 <= x && x <= 1) {
755 candidateHSVA[index] = x;
756 WebInspector.Color.hsva2rgba(candidateHSVA, candidateRGBA);
757 WebInspector.Color.blendColors(candidateRGBA, bgRGBA, blendedRGB A);
758 var fgLuminance = WebInspector.Color.luminance(blendedRGBA);
759 var dLuminance = fgLuminance - desiredLuminance;
760
761 if (Math.abs(dLuminance) < (onAxis ? epsilon / 10 : epsilon))
762 return x;
763 else
764 x += (index === V ? -dLuminance : dLuminance);
765 }
766 return null;
767 }
768
769 for (var s = 0; s < 1 + dS; s += dS) {
770 s = Math.min(1, s);
771 candidateHSVA[S] = s;
772
773 var v = lastV;
774 v = lastV + currentSlope * dS;
775
776 v = approach(V, v, s === 0);
777 if (v === null)
778 break;
779
780 currentSlope = (v - lastV) / dS;
781
782 pathBuilder.push(pathBuilder.length ? "L" : "M");
783 pathBuilder.push(s * width);
784 pathBuilder.push((1 - v) * height);
785 }
786
787 if (s < 1 + dS) {
788 s -= dS;
789 candidateHSVA[V] = 1;
790 s = approach(S, s, true);
791 if (s !== null)
792 pathBuilder = pathBuilder.concat(["L", s * width, -1])
793 }
794
795 this._contrastRatioLine.setAttribute("d", pathBuilder.join(" "));
796 },
797
798 _updateUI: function()
799 {
800 var h = WebInspector.Color.fromHSVA([this._hsv[0], 1, 1, 1]);
801 this._colorElement.style.backgroundColor = /** @type {string} */ (h.asSt ring(WebInspector.Color.Format.RGB));
802 if (Runtime.experiments.isEnabled("colorContrastRatio")) {
803 // TODO(samli): Determine size of text and switch between AA/AAA rat ings.
804 this._drawContrastRatioLine(4.5);
805 }
806 this._swatchInnerElement.style.backgroundColor = /** @type {string} */ ( this._color().asString(WebInspector.Color.Format.RGBA));
807 // Show border if the swatch is white.
808 this._swatchInnerElement.classList.toggle("swatch-inner-white", this._co lor().hsla()[2] > 0.9);
809 this._colorDragElement.style.backgroundColor = /** @type {string} */ (th is._color().asString(WebInspector.Color.Format.RGBA));
810 var noAlpha = WebInspector.Color.fromHSVA(this._hsv.slice(0,3).concat(1) );
811 this._alphaElementBackground.style.backgroundImage = String.sprintf("lin ear-gradient(to right, rgba(0,0,0,0), %s)", noAlpha.asString(WebInspector.Color. Format.RGB));
812 },
813
814 _formatViewSwitch: function()
815 {
816 var cf = WebInspector.Color.Format;
817 var format = cf.RGB;
818 if (this._colorFormat === cf.RGB)
819 format = cf.HSL;
820 else if (this._colorFormat === cf.HSL)
821 format = this._originalFormat === cf.ShortHEX ? cf.ShortHEX : cf.HEX ;
822 this._innerSetColor(undefined, "", format, WebInspector.Spectrum._Change Source.Other);
823 },
824
825 /**
826 * @param {!Event} event
827 */
828 _inputChanged: function(event)
829 {
830 /**
831 * @param {!Element} element
832 * @return {string}
833 */
834 function elementValue(element)
835 {
836 return element.value;
837 }
838
839 var inputElement = /** @type {!Element} */(event.currentTarget);
840 var arrowKeyOrMouseWheelEvent = (event.key === "ArrowUp" || event.key == = "ArrowDown" || event.type === "mousewheel");
841 var pageKeyPressed = (event.key === "PageUp" || event.key === "PageDown" );
842 if (arrowKeyOrMouseWheelEvent || pageKeyPressed) {
843 var newValue = WebInspector.createReplacementString(inputElement.val ue, event);
844 if (newValue) {
845 inputElement.value = newValue;
846 inputElement.selectionStart = 0;
847 inputElement.selectionEnd = newValue.length;
848 }
849 event.consume(true);
850 }
851
852 const cf = WebInspector.Color.Format;
853 var colorString;
854 if (this._colorFormat === cf.HEX || this._colorFormat === cf.ShortHEX) {
855 colorString = this._hexValue.value;
856 } else {
857 var format = this._colorFormat === cf.RGB ? "rgba" : "hsla";
858 var values = this._textValues.map(elementValue).join(",");
859 colorString = String.sprintf("%s(%s)", format, values);
860 }
861
862 var color = WebInspector.Color.parse(colorString);
863 if (!color)
864 return;
865 var hsv = color.hsva();
866 if (this._colorFormat === cf.HEX || this._colorFormat === cf.ShortHEX)
867 this._colorFormat = color.detectHEXFormat();
868 this._innerSetColor(hsv, colorString, undefined, WebInspector.Spectrum._ ChangeSource.Input);
869 },
870
871 wasShown: function()
872 {
873 this._hueAlphaWidth = this._hueElement.offsetWidth;
874 this.slideHelperWidth = this._hueSlider.offsetWidth / 2;
875 this.dragWidth = this._colorElement.offsetWidth;
876 this.dragHeight = this._colorElement.offsetHeight;
877 this._colorDragElementHeight = this._colorDragElement.offsetHeight / 2;
878 this._innerSetColor(undefined, undefined, undefined, WebInspector.Spectr um._ChangeSource.Model);
879 this._toggleColorPicker(true);
880 WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeMod el, WebInspector.ResourceTreeModel.EventTypes.ColorPicked, this._colorPicked, th is);
881 },
882
883 willHide: function()
884 {
885 this._toggleColorPicker(false);
886 WebInspector.targetManager.removeModelListener(WebInspector.ResourceTree Model, WebInspector.ResourceTreeModel.EventTypes.ColorPicked, this._colorPicked, this);
887 },
888
889 /**
890 * @param {boolean=} enabled
891 * @param {!WebInspector.Event=} event
892 */
893 _toggleColorPicker: function(enabled, event)
894 {
895 if (enabled === undefined)
896 enabled = !this._colorPickerButton.toggled();
897 this._colorPickerButton.setToggled(enabled);
898 for (var target of WebInspector.targetManager.targets())
899 target.pageAgent().setColorPickerEnabled(enabled);
900 },
901
902 /**
903 * @param {!WebInspector.Event} event
904 */
905 _colorPicked: function(event)
906 {
907 var rgbColor = /** @type {!DOMAgent.RGBA} */ (event.data);
908 var rgba = [rgbColor.r, rgbColor.g, rgbColor.b, (rgbColor.a / 2.55 | 0) / 100];
909 var color = WebInspector.Color.fromRGBA(rgba);
910 this._innerSetColor(color.hsva(), "", undefined, WebInspector.Spectrum._ ChangeSource.Other);
911 InspectorFrontendHost.bringToFront();
912 },
913
914
915 __proto__: WebInspector.VBox.prototype
916 }
917
918 /** @typedef {{ title: string, colors: !Array.<string>, mutable: boolean }} */
919 WebInspector.Spectrum.Palette;
920 WebInspector.Spectrum.GeneratedPaletteTitle = "Page colors";
921
922 /**
923 * @constructor
924 * @param {function(!WebInspector.Spectrum.Palette)} callback
925 */
926 WebInspector.Spectrum.PaletteGenerator = function(callback)
927 {
928 this._callback = callback;
929 /** @type {!Map.<string, number>} */
930 this._frequencyMap = new Map();
931 var stylesheetPromises = [];
932 for (var target of WebInspector.targetManager.targets(WebInspector.Target.Ty pe.Page)) {
933 var cssModel = WebInspector.CSSModel.fromTarget(target);
934 for (var stylesheet of cssModel.allStyleSheets())
935 stylesheetPromises.push(new Promise(this._processStylesheet.bind(thi s, stylesheet)));
936 }
937 Promise.all(stylesheetPromises)
938 .catchException(null)
939 .then(this._finish.bind(this));
940 }
941
942 WebInspector.Spectrum.PaletteGenerator.prototype = {
943 /**
944 * @param {string} a
945 * @param {string} b
946 * @return {number}
947 */
948 _frequencyComparator: function(a, b)
949 {
950 return this._frequencyMap.get(b) - this._frequencyMap.get(a);
951 },
952
953 _finish: function()
954 {
955 /**
956 * @param {string} a
957 * @param {string} b
958 * @return {number}
959 */
960 function hueComparator(a, b)
961 {
962 var hsva = paletteColors.get(a).hsva();
963 var hsvb = paletteColors.get(b).hsva();
964
965 // First trim the shades of gray
966 if (hsvb[1] < 0.12 && hsva[1] < 0.12)
967 return hsvb[2] * hsvb[3] - hsva[2] * hsva[3];
968 if (hsvb[1] < 0.12)
969 return -1;
970 if (hsva[1] < 0.12)
971 return 1;
972
973 // Equal hue -> sort by sat
974 if (hsvb[0] === hsva[0])
975 return hsvb[1] * hsvb[3] - hsva[1] * hsva[3];
976
977 return (hsvb[0] + 0.94) % 1 - (hsva[0] + 0.94) % 1;
978 }
979
980 var colors = this._frequencyMap.keysArray();
981 colors = colors.sort(this._frequencyComparator.bind(this));
982 /** @type {!Map.<string, !WebInspector.Color>} */
983 var paletteColors = new Map();
984 var colorsPerRow = 24;
985 while (paletteColors.size < colorsPerRow && colors.length) {
986 var colorText = colors.shift();
987 var color = WebInspector.Color.parse(colorText);
988 if (!color || color.nickname() === "white" || color.nickname() === " black")
989 continue;
990 paletteColors.set(colorText, color);
991 }
992
993 this._callback({ title: WebInspector.Spectrum.GeneratedPaletteTitle, col ors: paletteColors.keysArray().sort(hueComparator), mutable: false });
994 },
995
996 /**
997 * @param {!WebInspector.CSSStyleSheetHeader} stylesheet
998 * @param {function(?)} resolve
999 * @this {WebInspector.Spectrum.PaletteGenerator}
1000 */
1001 _processStylesheet: function(stylesheet, resolve)
1002 {
1003 /**
1004 * @param {?string} text
1005 * @this {WebInspector.Spectrum.PaletteGenerator}
1006 */
1007 function parseContent(text)
1008 {
1009 text = text.toLowerCase();
1010 var regexResult = text.match(/((?:rgb|hsl)a?\([^)]+\)|#[0-9a-f]{6}|# [0-9a-f]{3})/g) || [];
1011 for (var c of regexResult) {
1012 var frequency = this._frequencyMap.get(c) || 0;
1013 this._frequencyMap.set(c, ++frequency);
1014 }
1015 resolve(null);
1016 }
1017
1018 stylesheet.requestContent().then(parseContent.bind(this));
1019 }
1020 }
1021
1022 WebInspector.Spectrum.MaterialPaletteShades = {
1023 "#F44336": ["#FFEBEE", "#FFCDD2", "#EF9A9A", "#E57373", "#EF5350", "#F44336" , "#E53935", "#D32F2F", "#C62828", "#B71C1C"],
1024 "#E91E63": ["#FCE4EC", "#F8BBD0", "#F48FB1", "#F06292", "#EC407A", "#E91E63" , "#D81B60", "#C2185B", "#AD1457", "#880E4F"],
1025 "#9C27B0": ["#F3E5F5", "#E1BEE7", "#CE93D8", "#BA68C8", "#AB47BC", "#9C27B0" , "#8E24AA", "#7B1FA2", "#6A1B9A", "#4A148C"],
1026 "#673AB7": ["#EDE7F6", "#D1C4E9", "#B39DDB", "#9575CD", "#7E57C2", "#673AB7" , "#5E35B1", "#512DA8", "#4527A0", "#311B92"],
1027 "#3F51B5": ["#E8EAF6", "#C5CAE9", "#9FA8DA", "#7986CB", "#5C6BC0", "#3F51B5" , "#3949AB", "#303F9F", "#283593", "#1A237E"],
1028 "#2196F3": ["#E3F2FD", "#BBDEFB", "#90CAF9", "#64B5F6", "#42A5F5", "#2196F3" , "#1E88E5", "#1976D2", "#1565C0", "#0D47A1"],
1029 "#03A9F4": ["#E1F5FE", "#B3E5FC", "#81D4FA", "#4FC3F7", "#29B6F6", "#03A9F4" , "#039BE5", "#0288D1", "#0277BD", "#01579B"],
1030 "#00BCD4": ["#E0F7FA", "#B2EBF2", "#80DEEA", "#4DD0E1", "#26C6DA", "#00BCD4" , "#00ACC1", "#0097A7", "#00838F", "#006064"],
1031 "#009688": ["#E0F2F1", "#B2DFDB", "#80CBC4", "#4DB6AC", "#26A69A", "#009688" , "#00897B", "#00796B", "#00695C", "#004D40"],
1032 "#4CAF50": ["#E8F5E9", "#C8E6C9", "#A5D6A7", "#81C784", "#66BB6A", "#4CAF50" , "#43A047", "#388E3C", "#2E7D32", "#1B5E20"],
1033 "#8BC34A": ["#F1F8E9", "#DCEDC8", "#C5E1A5", "#AED581", "#9CCC65", "#8BC34A" , "#7CB342", "#689F38", "#558B2F", "#33691E"],
1034 "#CDDC39": ["#F9FBE7", "#F0F4C3", "#E6EE9C", "#DCE775", "#D4E157", "#CDDC39" , "#C0CA33", "#AFB42B", "#9E9D24", "#827717"],
1035 "#FFEB3B": ["#FFFDE7", "#FFF9C4", "#FFF59D", "#FFF176", "#FFEE58", "#FFEB3B" , "#FDD835", "#FBC02D", "#F9A825", "#F57F17"],
1036 "#FFC107": ["#FFF8E1", "#FFECB3", "#FFE082", "#FFD54F", "#FFCA28", "#FFC107" , "#FFB300", "#FFA000", "#FF8F00", "#FF6F00"],
1037 "#FF9800": ["#FFF3E0", "#FFE0B2", "#FFCC80", "#FFB74D", "#FFA726", "#FF9800" , "#FB8C00", "#F57C00", "#EF6C00", "#E65100"],
1038 "#FF5722": ["#FBE9E7", "#FFCCBC", "#FFAB91", "#FF8A65", "#FF7043", "#FF5722" , "#F4511E", "#E64A19", "#D84315", "#BF360C"],
1039 "#795548": ["#EFEBE9", "#D7CCC8", "#BCAAA4", "#A1887F", "#8D6E63", "#795548" , "#6D4C41", "#5D4037", "#4E342E", "#3E2723"],
1040 "#9E9E9E": ["#FAFAFA", "#F5F5F5", "#EEEEEE", "#E0E0E0", "#BDBDBD", "#9E9E9E" , "#757575", "#616161", "#424242", "#212121"],
1041 "#607D8B": ["#ECEFF1", "#CFD8DC", "#B0BEC5", "#90A4AE", "#78909C", "#607D8B" , "#546E7A", "#455A64", "#37474F", "#263238"]
1042 };
1043
1044 WebInspector.Spectrum.MaterialPalette = { title: "Material", mutable: false, mat chUserFormat: true, colors: Object.keys(WebInspector.Spectrum.MaterialPaletteSha des) };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698