| // Copyright (c) 2012 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. |
| |
| /** |
| * @fileoverview Provides dialog-like behaviors for the tracing UI. |
| */ |
| cr.define('cr.ui.overlay', function() { |
| /** |
| * Gets the top, visible overlay. It makes the assumption that if multiple |
| * overlays are visible, the last in the byte order is topmost. |
| * TODO(estade): rely on aria-visibility instead? |
| * @return {HTMLElement} The overlay. |
| */ |
| function getTopOverlay() { |
| var overlays = /** @type !NodeList<!HTMLElement> */( |
| document.querySelectorAll('.overlay:not([hidden])')); |
| return overlays[overlays.length - 1]; |
| } |
| |
| /** |
| * Returns a visible default button of the overlay, if it has one. If the |
| * overlay has more than one, the first one will be returned. |
| * |
| * @param {HTMLElement} overlay The .overlay. |
| * @return {HTMLElement} The default button. |
| */ |
| function getDefaultButton(overlay) { |
| function isHidden(node) { return node.hidden; } |
| var defaultButtons = /** @type !NodeList<!HTMLElement> */( |
| overlay.querySelectorAll('.page .button-strip > .default-button')); |
| for (var i = 0; i < defaultButtons.length; i++) { |
| if (!findAncestor(defaultButtons[i], isHidden)) |
| return defaultButtons[i]; |
| } |
| return null; |
| } |
| |
| /** @type {boolean} */ |
| var globallyInitialized = false; |
| |
| /** |
| * Makes initializations which must hook at the document level. |
| */ |
| function globalInitialization() { |
| if (!globallyInitialized) { |
| document.addEventListener('keydown', function(e) { |
| var overlay = getTopOverlay(); |
| if (!overlay) |
| return; |
| |
| // Close the overlay on escape. |
| if (e.key == 'Escape') |
| cr.dispatchSimpleEvent(overlay, 'cancelOverlay'); |
| |
| // Execute the overlay's default button on enter, unless focus is on an |
| // element that has standard behavior for the enter key. |
| var forbiddenTagNames = /^(A|BUTTON|SELECT|TEXTAREA)$/; |
| if (e.key == 'Enter' && |
| !forbiddenTagNames.test(document.activeElement.tagName)) { |
| var button = getDefaultButton(overlay); |
| if (button) { |
| button.click(); |
| // Executing the default button may result in focus moving to a |
| // different button. Calling preventDefault is necessary to not have |
| // that button execute as well. |
| e.preventDefault(); |
| } |
| } |
| }); |
| |
| window.addEventListener('resize', setMaxHeightAllPages); |
| globallyInitialized = true; |
| } |
| |
| setMaxHeightAllPages(); |
| } |
| |
| /** |
| * Sets the max-height of all pages in all overlays, based on the window |
| * height. |
| */ |
| function setMaxHeightAllPages() { |
| var pages = document.querySelectorAll( |
| '.overlay .page:not(.not-resizable)'); |
| |
| var maxHeight = Math.min(0.9 * window.innerHeight, 640) + 'px'; |
| for (var i = 0; i < pages.length; i++) |
| pages[i].style.maxHeight = maxHeight; |
| } |
| |
| /** |
| * Adds behavioral hooks for the given overlay. |
| * @param {HTMLElement} overlay The .overlay. |
| */ |
| function setupOverlay(overlay) { |
| // Close the overlay on clicking any of the pages' close buttons. |
| var closeButtons = overlay.querySelectorAll('.page > .close-button'); |
| for (var i = 0; i < closeButtons.length; i++) { |
| closeButtons[i].addEventListener('click', function(e) { |
| if (cr.ui.FocusOutlineManager) |
| cr.ui.FocusOutlineManager.forDocument(document).updateVisibility(); |
| cr.dispatchSimpleEvent(overlay, 'cancelOverlay'); |
| }); |
| } |
| |
| // Remove the 'pulse' animation any time the overlay is hidden or shown. |
| overlay.__defineSetter__('hidden', function(value) { |
| this.classList.remove('pulse'); |
| if (value) |
| this.setAttribute('hidden', true); |
| else |
| this.removeAttribute('hidden'); |
| }); |
| overlay.__defineGetter__('hidden', function() { |
| return this.hasAttribute('hidden'); |
| }); |
| |
| // Shake when the user clicks away. |
| overlay.addEventListener('click', function(e) { |
| // Only pulse if the overlay was the target of the click. |
| if (this != e.target) |
| return; |
| |
| // This may be null while the overlay is closing. |
| var overlayPage = this.querySelector('.page:not([hidden])'); |
| if (overlayPage) |
| overlayPage.classList.add('pulse'); |
| }); |
| overlay.addEventListener('webkitAnimationEnd', function(e) { |
| e.target.classList.remove('pulse'); |
| }); |
| } |
| |
| return { |
| getDefaultButton: getDefaultButton, |
| globalInitialization: globalInitialization, |
| setupOverlay: setupOverlay, |
| }; |
| }); |