Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / resources / help / help_base_page.js
blob578e260a5e40b731a6a96592644e6c9fb55913db
1 // Copyright 2013 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.
5 // Helper base class for all help pages and overlays, which controls
6 // overlays, focus and scroll. This class is partially based on
7 // OptionsPage, but simpler and contains only overlay- and focus-
8 // handling logic. As in OptionsPage each page can be an overlay itself,
9 // but each page contains its own list of registered overlays which can be
10 // displayed over it.
12 // TODO (ygorshenin@): crbug.com/313244.
13 cr.define('help', function() {
14   function HelpBasePage() {
15   }
17   HelpBasePage.prototype = {
18     __proto__: HTMLDivElement.prototype,
20     /**
21      * name of the page, should be the same as an id of the
22      * corresponding HTMLDivElement.
23      */
24     name: null,
26     /**
27      * HTML counterpart of this page.
28      */
29     pageDiv: null,
31     /**
32      * True if current page is overlay.
33      */
34     isOverlay: false,
36     /**
37      * HTMLElement that was last focused when this page was the
38      * topmost.
39      */
40     lastFocusedElement: null,
42     /**
43      * State of vertical scrollbars when this page was the topmost.
44      */
45     lastScrollTop: 0,
47     /**
48      * Dictionary of registered overlays.
49      */
50     registeredOverlays: {},
52     /**
53      * Stores currently focused element.
54      * @private
55      */
56     storeLastFocusedElement_: function() {
57       if (this.pageDiv.contains(document.activeElement))
58         this.lastFocusedElement = document.activeElement;
59     },
61     /**
62      * Restores focus to the last focused element on this page.
63      * @private
64      */
65     restoreLastFocusedElement_: function() {
66       if (this.lastFocusedElement)
67         this.lastFocusedElement.focus();
68       else
69         this.focus();
70     },
72     /**
73      * Shows or hides current page iff it's an overlay.
74      * @param {boolean} visible True if overlay should be displayed.
75      * @private
76      */
77     setOverlayVisible_: function(visible) {
78       assert(this.isOverlay);
79       this.container.hidden = !visible;
80       if (visible)
81         this.pageDiv.classList.add('showing');
82       else
83         this.pageDiv.classList.remove('showing');
84     },
86     /**
87      * @return {HTMLDivElement}  visible non-overlay page or
88      * null, if there are no visible non-overlay pages.
89      * @private
90      */
91     getVisibleNonOverlay_: function() {
92       if (this.isOverlay || !this.visible)
93         return null;
94       return this;
95     },
97     /**
98      * @return {HTMLDivElement} Visible overlay page, or null,
99      * if there are no visible overlay pages.
100      * @private
101      */
102     getVisibleOverlay_: function() {
103       for (var name in this.registeredOverlays) {
104         var overlay = this.registeredOverlays[name];
105         if (overlay.visible)
106           return overlay;
107       }
108       return null;
109     },
111     /**
112      * Freezes current page, makes it impossible to scroll it.
113      * @param {boolean} freeze True if the page should be frozen.
114      * @private
115      */
116     freeze_: function(freeze) {
117       var scrollLeft = scrollLeftForDocument(document);
118       if (freeze) {
119         this.lastScrollTop = scrollTopForDocument(document);
120         document.body.style.overflow = 'hidden';
121         window.scroll(scrollLeft, 0);
122       } else {
123         document.body.style.overflow = 'auto';
124         window.scroll(scrollLeft, this.lastScrollTop);
125       }
126     },
128     /**
129      * Initializes current page.
130      * @param {string} name Name of the current page.
131      */
132     initialize: function(name) {
133       this.name = name;
134       this.pageDiv = $(name);
135     },
137     /**
138      * Called before overlay is displayed.
139      */
140     onBeforeShow: function() {
141     },
143     /**
144      * @return {HTMLDivElement} Topmost visible page, or null, if
145      * there are no visible pages.
146      */
147     getTopmostVisiblePage: function() {
148       return this.getVisibleOverlay_() || this.getVisibleNonOverlay_();
149     },
151     /**
152      * Registers overlay.
153      * @param {HelpBasePage} overlay Overlay that should be registered.
154      */
155     registerOverlay: function(overlay) {
156       this.registeredOverlays[overlay.name] = overlay;
157       overlay.isOverlay = true;
158     },
160     /**
161      * Shows or hides current page.
162      * @param {boolean} visible True if current page should be displayed.
163      */
164     set visible(visible) {
165       if (this.visible == visible)
166         return;
168       if (!visible)
169         this.storeLastFocusedElement_();
171       if (this.isOverlay)
172         this.setOverlayVisible_(visible);
173       else
174         this.pageDiv.hidden = !visible;
176       if (visible)
177         this.restoreLastFocusedElement_();
179       if (visible)
180         this.onBeforeShow();
181     },
183     /**
184      * Returns true if current page is visible.
185      * @return {boolean} True if current page is visible.
186      */
187     get visible() {
188       if (this.isOverlay)
189         return this.pageDiv.classList.contains('showing');
190       return !this.pageDiv.hidden;
191     },
193     /**
194      * This method returns overlay container, it should be called only
195      * on overlays.
196      * @return {HTMLDivElement} overlay container.
197      */
198     get container() {
199       assert(this.isOverlay);
200       return this.pageDiv.parentNode;
201     },
203     /**
204      * Shows registered overlay.
205      * @param {string} name Name of registered overlay to show.
206      */
207     showOverlay: function(name) {
208       var currentPage = this.getTopmostVisiblePage();
209       currentPage.storeLastFocusedElement_();
210       currentPage.freeze_(true);
212       var overlay = this.registeredOverlays[name];
213       if (!overlay)
214         return;
215       overlay.visible = true;
216     },
218     /**
219      * Hides currently displayed overlay.
220      */
221     closeOverlay: function() {
222       var overlay = this.getVisibleOverlay_();
223       if (!overlay)
224         return;
225       overlay.visible = false;
227       var currentPage = this.getTopmostVisiblePage();
228       currentPage.restoreLastFocusedElement_();
229       currentPage.freeze_(false);
230     },
232     /**
233      * If the page does not contain focused elements, focuses on the
234      * first appropriate.
235      */
236     focus: function() {
237       if (this.pageDiv.contains(document.activeElement))
238         return;
239       var elements = this.pageDiv.querySelectorAll(
240         'input, list, select, textarea, button');
241       for (var i = 0; i < elements.length; i++) {
242         var element = elements[i];
243         element.focus();
244         if (document.activeElement == element)
245           return;
246       }
247     },
248   };
250   // Export
251   return {
252     HelpBasePage: HelpBasePage
253   };