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
566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis'use strict';
6010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
7010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis/**
8010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * @fileoverview Implements an element that is hidden by default, but
9010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * when shown, dims and (attempts to) disable the main document.
10010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis *
11010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * You can turn any div into an overlay. Note that while an
12010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * overlay element is shown, its parent is changed. Hiding the overlay
13010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * restores its original parentage.
14010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis *
15010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis */
1666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennisbase.requireStylesheet('ui.overlay');
1766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
1866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennisbase.require('base.properties');
1966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennisbase.require('base.events');
202da489cd246702bee5938545b18a6f710ed214bcJamie Gennisbase.require('ui');
2166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
2266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennisbase.exportTo('ui', function() {
23010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis  /**
24010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis   * Manages a full-window div that darkens the window, disables
25010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis   * input, and hosts the currently-visible overlays. You shouldn't
26010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis   * have to instantiate this directly --- it gets set automatically.
27010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis   * @param {Object=} opt_propertyBag Optional properties.
28010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis   * @constructor
29010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis   * @extends {HTMLDivElement}
30010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis   */
3166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis  var OverlayRoot = ui.define('div');
32010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis  OverlayRoot.prototype = {
33010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    __proto__: HTMLDivElement.prototype,
34010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    decorate: function() {
35010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      this.classList.add('overlay-root');
3666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
3766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
3866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.createToolBar_();
39010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
40010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      this.contentHost = this.ownerDocument.createElement('div');
41010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      this.contentHost.classList.add('content-host');
42010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
43010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      this.tabCatcher = this.ownerDocument.createElement('span');
44010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      this.tabCatcher.tabIndex = 0;
45010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
46010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      this.appendChild(this.contentHost);
47010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
4866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.onKeydown_ = this.onKeydown_.bind(this);
4966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.onFocusIn_ = this.onFocusIn_.bind(this);
50010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      this.addEventListener('mousedown', this.onMousedown_.bind(this));
51010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    },
52010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
5366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    toggleToolbar: function(show) {
5466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      if (show) {
5566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis        if (this.contentHost.firstChild)
5666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          this.contentHost.insertBefore(this.contentHost.firstChild,
5766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis                                        this.toolbar_);
5866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis        else
5966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          this.contentHost.appendChild(this.toolbar_);
6066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      } else {
6166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis        if (this.toolbar_.parentElement)
6266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          this.contentHost.removeChild(this.toolbar_);
6366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      }
6466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    },
6566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
6666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    createToolBar_: function() {
6766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.toolbar_ = this.ownerDocument.createElement('div');
6866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.toolbar_.className = 'tool-bar';
6966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.exitButton_ = this.ownerDocument.createElement('span');
7066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.exitButton_.className = 'exit-button';
7166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.exitButton_.textContent = 'x';
7266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.exitButton_.title = 'Close Overlay (esc)';
7366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.toolbar_.appendChild(this.exitButton_);
7466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    },
7566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
76010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    /**
77010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     * Adds an overlay, attaching it to the contentHost so that it is visible.
78010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     */
79010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    showOverlay: function(overlay) {
80010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      // Reparent this to the overlay content host.
81010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      overlay.oldParent_ = overlay.parentNode;
82010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      this.contentHost.appendChild(overlay);
83010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      this.contentHost.appendChild(this.tabCatcher);
84010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
85010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      // Show the overlay root.
86010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      this.ownerDocument.body.classList.add('disabled-by-overlay');
87010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
88010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      // Bring overlay into focus.
89010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      overlay.tabIndex = 0;
90010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      var focusElement =
91010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis          overlay.querySelector('button, input, list, select, a');
92010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      if (!focusElement) {
93010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        focusElement = overlay;
94010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      }
95010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      focusElement.focus();
96010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
97010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      // Listen to key and focus events to prevent focus from
98010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      // leaving the overlay.
9966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.ownerDocument.addEventListener('focusin', this.onFocusIn_, true);
10066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      overlay.addEventListener('keydown', this.onKeydown_);
101010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    },
102010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
103010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    /**
104010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     * Clicking outside of the overlay will de-focus the overlay. The
105010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     * next tab will look at the entire document to determine the focus.
106010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     * For certain documents, this can cause focus to "leak" outside of
107010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     * the overlay.
108010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     */
109010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    onMousedown_: function(e) {
110010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      if (e.target == this) {
111010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        e.preventDefault();
112010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      }
113010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    },
114010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
115010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    /**
116010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     * Prevents forward-tabbing out of the overlay
117010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     */
118010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    onFocusIn_: function(e) {
119010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      if (e.target == this.tabCatcher) {
120010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        window.setTimeout(this.focusOverlay_.bind(this), 0);
121010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      }
122010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    },
123010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
124010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    focusOverlay_: function() {
125010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      this.contentHost.firstChild.focus();
126010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    },
127010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
128010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    /**
129010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     * Prevent the user from shift-tabbing backwards out of the overlay.
130010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     */
131010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    onKeydown_: function(e) {
13288448d9ae4dfff1805045790ef5f32495d62abccJeff Brown      if (e.keyCode == 9 &&  // tab
133010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis          e.shiftKey &&
134010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis          e.target == this.contentHost.firstChild) {
135010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        e.preventDefault();
136010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      }
137010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    },
138010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
139010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    /**
140010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     * Hides an overlay, attaching it to its original parent if needed.
141010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     */
142010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    hideOverlay: function(overlay) {
143010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      // hide the overlay root
144010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      this.visible = false;
145010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      this.ownerDocument.body.classList.remove('disabled-by-overlay');
146010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      this.lastFocusOut_ = undefined;
147010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
148010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      // put the overlay back on its previous parent
149010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      overlay.parentNode.removeChild(this.tabCatcher);
150010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      if (overlay.oldParent_) {
151010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        overlay.oldParent_.appendChild(overlay);
152010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        delete overlay.oldParent_;
153010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      } else {
154010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        this.contentHost.removeChild(overlay);
155010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      }
156010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
157010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      // remove listeners
15866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      overlay.removeEventListener('keydown', this.onKeydown_);
15966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.ownerDocument.removeEventListener('focusin', this.onFocusIn_);
160010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    }
161010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis  };
162010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
163010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis  /**
164010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis   * Creates a new overlay element. It will not be visible until shown.
165010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis   * @param {Object=} opt_propertyBag Optional properties.
166010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis   * @constructor
167010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis   * @extends {HTMLDivElement}
168010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis   */
16966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis  var Overlay = ui.define('div');
170010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
171010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis  Overlay.prototype = {
172010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    __proto__: HTMLDivElement.prototype,
173010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
174010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    /**
175010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     * Initializes the overlay element.
176010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis     */
177010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    decorate: function() {
178010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      // create the overlay root on this document if its not present
179010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      if (!this.ownerDocument.querySelector('.overlay-root')) {
180010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        var overlayRoot = this.ownerDocument.createElement('div');
18166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis        ui.decorate(overlayRoot, OverlayRoot);
182010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        this.ownerDocument.body.appendChild(overlayRoot);
183010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      }
184010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
185010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      this.classList.add('overlay');
18666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.visible_ = false;
18766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.obeyCloseEvents = false;
1882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.additionalCloseKeyCodes = [];
1892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.onKeyDown = this.onKeyDown.bind(this);
1902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.onKeyPress = this.onKeyPress.bind(this);
1912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.onDocumentClick = this.onDocumentClick.bind(this);
19266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.addEventListener('visibleChange',
19366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis          Overlay.prototype.onVisibleChange_.bind(this), true);
19466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.obeyCloseEvents = true;
195010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    },
196010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
19766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    get visible() {
19866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      return this.visible_;
19966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    },
20066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
20166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    set visible(newValue) {
20266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      base.setPropertyAndDispatchChange(this, 'visible', newValue);
20366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    },
20466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
20566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    get obeyCloseEvents() {
20666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      return this.obeyCloseEvents_;
20766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    },
20866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
20966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    set obeyCloseEvents(newValue) {
21066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      base.setPropertyAndDispatchChange(this, 'obeyCloseEvents', newValue);
21166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      var overlayRoot = this.ownerDocument.querySelector('.overlay-root');
21266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      // Currently the toolbar only has the close button.
21366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      overlayRoot.toggleToolbar(newValue);
21466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    },
21566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
21666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    get toolbar() {
21766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      return this.ownerDocument.querySelector('.overlay-root .tool-bar');
21866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    },
21966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
22066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    onVisibleChange_: function() {
221010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      var overlayRoot = this.ownerDocument.querySelector('.overlay-root');
222010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      if (this.visible) {
22366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis        overlayRoot.setAttribute('visible', 'visible');
224010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        overlayRoot.showOverlay(this);
2252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        document.addEventListener('keydown', this.onKeyDown, true);
2262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        document.addEventListener('keypress', this.onKeyPress, true);
2272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        document.addEventListener('click', this.onDocumentClick, true);
228010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      } else {
22966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis        overlayRoot.removeAttribute('visible');
2302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        document.removeEventListener('keydown', this.onKeyDown, true);
2312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        document.removeEventListener('keypress', this.onKeyPress, true);
2322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        document.removeEventListener('click', this.onDocumentClick, true);
233010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis        overlayRoot.hideOverlay(this);
234010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis      }
2352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
2362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    onKeyDown: function(e) {
23866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      if (!this.obeyCloseEvents)
2392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return;
2402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
24188448d9ae4dfff1805045790ef5f32495d62abccJeff Brown      if (e.keyCode == 27) {  // escape
2422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        this.visible = false;
2432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        e.preventDefault();
2442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return;
2452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
2462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
2472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    onKeyPress: function(e) {
24966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      if (!this.obeyCloseEvents)
2502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return;
2512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      for (var i = 0; i < this.additionalCloseKeyCodes.length; i++) {
2532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (e.keyCode == this.additionalCloseKeyCodes[i]) {
2542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          this.visible = false;
2552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          e.preventDefault();
2562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          return;
2572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        }
2582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
2592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
2602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    onDocumentClick: function(e) {
26266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      if (!this.obeyCloseEvents)
2632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return;
2642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var target = e.target;
2652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      while (target !== null) {
2662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (target === this)
2672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          return;
2682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        target = target.parentNode;
2692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
2702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.visible = false;
2712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      e.preventDefault();
2722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return;
273010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    }
2742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
275010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis  };
276010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis
277010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis  return {
278010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis    Overlay: Overlay
279010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis  };
280010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis});
281