1010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis// Use of this source code is governed by a BSD-style license that can be 3010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis// found in the LICENSE file. 4010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 5010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 6010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis/** 7010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * @fileoverview Implements an element that is hidden by default, but 8010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * when shown, dims and (attempts to) disable the main document. 9010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * 10010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * You can turn any div into an overlay. Note that while an 11010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * overlay element is shown, its parent is changed. Hiding the overlay 12010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * restores its original parentage. 13010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * 14010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis */ 15010583560e0e6db74fe50b840bce46ba6537de63Jamie Genniscr.define('tracing', function() { 16010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis /** 17010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * Manages a full-window div that darkens the window, disables 18010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * input, and hosts the currently-visible overlays. You shouldn't 19010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * have to instantiate this directly --- it gets set automatically. 20010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * @param {Object=} opt_propertyBag Optional properties. 21010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * @constructor 22010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * @extends {HTMLDivElement} 23010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis */ 24010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis var OverlayRoot = cr.ui.define('div'); 25010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis OverlayRoot.prototype = { 26010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis __proto__: HTMLDivElement.prototype, 27010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis decorate: function() { 28010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.classList.add('overlay-root'); 29010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.visible = false; 30010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 31010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.contentHost = this.ownerDocument.createElement('div'); 32010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.contentHost.classList.add('content-host'); 33010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 34010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.tabCatcher = this.ownerDocument.createElement('span'); 35010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.tabCatcher.tabIndex = 0; 36010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 37010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.appendChild(this.contentHost); 38010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 39010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.onKeydownBoundToThis_ = this.onKeydown_.bind(this); 40010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.onFocusInBoundToThis_ = this.onFocusIn_.bind(this); 41010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.addEventListener('mousedown', this.onMousedown_.bind(this)); 42010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis }, 43010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 44010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis /** 45010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * Adds an overlay, attaching it to the contentHost so that it is visible. 46010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis */ 47010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis showOverlay: function(overlay) { 48010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis // Reparent this to the overlay content host. 49010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis overlay.oldParent_ = overlay.parentNode; 50010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.contentHost.appendChild(overlay); 51010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.contentHost.appendChild(this.tabCatcher); 52010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 53010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis // Show the overlay root. 54010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.ownerDocument.body.classList.add('disabled-by-overlay'); 55010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.visible = true; 56010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 57010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis // Bring overlay into focus. 58010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis overlay.tabIndex = 0; 59010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis var focusElement = 60010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis overlay.querySelector('button, input, list, select, a'); 61010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis if (!focusElement) { 62010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis focusElement = overlay; 63010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis } 64010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis focusElement.focus(); 65010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 66010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis // Listen to key and focus events to prevent focus from 67010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis // leaving the overlay. 68010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.ownerDocument.addEventListener('focusin', 69010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.onFocusInBoundToThis_, true); 70010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis overlay.addEventListener('keydown', this.onKeydownBoundToThis_); 71010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis }, 72010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 73010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis /** 74010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * Clicking outside of the overlay will de-focus the overlay. The 75010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * next tab will look at the entire document to determine the focus. 76010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * For certain documents, this can cause focus to "leak" outside of 77010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * the overlay. 78010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis */ 79010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis onMousedown_: function(e) { 80010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis if (e.target == this) { 81010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis e.preventDefault(); 82010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis } 83010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis }, 84010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 85010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis /** 86010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * Prevents forward-tabbing out of the overlay 87010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis */ 88010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis onFocusIn_: function(e) { 89010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis if (e.target == this.tabCatcher) { 90010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis window.setTimeout(this.focusOverlay_.bind(this), 0); 91010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis } 92010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis }, 93010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 94010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis focusOverlay_: function() { 95010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.contentHost.firstChild.focus(); 96010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis }, 97010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 98010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis /** 99010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * Prevent the user from shift-tabbing backwards out of the overlay. 100010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis */ 101010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis onKeydown_: function(e) { 102010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis if (e.keyCode == 9 && 103010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis e.shiftKey && 104010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis e.target == this.contentHost.firstChild) { 105010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis e.preventDefault(); 106010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis } 107010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis }, 108010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 109010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis /** 110010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * Hides an overlay, attaching it to its original parent if needed. 111010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis */ 112010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis hideOverlay: function(overlay) { 113010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis // hide the overlay root 114010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.visible = false; 115010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.ownerDocument.body.classList.remove('disabled-by-overlay'); 116010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.lastFocusOut_ = undefined; 117010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 118010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis // put the overlay back on its previous parent 119010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis overlay.parentNode.removeChild(this.tabCatcher); 120010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis if (overlay.oldParent_) { 121010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis overlay.oldParent_.appendChild(overlay); 122010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis delete overlay.oldParent_; 123010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis } else { 124010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.contentHost.removeChild(overlay); 125010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis } 126010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 127010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis // remove listeners 128010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis overlay.removeEventListener('keydown', this.onKeydownBoundToThis_); 129010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.ownerDocument.removeEventListener('focusin', 130010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.onFocusInBoundToThis_); 131010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis } 132010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis }; 133010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 134010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis cr.defineProperty(OverlayRoot, 'visible', cr.PropertyKind.BOOL_ATTR); 135010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 136010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis /** 137010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * Creates a new overlay element. It will not be visible until shown. 138010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * @param {Object=} opt_propertyBag Optional properties. 139010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * @constructor 140010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * @extends {HTMLDivElement} 141010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis */ 142010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis var Overlay = cr.ui.define('div'); 143010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 144010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis Overlay.prototype = { 145010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis __proto__: HTMLDivElement.prototype, 146010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 147010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis /** 148010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * Initializes the overlay element. 149010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis */ 150010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis decorate: function() { 151010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis // create the overlay root on this document if its not present 152010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis if (!this.ownerDocument.querySelector('.overlay-root')) { 153010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis var overlayRoot = this.ownerDocument.createElement('div'); 154010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis cr.ui.decorate(overlayRoot, OverlayRoot); 155010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.ownerDocument.body.appendChild(overlayRoot); 156010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis } 157010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 158010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.classList.add('overlay'); 159010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.visible = false; 160010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis }, 161010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 162010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis onVisibleChanged_: function() { 163010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis var overlayRoot = this.ownerDocument.querySelector('.overlay-root'); 164010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis if (this.visible) { 165010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis overlayRoot.showOverlay(this); 166010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis } else { 167010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis overlayRoot.hideOverlay(this); 168010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis } 169010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis } 170010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis }; 171010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 172010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis /** 173010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * Shows and hides the overlay. Note that while visible == true, the overlay 174010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * element will be tempoarily reparented to another place in the DOM. 175010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis */ 176010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis cr.defineProperty(Overlay, 'visible', cr.PropertyKind.BOOL_ATTR, 177010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis Overlay.prototype.onVisibleChanged_); 178010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 179010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis return { 180010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis Overlay: Overlay 181010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis }; 182010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis}); 183