1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
8 * @fileoverview Implements an element that is hidden by default, but
9 * when shown, dims and (attempts to) disable the main document.
11 * You can turn any div into an overlay. Note that while an
12 * overlay element is shown, its parent is changed. Hiding the overlay
13 * restores its original parentage.
16 tvcm.requireTemplate('tvcm.ui.overlay');
18 tvcm.require('tvcm.utils');
19 tvcm.require('tvcm.properties');
20 tvcm.require('tvcm.events');
21 tvcm.require('tvcm.ui');
23 tvcm.exportTo('tvcm.ui', function() {
25 * Creates a new overlay element. It will not be visible until shown.
27 * @extends {HTMLDivElement}
29 var Overlay = tvcm.ui.define('overlay');
32 __proto__: HTMLDivElement.prototype,
35 * Initializes the overlay element.
37 decorate: function() {
38 this.classList.add('overlay');
40 this.parentEl_ = this.ownerDocument.body;
42 this.visible_ = false;
43 this.userCanClose_ = true;
45 this.onKeyDown_ = this.onKeyDown_.bind(this);
46 this.onClick_ = this.onClick_.bind(this);
47 this.onFocusIn_ = this.onFocusIn_.bind(this);
48 this.onDocumentClick_ = this.onDocumentClick_.bind(this);
49 this.onClose_ = this.onClose_.bind(this);
51 this.addEventListener('visibleChange',
52 tvcm.ui.Overlay.prototype.onVisibleChange_.bind(this), true);
54 // Setup the shadow root
55 var createShadowRoot = this.createShadowRoot ||
56 this.webkitCreateShadowRoot;
57 this.shadow_ = createShadowRoot.call(this);
58 this.shadow_.appendChild(tvcm.instantiateTemplate('#overlay-template'));
60 this.closeBtn_ = this.shadow_.querySelector('close-button');
61 this.closeBtn_.addEventListener('click', this.onClose_);
64 .querySelector('overlay-frame')
65 .addEventListener('click', this.onClick_);
67 this.observer_ = new WebKitMutationObserver(
68 this.didButtonBarMutate_.bind(this));
69 this.observer_.observe(this.shadow_.querySelector('button-bar'),
72 // title is a variable on regular HTMLElements. However, we want to
73 // use it for something more useful.
74 Object.defineProperty(
77 return this.shadow_.querySelector('title').textContent;
79 set: function(title) {
80 this.shadow_.querySelector('title').textContent = title;
85 set userCanClose(userCanClose) {
86 this.userCanClose_ = userCanClose;
87 this.closeBtn_.style.display =
88 userCanClose ? 'block' : 'none';
92 return this.shadow_.querySelector('left-buttons');
96 return this.shadow_.querySelector('right-buttons');
100 return this.visible_;
103 set visible(newValue) {
104 if (this.visible_ === newValue)
107 tvcm.setPropertyAndDispatchChange(this, 'visible', newValue);
110 onVisibleChange_: function() {
111 this.visible_ ? this.show_() : this.hide_();
115 this.parentEl_.appendChild(this);
117 if (this.userCanClose_) {
118 document.addEventListener('keydown', this.onKeyDown_);
119 document.addEventListener('click', this.onDocumentClick_);
122 this.parentEl_.addEventListener('focusin', this.onFocusIn_);
125 // Focus the first thing we find that makes sense. (Skip the close button
126 // as it doesn't make sense as the first thing to focus.)
127 var focusEl = undefined;
128 var elList = this.querySelectorAll('button, input, list, select, a');
129 if (elList.length > 0) {
130 if (elList[0] === this.closeBtn_) {
131 if (elList.length > 1)
137 if (focusEl === undefined)
143 this.parentEl_.removeChild(this);
145 this.parentEl_.removeEventListener('focusin', this.onFocusIn_);
148 this.closeBtn_.removeEventListener(this.onClose_);
150 document.removeEventListener('keydown', this.onKeyDown_);
151 document.removeEventListener('click', this.onDocumentClick_);
154 onClose_: function(e) {
155 this.visible = false;
156 if (e.type != 'keydown')
159 tvcm.dispatchSimpleEvent(this, 'closeclick');
162 onFocusIn_: function(e) {
163 if (e.target === this)
166 window.setTimeout(function() { this.focus(); }, 0);
171 didButtonBarMutate_: function(e) {
172 var hasButtons = this.leftButtons.children.length +
173 this.rightButtons.children.length > 0;
175 this.shadow_.querySelector('button-bar').style.display = undefined;
177 this.shadow_.querySelector('button-bar').style.display = 'none';
180 onKeyDown_: function(e) {
181 // Disallow shift-tab back to another element.
182 if (e.keyCode === 9 && // tab
189 if (e.keyCode !== 27) // escape
195 onClick_: function(e) {
199 onDocumentClick_: function(e) {
200 if (!this.userCanClose_)
207 Overlay.showError = function(msg, opt_err) {
208 var o = new Overlay();
212 var e = tvcm.normalizeException(opt_err);
214 var stackDiv = document.createElement('pre');
215 stackDiv.textContent = e.stack;
216 stackDiv.style.paddingLeft = '8px';
217 stackDiv.style.margin = 0;
218 o.appendChild(stackDiv);
220 var b = document.createElement('button');
221 b.textContent = 'OK';
222 b.addEventListener('click', function() {
225 o.rightButtons.appendChild(b);