| /* |
| * Copyright (C) 2012 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * 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. |
| * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT |
| * OWNER OR 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. |
| */ |
| |
| export default class Dialog extends UI.GlassPane { |
| constructor() { |
| super(); |
| this.registerRequiredCSS('ui/dialog.css'); |
| this.contentElement.tabIndex = 0; |
| this.contentElement.addEventListener('focus', () => this.widget().focus(), false); |
| this.widget().setDefaultFocusedElement(this.contentElement); |
| this.setPointerEventsBehavior(UI.GlassPane.PointerEventsBehavior.BlockedByGlassPane); |
| this.setOutsideClickCallback(event => { |
| this.hide(); |
| event.consume(true); |
| }); |
| UI.ARIAUtils.markAsModalDialog(this.contentElement); |
| /** @type {!UI.Dialog.OutsideTabIndexBehavior} */ |
| this._tabIndexBehavior = OutsideTabIndexBehavior.DisableAllOutsideTabIndex; |
| /** @type {!Map<!HTMLElement, number>} */ |
| this._tabIndexMap = new Map(); |
| /** @type {?UI.WidgetFocusRestorer} */ |
| this._focusRestorer = null; |
| this._closeOnEscape = true; |
| /** @type {?Document} */ |
| this._targetDocument; |
| this._targetDocumentKeyDownHandler = this._onKeyDown.bind(this); |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| static hasInstance() { |
| return !!UI.Dialog._instance; |
| } |
| |
| /** |
| * @override |
| * @param {!Document|!Element=} where |
| */ |
| show(where) { |
| const document = /** @type {!Document} */ ( |
| where instanceof Document ? where : (where || UI.inspectorView.element).ownerDocument); |
| this._targetDocument = document; |
| this._targetDocument.addEventListener('keydown', this._targetDocumentKeyDownHandler, true); |
| |
| if (UI.Dialog._instance) { |
| UI.Dialog._instance.hide(); |
| } |
| UI.Dialog._instance = this; |
| this._disableTabIndexOnElements(document); |
| super.show(document); |
| this._focusRestorer = new UI.WidgetFocusRestorer(this.widget()); |
| } |
| |
| /** |
| * @override |
| */ |
| hide() { |
| this._focusRestorer.restore(); |
| super.hide(); |
| |
| if (this._targetDocument) { |
| this._targetDocument.removeEventListener('keydown', this._targetDocumentKeyDownHandler, true); |
| } |
| this._restoreTabIndexOnElements(); |
| delete UI.Dialog._instance; |
| } |
| |
| /** |
| * @param {boolean} close |
| */ |
| setCloseOnEscape(close) { |
| this._closeOnEscape = close; |
| } |
| |
| addCloseButton() { |
| const closeButton = this.contentElement.createChild('div', 'dialog-close-button', 'dt-close-button'); |
| closeButton.gray = true; |
| closeButton.addEventListener('click', () => this.hide(), false); |
| } |
| |
| /** |
| * @param {!UI.Dialog.OutsideTabIndexBehavior} tabIndexBehavior |
| */ |
| setOutsideTabIndexBehavior(tabIndexBehavior) { |
| this._tabIndexBehavior = tabIndexBehavior; |
| } |
| |
| /** |
| * @param {!Document} document |
| */ |
| _disableTabIndexOnElements(document) { |
| if (this._tabIndexBehavior === OutsideTabIndexBehavior.PreserveTabIndex) { |
| return; |
| } |
| |
| let exclusionSet = /** @type {?Set.<!HTMLElement>} */ (null); |
| if (this._tabIndexBehavior === OutsideTabIndexBehavior.PreserveMainViewTabIndex) { |
| exclusionSet = this._getMainWidgetTabIndexElements(UI.inspectorView.ownerSplit()); |
| } |
| |
| this._tabIndexMap.clear(); |
| for (let node = document; node; node = node.traverseNextNode(document)) { |
| if (node instanceof HTMLElement) { |
| const element = /** @type {!HTMLElement} */ (node); |
| const tabIndex = element.tabIndex; |
| if (tabIndex >= 0 && (!exclusionSet || !exclusionSet.has(element))) { |
| this._tabIndexMap.set(element, tabIndex); |
| element.tabIndex = -1; |
| } |
| } |
| } |
| } |
| |
| /** |
| * @param {?UI.SplitWidget} splitWidget |
| * @return {!Set.<!HTMLElement>} |
| */ |
| _getMainWidgetTabIndexElements(splitWidget) { |
| const elementSet = /** @type {!Set.<!HTMLElement>} */ (new Set()); |
| if (!splitWidget) { |
| return elementSet; |
| } |
| |
| const mainWidget = splitWidget.mainWidget(); |
| if (!mainWidget || !mainWidget.element) { |
| return elementSet; |
| } |
| |
| for (let node = mainWidget.element; node; node = node.traverseNextNode(mainWidget.element)) { |
| if (!(node instanceof HTMLElement)) { |
| continue; |
| } |
| |
| const element = /** @type {!HTMLElement} */ (node); |
| const tabIndex = element.tabIndex; |
| if (tabIndex < 0) { |
| continue; |
| } |
| |
| elementSet.add(element); |
| } |
| |
| return elementSet; |
| } |
| |
| _restoreTabIndexOnElements() { |
| for (const element of this._tabIndexMap.keys()) { |
| element.tabIndex = /** @type {number} */ (this._tabIndexMap.get(element)); |
| } |
| this._tabIndexMap.clear(); |
| } |
| |
| /** |
| * @param {!Event} event |
| */ |
| _onKeyDown(event) { |
| if (this._closeOnEscape && event.keyCode === UI.KeyboardShortcut.Keys.Esc.code && |
| UI.KeyboardShortcut.hasNoModifiers(event)) { |
| event.consume(true); |
| this.hide(); |
| } |
| } |
| } |
| |
| /** @enum {symbol} */ |
| export const OutsideTabIndexBehavior = { |
| DisableAllOutsideTabIndex: Symbol('DisableAllTabIndex'), |
| PreserveMainViewTabIndex: Symbol('PreserveMainViewTabIndex'), |
| PreserveTabIndex: Symbol('PreserveTabIndex') |
| }; |
| |
| /* Legacy exported object*/ |
| self.UI = self.UI || {}; |
| |
| /* Legacy exported object*/ |
| UI = UI || {}; |
| |
| /** @constructor */ |
| UI.Dialog = Dialog; |
| |
| /** @enum {symbol} */ |
| UI.Dialog.OutsideTabIndexBehavior = OutsideTabIndexBehavior; |