| // Copyright (c) 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import * as i18n from '../i18n/i18n.js'; |
| import * as SDK from '../sdk/sdk.js'; |
| import * as UI from '../ui/ui.js'; |
| |
| import {ElementsPanel} from './ElementsPanel.js'; |
| |
| export const UIStrings = { |
| /** |
| *@description Text of a DOM element in Element State Pane Widget of the Elements panel |
| */ |
| forceElementState: 'Force element state', |
| /** |
| *@description Text in Element State Pane Widget of the Elements panel |
| */ |
| toggleElementState: 'Toggle Element State', |
| /** |
| *@description Text in Element State Pane Widget of the Elements panel |
| */ |
| hov: ':hov', |
| }; |
| const str_ = i18n.i18n.registerUIStrings('elements/ElementStatePaneWidget.js', UIStrings); |
| const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); |
| export class ElementStatePaneWidget extends UI.Widget.Widget { |
| constructor() { |
| super(true); |
| this.registerRequiredCSS('elements/elementStatePaneWidget.css', {enableLegacyPatching: true}); |
| this.contentElement.className = 'styles-element-state-pane'; |
| UI.UIUtils.createTextChild(this.contentElement.createChild('div'), i18nString(UIStrings.forceElementState)); |
| const table = document.createElement('table'); |
| table.classList.add('source-code'); |
| UI.ARIAUtils.markAsPresentation(table); |
| |
| /** @type {!Array<!HTMLInputElement>} */ |
| const inputs = []; |
| this._inputs = inputs; |
| |
| /** @type {!WeakMap<!HTMLInputElement,string>} */ |
| this._inputStates = new WeakMap(); |
| |
| /** |
| * @param {!MouseEvent} event |
| */ |
| const clickListener = event => { |
| const node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode); |
| if (!node || !(event.target instanceof HTMLInputElement)) { |
| return; |
| } |
| const state = this._inputStates.get(event.target); |
| if (!state) { |
| return; |
| } |
| node.domModel().cssModel().forcePseudoState(node, state, event.target.checked); |
| }; |
| |
| /** |
| * @param {string} state |
| * @return {!Element} |
| */ |
| const createCheckbox = state => { |
| const td = document.createElement('td'); |
| const label = UI.UIUtils.CheckboxLabel.create(':' + state); |
| const input = label.checkboxElement; |
| this._inputStates.set(input, state); |
| input.addEventListener('click', /** @type {!EventListener} */ (clickListener), false); |
| inputs.push(input); |
| td.appendChild(label); |
| return td; |
| }; |
| |
| let tr = table.createChild('tr'); |
| tr.appendChild(createCheckbox('active')); |
| tr.appendChild(createCheckbox('hover')); |
| |
| tr = table.createChild('tr'); |
| tr.appendChild(createCheckbox('focus')); |
| tr.appendChild(createCheckbox('visited')); |
| |
| tr = table.createChild('tr'); |
| tr.appendChild(createCheckbox('focus-within')); |
| tr.appendChild(createCheckbox('focus-visible')); |
| |
| tr = table.createChild('tr'); |
| tr.appendChild(createCheckbox('target')); |
| |
| this.contentElement.appendChild(table); |
| UI.Context.Context.instance().addFlavorChangeListener(SDK.DOMModel.DOMNode, this._update, this); |
| } |
| |
| /** |
| * @param {?SDK.CSSModel.CSSModel} cssModel |
| */ |
| _updateModel(cssModel) { |
| if (this._cssModel === cssModel) { |
| return; |
| } |
| if (this._cssModel) { |
| this._cssModel.removeEventListener(SDK.CSSModel.Events.PseudoStateForced, this._update, this); |
| } |
| /** @type {?SDK.CSSModel.CSSModel} */ |
| this._cssModel = cssModel; |
| if (this._cssModel) { |
| this._cssModel.addEventListener(SDK.CSSModel.Events.PseudoStateForced, this._update, this); |
| } |
| } |
| |
| /** |
| * @override |
| */ |
| wasShown() { |
| this._update(); |
| } |
| |
| _update() { |
| if (!this.isShowing()) { |
| return; |
| } |
| |
| let node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode); |
| if (node) { |
| node = node.enclosingElementOrSelf(); |
| } |
| |
| this._updateModel(node ? node.domModel().cssModel() : null); |
| if (node) { |
| const nodePseudoState = node.domModel().cssModel().pseudoState(node); |
| for (const input of this._inputs) { |
| input.disabled = Boolean(node.pseudoType()); |
| const state = this._inputStates.get(input); |
| input.checked = nodePseudoState && state !== undefined ? nodePseudoState.indexOf(state) >= 0 : false; |
| } |
| } else { |
| for (const input of this._inputs) { |
| input.disabled = true; |
| input.checked = false; |
| } |
| } |
| } |
| } |
| |
| /** @type {!ButtonProvider} */ |
| let buttonProviderInstance; |
| |
| /** |
| * @implements {UI.Toolbar.Provider} |
| */ |
| export class ButtonProvider { |
| /** @private */ |
| constructor() { |
| this._button = new UI.Toolbar.ToolbarToggle(i18nString(UIStrings.toggleElementState), ''); |
| this._button.setText(i18nString(UIStrings.hov)); |
| this._button.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this._clicked, this); |
| this._button.element.classList.add('monospace'); |
| this._view = new ElementStatePaneWidget(); |
| } |
| |
| /** |
| * @param {{forceNew: ?boolean}} opts |
| */ |
| static instance(opts = {forceNew: null}) { |
| const {forceNew} = opts; |
| if (!buttonProviderInstance || forceNew) { |
| buttonProviderInstance = new ButtonProvider(); |
| } |
| |
| return buttonProviderInstance; |
| } |
| |
| _clicked() { |
| ElementsPanel.instance().showToolbarPane(!this._view.isShowing() ? this._view : null, this._button); |
| } |
| |
| /** |
| * @override |
| * @return {!UI.Toolbar.ToolbarItem} |
| */ |
| item() { |
| return this._button; |
| } |
| } |