| /* |
| * Copyright (C) 2011 Brian Grinstead All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
| * its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.VBox} |
| */ |
| WebInspector.Spectrum = function() |
| { |
| /** |
| * @param {!Element} parentElement |
| */ |
| function appendSwitcherIcon(parentElement) |
| { |
| var icon = parentElement.createSVGChild("svg"); |
| icon.setAttribute("height", 16); |
| icon.setAttribute("width", 16); |
| var path = icon.createSVGChild("path"); |
| path.setAttribute("d", "M5,6 L11,6 L8,2 Z M5,10 L11,10 L8,14 Z"); |
| return icon; |
| } |
| |
| WebInspector.VBox.call(this, true); |
| this.registerRequiredCSS("elements/spectrum.css"); |
| this.contentElement.tabIndex = 0; |
| |
| this._colorElement = this.contentElement.createChild("div", "spectrum-color"); |
| this._colorDragElement = this._colorElement.createChild("div", "spectrum-sat fill").createChild("div", "spectrum-val fill").createChild("div", "spectrum-dragger"); |
| var contrastRatioSVG = this._colorElement.createSVGChild("svg", "spectrum-contrast-container fill"); |
| this._contrastRatioLine = contrastRatioSVG.createSVGChild("path", "spectrum-contrast-line"); |
| |
| var toolbar = new WebInspector.Toolbar("spectrum-eye-dropper", this.contentElement); |
| this._colorPickerButton = new WebInspector.ToolbarToggle(WebInspector.UIString("Toggle color picker"), "eyedropper-toolbar-item"); |
| this._colorPickerButton.setToggled(true); |
| this._colorPickerButton.addEventListener("click", this._toggleColorPicker.bind(this, undefined)); |
| toolbar.appendToolbarItem(this._colorPickerButton); |
| |
| var swatchElement = this.contentElement.createChild("span", "swatch"); |
| this._swatchInnerElement = swatchElement.createChild("span", "swatch-inner"); |
| |
| this._hueElement = this.contentElement.createChild("div", "spectrum-hue"); |
| this._hueSlider = this._hueElement.createChild("div", "spectrum-slider"); |
| this._alphaElement = this.contentElement.createChild("div", "spectrum-alpha"); |
| this._alphaElementBackground = this._alphaElement.createChild("div", "spectrum-alpha-background"); |
| this._alphaSlider = this._alphaElement.createChild("div", "spectrum-slider"); |
| |
| var displaySwitcher = this.contentElement.createChild("div", "spectrum-display-switcher spectrum-switcher"); |
| appendSwitcherIcon(displaySwitcher); |
| displaySwitcher.addEventListener("click", this._formatViewSwitch.bind(this)); |
| |
| // RGBA/HSLA display. |
| this._displayContainer = this.contentElement.createChild("div", "spectrum-text source-code"); |
| this._textValues = []; |
| for (var i = 0; i < 4; ++i) { |
| var inputValue = this._displayContainer.createChild("input", "spectrum-text-value"); |
| inputValue.maxLength = 4; |
| this._textValues.push(inputValue); |
| inputValue.addEventListener("keydown", this._inputChanged.bind(this), false); |
| inputValue.addEventListener("input", this._inputChanged.bind(this), false); |
| inputValue.addEventListener("mousewheel", this._inputChanged.bind(this), false); |
| } |
| |
| this._textLabels = this._displayContainer.createChild("div", "spectrum-text-label"); |
| |
| // HEX display. |
| this._hexContainer = this.contentElement.createChild("div", "spectrum-text spectrum-text-hex source-code"); |
| this._hexValue = this._hexContainer.createChild("input", "spectrum-text-value"); |
| this._hexValue.maxLength = 9; |
| this._hexValue.addEventListener("keydown", this._inputChanged.bind(this), false); |
| this._hexValue.addEventListener("input", this._inputChanged.bind(this), false); |
| this._hexValue.addEventListener("mousewheel", this._inputChanged.bind(this), false); |
| |
| var label = this._hexContainer.createChild("div", "spectrum-text-label"); |
| label.textContent = "HEX"; |
| |
| WebInspector.installDragHandle(this._hueElement, dragStart.bind(this, positionHue.bind(this)), positionHue.bind(this), null, "default"); |
| WebInspector.installDragHandle(this._alphaElement, dragStart.bind(this, positionAlpha.bind(this)), positionAlpha.bind(this), null, "default"); |
| WebInspector.installDragHandle(this._colorElement, dragStart.bind(this, positionColor.bind(this)), positionColor.bind(this), null, "default"); |
| |
| this.element.classList.add("palettes-enabled"); |
| /** @type {!Map.<string, !WebInspector.Spectrum.Palette>} */ |
| this._palettes = new Map(); |
| this._palettePanel = this.contentElement.createChild("div", "palette-panel"); |
| this._palettePanelShowing = false; |
| this._paletteContainer = this.contentElement.createChild("div", "spectrum-palette"); |
| this._paletteContainer.addEventListener("contextmenu", this._showPaletteColorContextMenu.bind(this, -1)); |
| this._shadesContainer = this.contentElement.createChild("div", "palette-color-shades hidden"); |
| WebInspector.installDragHandle(this._paletteContainer, this._paletteDragStart.bind(this), this._paletteDrag.bind(this), this._paletteDragEnd.bind(this), "default"); |
| var paletteSwitcher = this.contentElement.createChild("div", "spectrum-palette-switcher spectrum-switcher"); |
| appendSwitcherIcon(paletteSwitcher); |
| paletteSwitcher.addEventListener("click", this._togglePalettePanel.bind(this, true)); |
| |
| this._deleteIconToolbar = new WebInspector.Toolbar("delete-color-toolbar"); |
| this._deleteButton = new WebInspector.ToolbarButton("", "garbage-collect-toolbar-item"); |
| this._deleteIconToolbar.appendToolbarItem(this._deleteButton); |
| |
| var overlay = this.contentElement.createChild("div", "spectrum-overlay fill"); |
| overlay.addEventListener("click", this._togglePalettePanel.bind(this, false)); |
| |
| this._addColorToolbar = new WebInspector.Toolbar("add-color-toolbar"); |
| var addColorButton = new WebInspector.ToolbarButton(WebInspector.UIString("Add to palette"), "add-toolbar-item"); |
| addColorButton.addEventListener("click", this._addColorToCustomPalette.bind(this)); |
| this._addColorToolbar.appendToolbarItem(addColorButton); |
| |
| this._loadPalettes(); |
| new WebInspector.Spectrum.PaletteGenerator(this._generatedPaletteLoaded.bind(this)); |
| |
| /** |
| * @param {function(!Event)} callback |
| * @param {!Event} event |
| * @return {boolean} |
| * @this {WebInspector.Spectrum} |
| */ |
| function dragStart(callback, event) |
| { |
| this._hueAlphaLeft = this._hueElement.totalOffsetLeft(); |
| this._colorOffset = this._colorElement.totalOffset(); |
| callback(event); |
| return true; |
| } |
| |
| /** |
| * @param {!Event} event |
| * @this {WebInspector.Spectrum} |
| */ |
| function positionHue(event) |
| { |
| var hsva = this._hsv.slice(); |
| hsva[0] = Number.constrain(1 - (event.x - this._hueAlphaLeft) / this._hueAlphaWidth, 0, 1); |
| this._innerSetColor(hsva, "", undefined, WebInspector.Spectrum._ChangeSource.Other); |
| } |
| |
| /** |
| * @param {!Event} event |
| * @this {WebInspector.Spectrum} |
| */ |
| function positionAlpha(event) |
| { |
| var newAlpha = Math.round((event.x - this._hueAlphaLeft) / this._hueAlphaWidth * 100) / 100; |
| var hsva = this._hsv.slice(); |
| hsva[3] = Number.constrain(newAlpha, 0, 1); |
| var colorFormat = undefined; |
| if (hsva[3] !== 1 && this._colorFormat === WebInspector.Color.Format.Nickname) |
| colorFormat = WebInspector.Color.Format.HEX; |
| this._innerSetColor(hsva, "", colorFormat, WebInspector.Spectrum._ChangeSource.Other); |
| } |
| |
| /** |
| * @param {!Event} event |
| * @this {WebInspector.Spectrum} |
| */ |
| function positionColor(event) |
| { |
| var hsva = this._hsv.slice(); |
| hsva[1] = Number.constrain((event.x - this._colorOffset.left) / this.dragWidth, 0, 1); |
| hsva[2] = Number.constrain(1 - (event.y - this._colorOffset.top) / this.dragHeight, 0, 1); |
| this._innerSetColor(hsva, "", undefined, WebInspector.Spectrum._ChangeSource.Other); |
| } |
| } |
| |
| WebInspector.Spectrum._ChangeSource = { |
| Input: "Input", |
| Model: "Model", |
| Other: "Other" |
| } |
| |
| WebInspector.Spectrum.Events = { |
| ColorChanged: "ColorChanged", |
| SizeChanged: "SizeChanged" |
| }; |
| |
| WebInspector.Spectrum._colorChipSize = 24; |
| WebInspector.Spectrum._itemsPerPaletteRow = 8; |
| |
| WebInspector.Spectrum.prototype = { |
| _updatePalettePanel: function() |
| { |
| this._palettePanel.removeChildren(); |
| var title = this._palettePanel.createChild("div", "palette-title"); |
| title.textContent = WebInspector.UIString("Color Palettes"); |
| var toolbar = new WebInspector.Toolbar("", this._palettePanel); |
| var closeButton = new WebInspector.ToolbarButton("Return to color picker", "delete-toolbar-item"); |
| closeButton.addEventListener("click", this._togglePalettePanel.bind(this, false)); |
| toolbar.appendToolbarItem(closeButton); |
| for (var palette of this._palettes.values()) |
| this._palettePanel.appendChild(this._createPreviewPaletteElement(palette)); |
| }, |
| |
| /** |
| * @param {boolean} show |
| */ |
| _togglePalettePanel: function(show) |
| { |
| if (this._palettePanelShowing === show) |
| return; |
| if (show) |
| this._updatePalettePanel(); |
| this._focus(); |
| this._palettePanelShowing = show; |
| this.contentElement.classList.toggle("palette-panel-showing", show); |
| }, |
| |
| _focus: function() |
| { |
| if (this.isShowing() && WebInspector.currentFocusElement() !== this.contentElement) |
| WebInspector.setCurrentFocusElement(this.contentElement); |
| }, |
| |
| /** |
| * @param {string} colorText |
| * @param {number=} animationDelay |
| * @return {!Element} |
| */ |
| _createPaletteColor: function(colorText, animationDelay) |
| { |
| var element = createElementWithClass("div", "spectrum-palette-color"); |
| element.style.background = String.sprintf("linear-gradient(%s, %s), url(Images/checker.png)", colorText, colorText); |
| if (animationDelay) |
| element.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 100, delay: animationDelay, fill: "backwards" }); |
| element.title = colorText; |
| return element; |
| }, |
| |
| /** |
| * @param {!WebInspector.Spectrum.Palette} palette |
| * @param {boolean} animate |
| * @param {!Event=} event |
| */ |
| _showPalette: function(palette, animate, event) |
| { |
| this._resizeForSelectedPalette(); |
| this._paletteContainer.removeChildren(); |
| for (var i = 0; i < palette.colors.length; i++) { |
| var animationDelay = animate ? i * 100 / palette.colors.length : 0; |
| var colorElement = this._createPaletteColor(palette.colors[i], animationDelay); |
| colorElement.addEventListener("mousedown", this._paletteColorSelected.bind(this, palette.colors[i], palette.matchUserFormat)); |
| if (palette.mutable) { |
| colorElement.__mutable = true; |
| colorElement.__color = palette.colors[i]; |
| colorElement.addEventListener("contextmenu", this._showPaletteColorContextMenu.bind(this, i)); |
| } else if (palette === WebInspector.Spectrum.MaterialPalette) { |
| colorElement.classList.add("has-material-shades"); |
| var shadow = colorElement.createChild("div", "spectrum-palette-color spectrum-palette-color-shadow"); |
| shadow.style.background = palette.colors[i]; |
| shadow = colorElement.createChild("div", "spectrum-palette-color spectrum-palette-color-shadow"); |
| shadow.style.background = palette.colors[i]; |
| colorElement.title = WebInspector.UIString(palette.colors[i] + ". Long-click to show alternate shades."); |
| new WebInspector.LongClickController(colorElement, this._showLightnessShades.bind(this, colorElement, palette.colors[i])); |
| } |
| this._paletteContainer.appendChild(colorElement); |
| } |
| this._paletteContainerMutable = palette.mutable; |
| |
| var numItems = palette.colors.length; |
| if (palette.mutable) |
| numItems++; |
| if (palette.mutable) { |
| this._paletteContainer.appendChild(this._addColorToolbar.element); |
| this._paletteContainer.appendChild(this._deleteIconToolbar.element); |
| } else { |
| this._addColorToolbar.element.remove(); |
| this._deleteIconToolbar.element.remove(); |
| } |
| |
| this._togglePalettePanel(false); |
| this._focus(); |
| }, |
| |
| /** |
| * @param {!Element} colorElement |
| * @param {string} colorText |
| * @param {!Event} event |
| */ |
| _showLightnessShades: function(colorElement, colorText, event) |
| { |
| /** |
| * @param {!Element} element |
| * @this {!WebInspector.Spectrum} |
| */ |
| function closeLightnessShades(element) |
| { |
| this._shadesContainer.classList.add("hidden"); |
| element.classList.remove("spectrum-shades-shown"); |
| this._shadesContainer.ownerDocument.removeEventListener("mousedown", this._shadesCloseHandler, true); |
| delete this._shadesCloseHandler; |
| } |
| |
| if (this._shadesCloseHandler) |
| this._shadesCloseHandler(); |
| |
| this._shadesContainer.classList.remove("hidden"); |
| this._shadesContainer.removeChildren(); |
| this._shadesContainer.animate([{ transform: "scaleY(0)", opacity: "0" }, { transform: "scaleY(1)", opacity: "1" }], { duration: 200, easing: "cubic-bezier(0.4, 0, 0.2, 1)" }); |
| this._shadesContainer.style.top = colorElement.offsetTop + colorElement.parentElement.offsetTop + "px"; |
| this._shadesContainer.style.left = colorElement.offsetLeft + "px"; |
| colorElement.classList.add("spectrum-shades-shown"); |
| |
| var shades = WebInspector.Spectrum.MaterialPaletteShades[colorText]; |
| for (var i = shades.length - 1; i >= 0; i--) { |
| var shadeElement = this._createPaletteColor(shades[i], i * 200 / shades.length + 100); |
| shadeElement.addEventListener("mousedown", this._paletteColorSelected.bind(this, shades[i], false)); |
| this._shadesContainer.appendChild(shadeElement); |
| } |
| |
| WebInspector.setCurrentFocusElement(this._shadesContainer); |
| this._shadesCloseHandler = closeLightnessShades.bind(this, colorElement); |
| this._shadesContainer.ownerDocument.addEventListener("mousedown", this._shadesCloseHandler, true); |
| }, |
| |
| /** |
| * @param {!Event} e |
| * @return {number} |
| */ |
| _slotIndexForEvent: function(e) |
| { |
| var localX = e.pageX - this._paletteContainer.totalOffsetLeft(); |
| var localY = e.pageY - this._paletteContainer.totalOffsetTop(); |
| var col = Math.min(localX / WebInspector.Spectrum._colorChipSize | 0, WebInspector.Spectrum._itemsPerPaletteRow - 1); |
| var row = (localY / WebInspector.Spectrum._colorChipSize) | 0; |
| return Math.min(row * WebInspector.Spectrum._itemsPerPaletteRow + col, this._customPaletteSetting.get().colors.length - 1); |
| }, |
| |
| /** |
| * @param {!Event} e |
| * @return {boolean} |
| */ |
| _isDraggingToBin: function(e) |
| { |
| return e.pageX > this._deleteIconToolbar.element.totalOffsetLeft(); |
| }, |
| |
| /** |
| * @param {!Event} e |
| * @return {boolean} |
| */ |
| _paletteDragStart: function(e) |
| { |
| var element = e.deepElementFromPoint(); |
| if (!element || !element.__mutable) |
| return false; |
| |
| var index = this._slotIndexForEvent(e); |
| this._dragElement = element; |
| this._dragHotSpotX = e.pageX - (index % WebInspector.Spectrum._itemsPerPaletteRow) * WebInspector.Spectrum._colorChipSize; |
| this._dragHotSpotY = e.pageY - (index / WebInspector.Spectrum._itemsPerPaletteRow | 0) * WebInspector.Spectrum._colorChipSize; |
| return true; |
| }, |
| |
| /** |
| * @param {!Event} e |
| */ |
| _paletteDrag: function(e) |
| { |
| if (e.pageX < this._paletteContainer.totalOffsetLeft() || e.pageY < this._paletteContainer.totalOffsetTop()) |
| return; |
| var newIndex = this._slotIndexForEvent(e); |
| var offsetX = e.pageX - (newIndex % WebInspector.Spectrum._itemsPerPaletteRow) * WebInspector.Spectrum._colorChipSize; |
| var offsetY = e.pageY - (newIndex / WebInspector.Spectrum._itemsPerPaletteRow | 0) * WebInspector.Spectrum._colorChipSize; |
| |
| var isDeleting = this._isDraggingToBin(e); |
| this._deleteIconToolbar.element.classList.add("dragging"); |
| this._deleteIconToolbar.element.classList.toggle("delete-color-toolbar-active", isDeleting); |
| var dragElementTransform = "translateX(" + (offsetX - this._dragHotSpotX) + "px) translateY(" + (offsetY - this._dragHotSpotY) + "px)"; |
| this._dragElement.style.transform = isDeleting ? dragElementTransform + " scale(0.8)" : dragElementTransform; |
| var children = Array.prototype.slice.call(this._paletteContainer.children); |
| var index = children.indexOf(this._dragElement); |
| /** @type {!Map.<!Element, {left: number, top: number}>} */ |
| var swatchOffsets = new Map(); |
| for (var swatch of children) |
| swatchOffsets.set(swatch, swatch.totalOffset()); |
| |
| if (index !== newIndex) |
| this._paletteContainer.insertBefore(this._dragElement, children[newIndex > index ? newIndex + 1 : newIndex]); |
| |
| for (var swatch of children) { |
| if (swatch === this._dragElement) |
| continue; |
| var before = swatchOffsets.get(swatch); |
| var after = swatch.totalOffset(); |
| if (before.left !== after.left || before.top !== after.top) { |
| swatch.animate([ |
| { transform: "translateX(" + (before.left - after.left) + "px) translateY(" + (before.top - after.top) + "px)" }, |
| { transform: "none" }], { duration: 100, easing: "cubic-bezier(0, 0, 0.2, 1)" }); |
| } |
| } |
| }, |
| |
| /** |
| * @param {!Event} e |
| */ |
| _paletteDragEnd: function(e) |
| { |
| if (this._isDraggingToBin(e)) |
| this._dragElement.remove(); |
| this._dragElement.style.removeProperty("transform"); |
| var children = this._paletteContainer.children; |
| var colors = []; |
| for (var i = 0; i < children.length; ++i) { |
| if (children[i].__color) |
| colors.push(children[i].__color); |
| } |
| var palette = this._customPaletteSetting.get(); |
| palette.colors = colors; |
| this._customPaletteSetting.set(palette); |
| this._showPalette(this._customPaletteSetting.get(), false); |
| |
| this._deleteIconToolbar.element.classList.remove("dragging"); |
| this._deleteIconToolbar.element.classList.remove("delete-color-toolbar-active"); |
| }, |
| |
| _loadPalettes: function() |
| { |
| this._palettes.set(WebInspector.Spectrum.MaterialPalette.title, WebInspector.Spectrum.MaterialPalette); |
| /** @type {!WebInspector.Spectrum.Palette} */ |
| var defaultCustomPalette = { title: "Custom", colors: [], mutable: true }; |
| this._customPaletteSetting = WebInspector.settings.createSetting("customColorPalette", defaultCustomPalette); |
| this._palettes.set(this._customPaletteSetting.get().title, this._customPaletteSetting.get()); |
| |
| this._selectedColorPalette = WebInspector.settings.createSetting("selectedColorPalette", WebInspector.Spectrum.GeneratedPaletteTitle); |
| var palette = this._palettes.get(this._selectedColorPalette.get()); |
| if (palette) |
| this._showPalette(palette, true); |
| }, |
| |
| /** |
| * @param {!WebInspector.Spectrum.Palette} generatedPalette |
| */ |
| _generatedPaletteLoaded: function(generatedPalette) |
| { |
| if (generatedPalette.colors.length) |
| this._palettes.set(generatedPalette.title, generatedPalette); |
| if (this._selectedColorPalette.get() !== generatedPalette.title) { |
| return; |
| } else if (!generatedPalette.colors.length) { |
| this._paletteSelected(WebInspector.Spectrum.MaterialPalette); |
| return; |
| } |
| this._showPalette(generatedPalette, true); |
| }, |
| |
| /** |
| * @param {!WebInspector.Spectrum.Palette} palette |
| * @return {!Element} |
| */ |
| _createPreviewPaletteElement: function(palette) |
| { |
| var colorsPerPreviewRow = 5; |
| var previewElement = createElementWithClass("div", "palette-preview"); |
| var titleElement = previewElement.createChild("div", "palette-preview-title"); |
| titleElement.textContent = palette.title; |
| for (var i = 0; i < colorsPerPreviewRow && i < palette.colors.length; i++) |
| previewElement.appendChild(this._createPaletteColor(palette.colors[i])); |
| for (; i < colorsPerPreviewRow; i++) |
| previewElement.createChild("div", "spectrum-palette-color empty-color"); |
| previewElement.addEventListener("click", this._paletteSelected.bind(this, palette)); |
| return previewElement; |
| }, |
| |
| /** |
| * @param {!WebInspector.Spectrum.Palette} palette |
| */ |
| _paletteSelected: function(palette) |
| { |
| this._selectedColorPalette.set(palette.title); |
| this._showPalette(palette, true); |
| }, |
| |
| _resizeForSelectedPalette: function() |
| { |
| var palette = this._palettes.get(this._selectedColorPalette.get()); |
| if (!palette) |
| return; |
| var numColors = palette.colors.length; |
| if (palette === this._customPaletteSetting.get()) |
| numColors++; |
| var rowsNeeded = Math.max(1, Math.ceil(numColors / WebInspector.Spectrum._itemsPerPaletteRow)); |
| if (this._numPaletteRowsShown === rowsNeeded) |
| return; |
| this._numPaletteRowsShown = rowsNeeded; |
| var paletteColorHeight = 12; |
| var paletteMargin = 12; |
| var paletteTop = 235; |
| this.element.style.height = (paletteTop + paletteMargin + (paletteColorHeight + paletteMargin) * rowsNeeded) + "px"; |
| this.dispatchEventToListeners(WebInspector.Spectrum.Events.SizeChanged); |
| }, |
| |
| /** |
| * @param {string} colorText |
| * @param {boolean} matchUserFormat |
| */ |
| _paletteColorSelected: function(colorText, matchUserFormat) |
| { |
| var color = WebInspector.Color.parse(colorText); |
| if (!color) |
| return; |
| this._innerSetColor(color.hsva(), colorText, matchUserFormat ? this._colorFormat : color.format(), WebInspector.Spectrum._ChangeSource.Other); |
| }, |
| |
| _addColorToCustomPalette: function() |
| { |
| var palette = this._customPaletteSetting.get(); |
| palette.colors.push(this.colorString()); |
| this._customPaletteSetting.set(palette); |
| this._showPalette(this._customPaletteSetting.get(), false); |
| }, |
| |
| /** |
| * @param {number} colorIndex |
| * @param {!Event} event |
| */ |
| _showPaletteColorContextMenu: function(colorIndex, event) |
| { |
| if (!this._paletteContainerMutable) |
| return; |
| var contextMenu = new WebInspector.ContextMenu(event); |
| if (colorIndex !== -1) { |
| contextMenu.appendItem(WebInspector.UIString("Remove color"), this._deletePaletteColors.bind(this, colorIndex, false)); |
| contextMenu.appendItem(WebInspector.UIString("Remove all to the right"), this._deletePaletteColors.bind(this, colorIndex, true)); |
| } |
| contextMenu.appendItem(WebInspector.UIString("Clear palette"), this._deletePaletteColors.bind(this, -1, true)); |
| contextMenu.show(); |
| }, |
| |
| /** |
| * @param {number} colorIndex |
| * @param {boolean} toRight |
| */ |
| _deletePaletteColors: function(colorIndex, toRight) |
| { |
| var palette = this._customPaletteSetting.get(); |
| if (toRight) |
| palette.colors.splice(colorIndex + 1, palette.colors.length - colorIndex - 1); |
| else |
| palette.colors.splice(colorIndex, 1); |
| this._customPaletteSetting.set(palette); |
| this._showPalette(this._customPaletteSetting.get(), false); |
| }, |
| |
| /** |
| * @param {!WebInspector.Color} color |
| * @param {string} colorFormat |
| */ |
| setColor: function(color, colorFormat) |
| { |
| this._originalFormat = colorFormat; |
| this._innerSetColor(color.hsva(), "", colorFormat, WebInspector.Spectrum._ChangeSource.Model); |
| }, |
| |
| /** |
| * @param {!Array<number>|undefined} hsva |
| * @param {string|undefined} colorString |
| * @param {string|undefined} colorFormat |
| * @param {string} changeSource |
| */ |
| _innerSetColor: function(hsva, colorString, colorFormat, changeSource) |
| { |
| if (hsva !== undefined) |
| this._hsv = hsva; |
| if (colorString !== undefined) |
| this._colorString = colorString; |
| if (colorFormat !== undefined) { |
| var cf = WebInspector.Color.Format; |
| console.assert(colorFormat !== cf.Original, "Spectrum's color format cannot be Original"); |
| if (colorFormat === cf.RGBA) |
| colorFormat = cf.RGB; |
| else if (colorFormat === cf.HSLA) |
| colorFormat = cf.HSL; |
| else if (colorFormat === cf.HEXA) |
| colorFormat = cf.HEX; |
| else if (colorFormat === cf.ShortHEXA) |
| colorFormat = cf.ShortHEX; |
| this._colorFormat = colorFormat; |
| } |
| |
| this._updateHelperLocations(); |
| this._updateUI(); |
| |
| if (changeSource !== WebInspector.Spectrum._ChangeSource.Input) |
| this._updateInput(); |
| if (changeSource !== WebInspector.Spectrum._ChangeSource.Model) |
| this.dispatchEventToListeners(WebInspector.Spectrum.Events.ColorChanged, this.colorString()); |
| }, |
| |
| /** |
| * @param {!WebInspector.Color} color |
| */ |
| setContrastColor: function(color) |
| { |
| this._contrastColor = color; |
| this._updateUI(); |
| }, |
| |
| /** |
| * @return {!WebInspector.Color} |
| */ |
| _color: function() |
| { |
| return WebInspector.Color.fromHSVA(this._hsv); |
| }, |
| |
| /** |
| * @return {string} |
| */ |
| colorString: function() |
| { |
| if (this._colorString) |
| return this._colorString; |
| var cf = WebInspector.Color.Format; |
| var color = this._color(); |
| var colorString = color.asString(this._colorFormat); |
| if (colorString) |
| return colorString; |
| |
| if (this._colorFormat === cf.Nickname) { |
| colorString = color.asString(cf.HEX); |
| if (colorString) |
| return colorString; |
| } |
| |
| if (this._colorFormat === cf.ShortHEX) |
| colorString = color.asString(color.detectHEXFormat()); |
| else if (this._colorFormat === cf.HEX) |
| colorString = color.asString(color.hasAlpha() ? cf.HEXA : cf.HEX); |
| else if (this._colorFormat === cf.HSL) |
| colorString = color.asString(cf.HSLA); |
| else |
| colorString = color.asString(cf.RGBA); |
| |
| console.assert(colorString); |
| return colorString || ""; |
| }, |
| |
| _updateHelperLocations: function() |
| { |
| var h = this._hsv[0]; |
| var s = this._hsv[1]; |
| var v = this._hsv[2]; |
| var alpha = this._hsv[3]; |
| |
| // Where to show the little circle that displays your current selected color. |
| var dragX = s * this.dragWidth; |
| var dragY = this.dragHeight - (v * this.dragHeight); |
| |
| dragX = Math.max(-this._colorDragElementHeight, |
| Math.min(this.dragWidth - this._colorDragElementHeight, dragX - this._colorDragElementHeight)); |
| dragY = Math.max(-this._colorDragElementHeight, |
| Math.min(this.dragHeight - this._colorDragElementHeight, dragY - this._colorDragElementHeight)); |
| |
| this._colorDragElement.positionAt(dragX, dragY); |
| |
| // Where to show the bar that displays your current selected hue. |
| var hueSlideX = (1 - h) * this._hueAlphaWidth - this.slideHelperWidth; |
| this._hueSlider.style.left = hueSlideX + "px"; |
| var alphaSlideX = alpha * this._hueAlphaWidth - this.slideHelperWidth; |
| this._alphaSlider.style.left = alphaSlideX + "px"; |
| }, |
| |
| _updateInput: function() |
| { |
| var cf = WebInspector.Color.Format; |
| if (this._colorFormat === cf.HEX || this._colorFormat === cf.ShortHEX || this._colorFormat === cf.Nickname) { |
| this._hexContainer.hidden = false; |
| this._displayContainer.hidden = true; |
| if (this._colorFormat === cf.ShortHEX) |
| this._hexValue.value = this._color().asString(this._color().detectHEXFormat()); |
| else |
| // Don't use short HEX if original was not in that format. |
| this._hexValue.value = this._color().asString(this._color().hasAlpha() ? cf.HEXA : cf.HEX); |
| } else { |
| // RGBA, HSLA display. |
| this._hexContainer.hidden = true; |
| this._displayContainer.hidden = false; |
| var isRgb = this._colorFormat === cf.RGB; |
| this._textLabels.textContent = isRgb ? "RGBA" : "HSLA"; |
| var colorValues = isRgb ? this._color().canonicalRGBA() : this._color().canonicalHSLA(); |
| for (var i = 0; i < 3; ++i) { |
| this._textValues[i].value = colorValues[i]; |
| if (!isRgb && (i === 1 || i === 2)) |
| this._textValues[i].value += "%"; |
| } |
| this._textValues[3].value = Math.round(colorValues[3] * 100) / 100; |
| } |
| }, |
| |
| /** |
| * @param {number} requiredContrast |
| */ |
| _drawContrastRatioLine: function(requiredContrast) |
| { |
| if (!this._contrastColor || !this.dragWidth || !this.dragHeight) |
| return; |
| |
| /** const */ var width = this.dragWidth; |
| /** const */ var height = this.dragHeight; |
| /** const */ var dS = 0.02; |
| /** const */ var epsilon = 0.002; |
| /** const */ var H = 0; |
| /** const */ var S = 1; |
| /** const */ var V = 2; |
| /** const */ var A = 3; |
| |
| var fgRGBA = []; |
| WebInspector.Color.hsva2rgba(this._hsv, fgRGBA); |
| var fgLuminance = WebInspector.Color.luminance(fgRGBA); |
| var bgRGBA = this._contrastColor.rgba(); |
| var bgLuminance = WebInspector.Color.luminance(bgRGBA); |
| var fgIsLighter = fgLuminance > bgLuminance; |
| var desiredLuminance = WebInspector.Color.desiredLuminance(bgLuminance, requiredContrast, fgIsLighter); |
| |
| var lastV = this._hsv[V]; |
| var currentSlope = 0; |
| var candidateHSVA = [this._hsv[H], 0, 0, this._hsv[A]]; |
| var pathBuilder = []; |
| var candidateRGBA = []; |
| WebInspector.Color.hsva2rgba(candidateHSVA, candidateRGBA); |
| var blendedRGBA = []; |
| WebInspector.Color.blendColors(candidateRGBA, bgRGBA, blendedRGBA); |
| |
| /** |
| * Approach the desired contrast ratio by modifying the given component |
| * from the given starting value. |
| * @param {number} index |
| * @param {number} x |
| * @param {boolean} onAxis |
| * @return {?number} |
| */ |
| function approach(index, x, onAxis) |
| { |
| while (0 <= x && x <= 1) { |
| candidateHSVA[index] = x; |
| WebInspector.Color.hsva2rgba(candidateHSVA, candidateRGBA); |
| WebInspector.Color.blendColors(candidateRGBA, bgRGBA, blendedRGBA); |
| var fgLuminance = WebInspector.Color.luminance(blendedRGBA); |
| var dLuminance = fgLuminance - desiredLuminance; |
| |
| if (Math.abs(dLuminance) < (onAxis ? epsilon / 10 : epsilon)) |
| return x; |
| else |
| x += (index === V ? -dLuminance : dLuminance); |
| } |
| return null; |
| } |
| |
| for (var s = 0; s < 1 + dS; s += dS) { |
| s = Math.min(1, s); |
| candidateHSVA[S] = s; |
| |
| var v = lastV; |
| v = lastV + currentSlope * dS; |
| |
| v = approach(V, v, s === 0); |
| if (v === null) |
| break; |
| |
| currentSlope = (v - lastV) / dS; |
| |
| pathBuilder.push(pathBuilder.length ? "L" : "M"); |
| pathBuilder.push(s * width); |
| pathBuilder.push((1 - v) * height); |
| } |
| |
| if (s < 1 + dS) { |
| s -= dS; |
| candidateHSVA[V] = 1; |
| s = approach(S, s, true); |
| if (s !== null) |
| pathBuilder = pathBuilder.concat(["L", s * width, -1]) |
| } |
| |
| this._contrastRatioLine.setAttribute("d", pathBuilder.join(" ")); |
| }, |
| |
| _updateUI: function() |
| { |
| var h = WebInspector.Color.fromHSVA([this._hsv[0], 1, 1, 1]); |
| this._colorElement.style.backgroundColor = /** @type {string} */ (h.asString(WebInspector.Color.Format.RGB)); |
| if (Runtime.experiments.isEnabled("colorContrastRatio")) { |
| // TODO(samli): Determine size of text and switch between AA/AAA ratings. |
| this._drawContrastRatioLine(4.5); |
| } |
| this._swatchInnerElement.style.backgroundColor = /** @type {string} */ (this._color().asString(WebInspector.Color.Format.RGBA)); |
| // Show border if the swatch is white. |
| this._swatchInnerElement.classList.toggle("swatch-inner-white", this._color().hsla()[2] > 0.9); |
| this._colorDragElement.style.backgroundColor = /** @type {string} */ (this._color().asString(WebInspector.Color.Format.RGBA)); |
| var noAlpha = WebInspector.Color.fromHSVA(this._hsv.slice(0,3).concat(1)); |
| this._alphaElementBackground.style.backgroundImage = String.sprintf("linear-gradient(to right, rgba(0,0,0,0), %s)", noAlpha.asString(WebInspector.Color.Format.RGB)); |
| }, |
| |
| _formatViewSwitch: function() |
| { |
| var cf = WebInspector.Color.Format; |
| var format = cf.RGB; |
| if (this._colorFormat === cf.RGB) |
| format = cf.HSL; |
| else if (this._colorFormat === cf.HSL) |
| format = this._originalFormat === cf.ShortHEX ? cf.ShortHEX : cf.HEX; |
| this._innerSetColor(undefined, "", format, WebInspector.Spectrum._ChangeSource.Other); |
| }, |
| |
| /** |
| * @param {!Event} event |
| */ |
| _inputChanged: function(event) |
| { |
| /** |
| * @param {!Element} element |
| * @return {string} |
| */ |
| function elementValue(element) |
| { |
| return element.value; |
| } |
| |
| var inputElement = /** @type {!Element} */(event.currentTarget); |
| var arrowKeyOrMouseWheelEvent = (event.key === "ArrowUp" || event.key === "ArrowDown" || event.type === "mousewheel"); |
| var pageKeyPressed = (event.key === "PageUp" || event.key === "PageDown"); |
| if (arrowKeyOrMouseWheelEvent || pageKeyPressed) { |
| var newValue = WebInspector.createReplacementString(inputElement.value, event); |
| if (newValue) { |
| inputElement.value = newValue; |
| inputElement.selectionStart = 0; |
| inputElement.selectionEnd = newValue.length; |
| } |
| event.consume(true); |
| } |
| |
| const cf = WebInspector.Color.Format; |
| var colorString; |
| if (this._colorFormat === cf.HEX || this._colorFormat === cf.ShortHEX) { |
| colorString = this._hexValue.value; |
| } else { |
| var format = this._colorFormat === cf.RGB ? "rgba" : "hsla"; |
| var values = this._textValues.map(elementValue).join(","); |
| colorString = String.sprintf("%s(%s)", format, values); |
| } |
| |
| var color = WebInspector.Color.parse(colorString); |
| if (!color) |
| return; |
| var hsv = color.hsva(); |
| if (this._colorFormat === cf.HEX || this._colorFormat === cf.ShortHEX) |
| this._colorFormat = color.detectHEXFormat(); |
| this._innerSetColor(hsv, colorString, undefined, WebInspector.Spectrum._ChangeSource.Input); |
| }, |
| |
| wasShown: function() |
| { |
| this._hueAlphaWidth = this._hueElement.offsetWidth; |
| this.slideHelperWidth = this._hueSlider.offsetWidth / 2; |
| this.dragWidth = this._colorElement.offsetWidth; |
| this.dragHeight = this._colorElement.offsetHeight; |
| this._colorDragElementHeight = this._colorDragElement.offsetHeight / 2; |
| this._innerSetColor(undefined, undefined, undefined, WebInspector.Spectrum._ChangeSource.Model); |
| this._toggleColorPicker(true); |
| WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.ColorPicked, this._colorPicked, this); |
| }, |
| |
| willHide: function() |
| { |
| this._toggleColorPicker(false); |
| WebInspector.targetManager.removeModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.ColorPicked, this._colorPicked, this); |
| }, |
| |
| /** |
| * @param {boolean=} enabled |
| * @param {!WebInspector.Event=} event |
| */ |
| _toggleColorPicker: function(enabled, event) |
| { |
| if (enabled === undefined) |
| enabled = !this._colorPickerButton.toggled(); |
| this._colorPickerButton.setToggled(enabled); |
| for (var target of WebInspector.targetManager.targets()) |
| target.pageAgent().setColorPickerEnabled(enabled); |
| }, |
| |
| /** |
| * @param {!WebInspector.Event} event |
| */ |
| _colorPicked: function(event) |
| { |
| var rgbColor = /** @type {!DOMAgent.RGBA} */ (event.data); |
| var rgba = [rgbColor.r, rgbColor.g, rgbColor.b, (rgbColor.a / 2.55 | 0) / 100]; |
| var color = WebInspector.Color.fromRGBA(rgba); |
| this._innerSetColor(color.hsva(), "", undefined, WebInspector.Spectrum._ChangeSource.Other); |
| InspectorFrontendHost.bringToFront(); |
| }, |
| |
| |
| __proto__: WebInspector.VBox.prototype |
| } |
| |
| /** @typedef {{ title: string, colors: !Array.<string>, mutable: boolean }} */ |
| WebInspector.Spectrum.Palette; |
| WebInspector.Spectrum.GeneratedPaletteTitle = "Page colors"; |
| |
| /** |
| * @constructor |
| * @param {function(!WebInspector.Spectrum.Palette)} callback |
| */ |
| WebInspector.Spectrum.PaletteGenerator = function(callback) |
| { |
| this._callback = callback; |
| /** @type {!Map.<string, number>} */ |
| this._frequencyMap = new Map(); |
| var stylesheetPromises = []; |
| for (var target of WebInspector.targetManager.targets(WebInspector.Target.Type.Page)) { |
| var cssModel = WebInspector.CSSModel.fromTarget(target); |
| for (var stylesheet of cssModel.allStyleSheets()) |
| stylesheetPromises.push(new Promise(this._processStylesheet.bind(this, stylesheet))); |
| } |
| Promise.all(stylesheetPromises) |
| .catchException(null) |
| .then(this._finish.bind(this)); |
| } |
| |
| WebInspector.Spectrum.PaletteGenerator.prototype = { |
| /** |
| * @param {string} a |
| * @param {string} b |
| * @return {number} |
| */ |
| _frequencyComparator: function(a, b) |
| { |
| return this._frequencyMap.get(b) - this._frequencyMap.get(a); |
| }, |
| |
| _finish: function() |
| { |
| /** |
| * @param {string} a |
| * @param {string} b |
| * @return {number} |
| */ |
| function hueComparator(a, b) |
| { |
| var hsva = paletteColors.get(a).hsva(); |
| var hsvb = paletteColors.get(b).hsva(); |
| |
| // First trim the shades of gray |
| if (hsvb[1] < 0.12 && hsva[1] < 0.12) |
| return hsvb[2] * hsvb[3] - hsva[2] * hsva[3]; |
| if (hsvb[1] < 0.12) |
| return -1; |
| if (hsva[1] < 0.12) |
| return 1; |
| |
| // Equal hue -> sort by sat |
| if (hsvb[0] === hsva[0]) |
| return hsvb[1] * hsvb[3] - hsva[1] * hsva[3]; |
| |
| return (hsvb[0] + 0.94) % 1 - (hsva[0] + 0.94) % 1; |
| } |
| |
| var colors = this._frequencyMap.keysArray(); |
| colors = colors.sort(this._frequencyComparator.bind(this)); |
| /** @type {!Map.<string, !WebInspector.Color>} */ |
| var paletteColors = new Map(); |
| var colorsPerRow = 24; |
| while (paletteColors.size < colorsPerRow && colors.length) { |
| var colorText = colors.shift(); |
| var color = WebInspector.Color.parse(colorText); |
| if (!color || color.nickname() === "white" || color.nickname() === "black") |
| continue; |
| paletteColors.set(colorText, color); |
| } |
| |
| this._callback({ title: WebInspector.Spectrum.GeneratedPaletteTitle, colors: paletteColors.keysArray().sort(hueComparator), mutable: false }); |
| }, |
| |
| /** |
| * @param {!WebInspector.CSSStyleSheetHeader} stylesheet |
| * @param {function(?)} resolve |
| * @this {WebInspector.Spectrum.PaletteGenerator} |
| */ |
| _processStylesheet: function(stylesheet, resolve) |
| { |
| /** |
| * @param {?string} text |
| * @this {WebInspector.Spectrum.PaletteGenerator} |
| */ |
| function parseContent(text) |
| { |
| text = text.toLowerCase(); |
| var regexResult = text.match(/((?:rgb|hsl)a?\([^)]+\)|#[0-9a-f]{6}|#[0-9a-f]{3})/g) || []; |
| for (var c of regexResult) { |
| var frequency = this._frequencyMap.get(c) || 0; |
| this._frequencyMap.set(c, ++frequency); |
| } |
| resolve(null); |
| } |
| |
| stylesheet.requestContent().then(parseContent.bind(this)); |
| } |
| } |
| |
| WebInspector.Spectrum.MaterialPaletteShades = { |
| "#F44336": ["#FFEBEE", "#FFCDD2", "#EF9A9A", "#E57373", "#EF5350", "#F44336", "#E53935", "#D32F2F", "#C62828", "#B71C1C"], |
| "#E91E63": ["#FCE4EC", "#F8BBD0", "#F48FB1", "#F06292", "#EC407A", "#E91E63", "#D81B60", "#C2185B", "#AD1457", "#880E4F"], |
| "#9C27B0": ["#F3E5F5", "#E1BEE7", "#CE93D8", "#BA68C8", "#AB47BC", "#9C27B0", "#8E24AA", "#7B1FA2", "#6A1B9A", "#4A148C"], |
| "#673AB7": ["#EDE7F6", "#D1C4E9", "#B39DDB", "#9575CD", "#7E57C2", "#673AB7", "#5E35B1", "#512DA8", "#4527A0", "#311B92"], |
| "#3F51B5": ["#E8EAF6", "#C5CAE9", "#9FA8DA", "#7986CB", "#5C6BC0", "#3F51B5", "#3949AB", "#303F9F", "#283593", "#1A237E"], |
| "#2196F3": ["#E3F2FD", "#BBDEFB", "#90CAF9", "#64B5F6", "#42A5F5", "#2196F3", "#1E88E5", "#1976D2", "#1565C0", "#0D47A1"], |
| "#03A9F4": ["#E1F5FE", "#B3E5FC", "#81D4FA", "#4FC3F7", "#29B6F6", "#03A9F4", "#039BE5", "#0288D1", "#0277BD", "#01579B"], |
| "#00BCD4": ["#E0F7FA", "#B2EBF2", "#80DEEA", "#4DD0E1", "#26C6DA", "#00BCD4", "#00ACC1", "#0097A7", "#00838F", "#006064"], |
| "#009688": ["#E0F2F1", "#B2DFDB", "#80CBC4", "#4DB6AC", "#26A69A", "#009688", "#00897B", "#00796B", "#00695C", "#004D40"], |
| "#4CAF50": ["#E8F5E9", "#C8E6C9", "#A5D6A7", "#81C784", "#66BB6A", "#4CAF50", "#43A047", "#388E3C", "#2E7D32", "#1B5E20"], |
| "#8BC34A": ["#F1F8E9", "#DCEDC8", "#C5E1A5", "#AED581", "#9CCC65", "#8BC34A", "#7CB342", "#689F38", "#558B2F", "#33691E"], |
| "#CDDC39": ["#F9FBE7", "#F0F4C3", "#E6EE9C", "#DCE775", "#D4E157", "#CDDC39", "#C0CA33", "#AFB42B", "#9E9D24", "#827717"], |
| "#FFEB3B": ["#FFFDE7", "#FFF9C4", "#FFF59D", "#FFF176", "#FFEE58", "#FFEB3B", "#FDD835", "#FBC02D", "#F9A825", "#F57F17"], |
| "#FFC107": ["#FFF8E1", "#FFECB3", "#FFE082", "#FFD54F", "#FFCA28", "#FFC107", "#FFB300", "#FFA000", "#FF8F00", "#FF6F00"], |
| "#FF9800": ["#FFF3E0", "#FFE0B2", "#FFCC80", "#FFB74D", "#FFA726", "#FF9800", "#FB8C00", "#F57C00", "#EF6C00", "#E65100"], |
| "#FF5722": ["#FBE9E7", "#FFCCBC", "#FFAB91", "#FF8A65", "#FF7043", "#FF5722", "#F4511E", "#E64A19", "#D84315", "#BF360C"], |
| "#795548": ["#EFEBE9", "#D7CCC8", "#BCAAA4", "#A1887F", "#8D6E63", "#795548", "#6D4C41", "#5D4037", "#4E342E", "#3E2723"], |
| "#9E9E9E": ["#FAFAFA", "#F5F5F5", "#EEEEEE", "#E0E0E0", "#BDBDBD", "#9E9E9E", "#757575", "#616161", "#424242", "#212121"], |
| "#607D8B": ["#ECEFF1", "#CFD8DC", "#B0BEC5", "#90A4AE", "#78909C", "#607D8B", "#546E7A", "#455A64", "#37474F", "#263238"] |
| }; |
| |
| WebInspector.Spectrum.MaterialPalette = { title: "Material", mutable: false, matchUserFormat: true, colors: Object.keys(WebInspector.Spectrum.MaterialPaletteShades) }; |