| // 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 = document.querySelectorAll('.overlay:not([hidden])'); |
| return overlays[overlays.length - 1]; |
| } |
| |
| /** |
| * Makes initializations which must hook at the document level. |
| */ |
| function globalInitialization() { |
| // Listen to focus events and make sure focus doesn't move outside of a |
| // visible overlay .page. |
| document.addEventListener('focus', function(e) { |
| var overlay = getTopOverlay(); |
| var page = overlay ? overlay.querySelector('.page:not([hidden])') : null; |
| if (!page || page.contains(e.target)) |
| return; |
| |
| var focusElement = page.querySelector('button, input, list, select, a'); |
| if (focusElement) |
| focusElement.focus(); |
| }, true); |
| |
| // Close the overlay on escape. |
| document.addEventListener('keydown', function(e) { |
| if (e.keyCode == 27) { // Escape |
| var overlay = getTopOverlay(); |
| if (!overlay) |
| return; |
| |
| cr.dispatchSimpleEvent(overlay, 'cancelOverlay'); |
| } |
| }); |
| |
| window.addEventListener('resize', setMaxHeightAllPages); |
| |
| setMaxHeightAllPages(); |
| } |
| |
| /** |
| * Sets the max-height of all pages in all overlays, based on the window |
| * height. |
| */ |
| function setMaxHeightAllPages() { |
| var pages = document.querySelectorAll('.overlay .page'); |
| |
| 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) { |
| 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 { |
| globalInitialization: globalInitialization, |
| setupOverlay: setupOverlay, |
| }; |
| }); |
| |
| document.addEventListener('DOMContentLoaded', |
| cr.ui.overlay.globalInitialization); |