/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; /** * This is a CSS Filter Editor widget used * for Rule View's filter swatches */ const EventEmitter = require("devtools/shared/event-emitter"); const { Cc, Ci } = require("chrome"); const XHTML_NS = "http://www.w3.org/1999/xhtml"; const { LocalizationHelper } = require("devtools/shared/l10n"); const STRINGS_URI = "devtools/client/locales/filterwidget.properties"; const L10N = new LocalizationHelper(STRINGS_URI); const {cssTokenizer} = require("devtools/shared/css/parsing-utils"); const asyncStorage = require("devtools/shared/async-storage"); loader.lazyGetter(this, "DOMUtils", () => { return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils); }); const DEFAULT_FILTER_TYPE = "length"; const UNIT_MAPPING = { percentage: "%", length: "px", angle: "deg", string: "" }; const FAST_VALUE_MULTIPLIER = 10; const SLOW_VALUE_MULTIPLIER = 0.1; const DEFAULT_VALUE_MULTIPLIER = 1; const LIST_PADDING = 7; const LIST_ITEM_HEIGHT = 32; const filterList = [ { "name": "blur", "range": [0, Infinity], "type": "length" }, { "name": "brightness", "range": [0, Infinity], "type": "percentage" }, { "name": "contrast", "range": [0, Infinity], "type": "percentage" }, { "name": "drop-shadow", "placeholder": L10N.getStr("dropShadowPlaceholder"), "type": "string" }, { "name": "grayscale", "range": [0, 100], "type": "percentage" }, { "name": "hue-rotate", "range": [0, Infinity], "type": "angle" }, { "name": "invert", "range": [0, 100], "type": "percentage" }, { "name": "opacity", "range": [0, 100], "type": "percentage" }, { "name": "saturate", "range": [0, Infinity], "type": "percentage" }, { "name": "sepia", "range": [0, 100], "type": "percentage" }, { "name": "url", "placeholder": "example.svg#c1", "type": "string" } ]; // Valid values that shouldn't be parsed for filters. const SPECIAL_VALUES = new Set(["none", "unset", "initial", "inherit"]); /** * A CSS Filter editor widget used to add/remove/modify * filters. * * Normally, it takes a CSS filter value as input, parses it * and creates the required elements / bindings. * * You can, however, use add/remove/update methods manually. * See each method's comments for more details * * @param {nsIDOMNode} el * The widget container. * @param {String} value * CSS filter value * @param {Function} cssIsValid * Test whether css name / value is valid. */ function CSSFilterEditorWidget(el, value = "", cssIsValid) { this.doc = el.ownerDocument; this.win = this.doc.defaultView; this.el = el; this._cssIsValid = cssIsValid; this._addButtonClick = this._addButtonClick.bind(this); this._removeButtonClick = this._removeButtonClick.bind(this); this._mouseMove = this._mouseMove.bind(this); this._mouseUp = this._mouseUp.bind(this); this._mouseDown = this._mouseDown.bind(this); this._keyDown = this._keyDown.bind(this); this._input = this._input.bind(this); this._presetClick = this._presetClick.bind(this); this._savePreset = this._savePreset.bind(this); this._togglePresets = this._togglePresets.bind(this); this._resetFocus = this._resetFocus.bind(this); // Passed to asyncStorage, requires binding this.renderPresets = this.renderPresets.bind(this); this._initMarkup(); this._buildFilterItemMarkup(); this._buildPresetItemMarkup(); this._addEventListeners(); EventEmitter.decorate(this); this.filters = []; this.setCssValue(value); this.renderPresets(); } exports.CSSFilterEditorWidget = CSSFilterEditorWidget; CSSFilterEditorWidget.prototype = { _initMarkup: function () { let filterListSelectPlaceholder = L10N.getStr("filterListSelectPlaceholder"); let addNewFilterButton = L10N.getStr("addNewFilterButton"); let presetsToggleButton = L10N.getStr("presetsToggleButton"); let newPresetPlaceholder = L10N.getStr("newPresetPlaceholder"); let savePresetButton = L10N.getStr("savePresetButton"); this.el.innerHTML = `
`; this.filtersList = this.el.querySelector("#filters"); this.presetsList = this.el.querySelector("#presets"); this.togglePresets = this.el.querySelector("#toggle-presets"); this.filterSelect = this.el.querySelector("select"); this.addPresetButton = this.el.querySelector(".presets-list .add"); this.addPresetInput = this.el.querySelector(".presets-list .footer input"); this.el.querySelector(".presets-list input").value = ""; this._populateFilterSelect(); }, _destroyMarkup: function () { this._filterItemMarkup.remove(); this.el.remove(); this.el = this.filtersList = this._filterItemMarkup = null; this.presetsList = this.togglePresets = this.filterSelect = null; this.addPresetButton = null; }, destroy: function () { this._removeEventListeners(); this._destroyMarkup(); }, /** * Creates