blob: 2e068c4950da2bf32327427ae7bb7b69c5f076bf [file] [log] [blame]
// 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,
};
});