OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 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. | |
4 | |
stevenjb
2014/07/28 22:27:23
We should provide some sort of top level comment d
michaelpg
2014/07/29 01:08:31
How's this?
| |
5 cr.define('cr.ui.pageManager', function() { | |
6 /** @const */ var FocusOutlineManager = cr.ui.FocusOutlineManager; | |
7 | |
8 var PageManager = { | |
9 /** | |
10 * Offset of page container in pixels, to allow room for side menu. | |
11 * Simplified settings pages can override this if they don't use the menu. | |
12 * The default (155) comes from -webkit-margin-start in uber_shared.css | |
13 * TODO(michaelpg@): Remove dependency on uber menu (crbug.com/313244). | |
stevenjb
2014/07/28 22:27:23
TODO(michaelpg)
michaelpg
2014/07/29 01:08:30
Done.
| |
14 * @private | |
15 */ | |
16 horizontalOffset: 155, | |
17 | |
18 /** | |
19 * Main level option pages. Maps lower-case page names to the respective | |
20 * page object. | |
21 * @protected | |
22 */ | |
23 registeredPages: {}, | |
24 | |
25 /** | |
26 * Pages which are meant to behave like modal dialogs. Maps lower-case | |
27 * overlay names to the respective overlay object. | |
28 * @protected | |
29 */ | |
30 registeredOverlayPages: {}, | |
31 | |
32 /** | |
33 * True if page is served from a dialog. | |
34 */ | |
35 isDialog: false, | |
36 | |
37 /** | |
38 * Gets the default page (to be shown on initial load). | |
39 */ | |
40 getDefaultPage: function() { | |
41 return options.BrowserOptions.getInstance(); | |
Dan Beam
2014/07/28 20:57:59
this should probably be abstract (e.g. just assert
michaelpg
2014/07/29 01:08:31
I'd rather not derive from PageManager, instead Op
| |
42 }, | |
43 | |
44 /** | |
45 * Shows the default page. | |
46 */ | |
47 showDefaultPage: function() { | |
48 this.navigateToPage(this.getDefaultPage().name); | |
49 }, | |
50 | |
51 /** | |
52 * "Navigates" to a page, meaning that the page will be shown and the | |
53 * appropriate entry is placed in the history. | |
54 * @param {string} pageName Page name. | |
55 */ | |
56 navigateToPage: function(pageName) { | |
57 this.showPageByName(pageName, true); | |
stevenjb
2014/07/28 22:27:22
JS novice question: Could this be done by giving u
michaelpg
2014/07/29 01:08:30
Done.
| |
58 }, | |
59 | |
60 /** | |
61 * Shows a registered page. This handles both top-level and overlay pages. | |
62 * @param {string} pageName Page name. | |
63 * @param {boolean} updateHistory True if we should update the history after | |
64 * showing the page. | |
65 * @param {Object=} opt_propertyBag An optional bag of properties including | |
66 * replaceState (if history state should be replaced instead of pushed). | |
67 * @private | |
Dan Beam
2014/07/28 20:58:00
this isn't private
michaelpg
2014/07/29 01:08:31
Done.
| |
68 */ | |
69 showPageByName: function(pageName, | |
70 updateHistory, | |
71 opt_propertyBag) { | |
72 // If |opt_propertyBag| is non-truthy, homogenize to object. | |
73 opt_propertyBag = opt_propertyBag || {}; | |
stevenjb
2014/07/28 22:27:23
JS novice question: Is this preferable to declarin
michaelpg
2014/07/29 01:08:30
Not sure what you are asking exactly:
function(fo
Dan Beam
2014/07/30 01:00:01
opt_propertyBag = opt_propertyBag || {};
simplifi
stevenjb
2014/07/30 22:36:21
Nevermind. I somehow thought you could provide def
| |
74 | |
75 // If a bubble is currently being shown, hide it. | |
76 this.hideBubble(); | |
77 | |
78 // Find the currently visible root-level page. | |
79 var rootPage = null; | |
80 for (var name in this.registeredPages) { | |
81 var page = this.registeredPages[name]; | |
82 if (page.visible && !page.parentPage) { | |
83 rootPage = page; | |
84 break; | |
85 } | |
86 } | |
87 | |
88 // Find the target page. | |
89 var targetPage = this.registeredPages[pageName.toLowerCase()]; | |
90 if (!targetPage || !targetPage.canShowPage()) { | |
91 // If it's not a page, try it as an overlay. | |
92 if (!targetPage && this.showOverlay_(pageName, rootPage)) { | |
93 if (updateHistory) | |
94 this.updateHistoryState_(!!opt_propertyBag.replaceState); | |
95 this.updateTitle_(); | |
96 return; | |
97 } else { | |
stevenjb
2014/07/28 22:27:23
nit: no else after return
michaelpg
2014/07/29 01:08:30
Done.
| |
98 targetPage = this.getDefaultPage(); | |
99 } | |
100 } | |
101 | |
102 pageName = targetPage.name.toLowerCase(); | |
103 var targetPageWasVisible = targetPage.visible; | |
104 | |
105 // Determine if the root page is 'sticky', meaning that it | |
106 // shouldn't change when showing an overlay. This can happen for special | |
107 // pages like Search. | |
108 var isRootPageLocked = | |
109 rootPage && rootPage.sticky && targetPage.parentPage; | |
110 | |
111 var allPageNames = Array.prototype.concat.call( | |
112 Object.keys(this.registeredPages), | |
113 Object.keys(this.registeredOverlayPages)); | |
114 | |
115 // Notify pages if they will be hidden. | |
116 for (var i = 0; i < allPageNames.length; ++i) { | |
117 var name = allPageNames[i]; | |
118 var page = this.registeredPages[name] || | |
119 this.registeredOverlayPages[name]; | |
120 if (!page.parentPage && isRootPageLocked) | |
121 continue; | |
122 if (page.willHidePage && name != pageName && | |
123 !page.isAncestorOfPage(targetPage)) { | |
124 page.willHidePage(); | |
125 } | |
126 } | |
127 | |
128 // Update visibilities to show only the hierarchy of the target page. | |
129 for (var i = 0; i < allPageNames.length; ++i) { | |
130 var name = allPageNames[i]; | |
131 var page = this.registeredPages[name] || | |
132 this.registeredOverlayPages[name]; | |
133 if (!page.parentPage && isRootPageLocked) | |
134 continue; | |
135 page.visible = name == pageName || page.isAncestorOfPage(targetPage); | |
136 } | |
137 | |
138 // Update the history and current location. | |
139 if (updateHistory) | |
140 this.updateHistoryState_(!!opt_propertyBag.replaceState); | |
141 | |
142 // Update focus if any other control was focused on the previous page, | |
143 // or the previous page is not known. | |
144 if (document.activeElement != document.body && | |
145 (!rootPage || rootPage.pageDiv.contains(document.activeElement))) { | |
146 targetPage.focus(); | |
147 } | |
148 | |
149 // Notify pages if they were shown. | |
150 for (var i = 0; i < allPageNames.length; ++i) { | |
151 var name = allPageNames[i]; | |
152 var page = this.registeredPages[name] || | |
153 this.registeredOverlayPages[name]; | |
154 if (!page.parentPage && isRootPageLocked) | |
155 continue; | |
stevenjb
2014/07/28 22:27:22
nit: We use the same pattern 3 times here, maybe c
michaelpg
2014/07/29 01:08:31
Sure. But I don't want to stray too far from the o
| |
156 if (!targetPageWasVisible && page.didShowPage && | |
157 (name == pageName || page.isAncestorOfPage(targetPage))) { | |
158 page.didShowPage(); | |
159 } | |
160 } | |
161 | |
162 // Update the document title. Do this after didShowPage was called, in | |
163 // case a page decides to change its title. | |
164 this.updateTitle_(); | |
165 }, | |
166 | |
167 /** | |
168 * Scrolls the page to the correct position (the top when opening an | |
169 * overaly, or the old scroll position a previously hidden overlay | |
170 * becomes visible). | |
171 * @private | |
172 */ | |
173 updateScrollPosition_: function() { | |
174 var container = $('page-container'); | |
175 var scrollTop = container.oldScrollTop || 0; | |
176 container.oldScrollTop = undefined; | |
177 window.scroll(scrollLeftForDocument(document), scrollTop); | |
178 }, | |
179 | |
180 /** | |
181 * Updates the title to title of the current page. | |
182 * @private | |
183 */ | |
184 updateTitle_: function() { | |
185 var page = this.getTopmostVisiblePage(); | |
186 // TODO(michaelpg@): Remove dependency on uber (crbug.com/313244). | |
Dan Beam
2014/07/28 20:58:00
same thing regarding uber deps: you should be able
stevenjb
2014/07/28 22:27:23
TODO(michaelpg)
michaelpg
2014/07/29 01:08:30
Done.
michaelpg
2014/07/29 01:08:30
Yes, setting up an observer interface was my plan.
| |
187 uber.setTitle(page.title); | |
188 }, | |
189 | |
190 /** | |
191 * Pushes the current page onto the history stack, replacing the current | |
192 * entry if appropriate. | |
193 * @param {boolean} replace If true, allow no history events to be created. | |
194 * @param {object=} opt_params A bag of optional params, including: | |
195 * {boolean} ignoreHash Whether to include the hash or not. | |
196 * @private | |
197 */ | |
198 updateHistoryState_: function(replace, opt_params) { | |
199 if (PageManager.isDialog) | |
200 return; | |
201 | |
202 var page = this.getTopmostVisiblePage(); | |
203 var path = window.location.pathname + window.location.hash; | |
204 if (path) { | |
205 // Remove trailing slash. | |
206 path = path.slice(1).replace(/\/(?:#|$)/, ''); | |
207 } | |
208 | |
209 // If the page is already in history (the user may have clicked the same | |
210 // link twice, or this is the initial load), do nothing. | |
211 var hash = opt_params && opt_params.ignoreHash ? | |
212 '' : window.location.hash; | |
213 var newPath = (page == this.getDefaultPage() ? '' : page.name) + hash; | |
214 if (path == newPath) | |
215 return; | |
216 | |
217 // TODO(michaelpg@): Remove dependency on uber (crbug.com/313244). | |
stevenjb
2014/07/28 22:27:22
TODO(michaelpg)
michaelpg
2014/07/29 01:08:30
Done.
| |
218 var historyFunction = replace ? uber.replaceState : uber.pushState; | |
219 historyFunction.call(uber, {}, newPath); | |
220 }, | |
221 | |
222 /** | |
223 * Shows a registered Overlay page. Does not update history. | |
224 * @param {string} overlayName Page name. | |
225 * @param {OptionPage} rootPage The currently visible root-level page. | |
226 * @return {boolean} whether we showed an overlay. | |
Dan Beam
2014/07/28 20:57:59
all the methods that end with _ => @private
michaelpg
2014/07/29 01:08:30
Done.
| |
227 */ | |
228 showOverlay_: function(overlayName, rootPage) { | |
229 var overlay = this.registeredOverlayPages[overlayName.toLowerCase()]; | |
230 if (!overlay || !overlay.canShowPage()) | |
231 return false; | |
232 | |
233 // Save the currently focused element in the page for restoration later. | |
234 var currentPage = this.getTopmostVisiblePage(); | |
235 if (currentPage) | |
236 currentPage.lastFocusedElement = document.activeElement; | |
237 | |
238 if ((!rootPage || !rootPage.sticky) && | |
239 overlay.parentPage && | |
240 !overlay.parentPage.visible) { | |
241 this.showPageByName(overlay.parentPage.name, false); | |
242 } | |
243 | |
244 if (!overlay.visible) { | |
245 overlay.visible = true; | |
246 if (overlay.didShowPage) overlay.didShowPage(); | |
stevenjb
2014/07/28 22:27:23
nit: two lines? (Or is that a C++ only thing?)
michaelpg
2014/07/29 01:08:30
This isn't my code, but I think two lines is the c
| |
247 } | |
248 | |
249 // Change focus to the overlay if any other control was focused by | |
250 // keyboard before. Otherwise, no one should have focus. | |
251 if (document.activeElement != document.body) { | |
252 if (FocusOutlineManager.forDocument(document).visible) { | |
253 overlay.focus(); | |
254 } else if (!overlay.pageDiv.contains(document.activeElement)) { | |
255 document.activeElement.blur(); | |
256 } | |
257 } | |
258 | |
259 if ($('search-field') && $('search-field').value == '') { | |
260 var section = overlay.associatedSection; | |
261 if (section) | |
262 options.BrowserOptions.scrollToSection(section); | |
263 } | |
264 | |
265 return true; | |
266 }, | |
267 | |
268 /** | |
269 * Returns whether or not an overlay is visible. | |
270 * @return {boolean} True if an overlay is visible. | |
271 * @private | |
272 */ | |
273 isOverlayVisible_: function() { | |
274 return this.getVisibleOverlay_() != null; | |
275 }, | |
276 | |
277 /** | |
278 * Returns the currently visible overlay, or null if no page is visible. | |
279 * @return {OptionPage} The visible overlay. | |
280 */ | |
281 getVisibleOverlay_: function() { | |
282 var topmostPage = null; | |
283 for (var name in this.registeredOverlayPages) { | |
284 var page = this.registeredOverlayPages[name]; | |
285 if (page.visible && | |
286 (!topmostPage || page.nestingLevel > topmostPage.nestingLevel)) { | |
287 topmostPage = page; | |
288 } | |
289 } | |
290 return topmostPage; | |
291 }, | |
292 | |
293 /** | |
294 * Restores the last focused element on a given page. | |
295 */ | |
296 restoreLastFocusedElement_: function() { | |
297 var currentPage = this.getTopmostVisiblePage(); | |
298 if (currentPage.lastFocusedElement) | |
299 currentPage.lastFocusedElement.focus(); | |
300 }, | |
301 | |
302 /** | |
303 * Closes the visible overlay. Updates the history state after closing the | |
304 * overlay. | |
305 */ | |
306 closeOverlay: function() { | |
307 var overlay = this.getVisibleOverlay_(); | |
308 if (!overlay) | |
309 return; | |
310 | |
311 overlay.visible = false; | |
312 | |
313 if (overlay.didClosePage) overlay.didClosePage(); | |
stevenjb
2014/07/28 22:27:22
two lines?
michaelpg
2014/07/29 01:08:30
Done.
Dan Beam
2014/07/30 01:00:01
for future reference, 2 lines is more common but 1
| |
314 this.updateHistoryState_(false, {ignoreHash: true}); | |
315 this.updateTitle_(); | |
316 | |
317 this.restoreLastFocusedElement_(); | |
318 }, | |
319 | |
320 /** | |
321 * Closes all overlays and updates the history after each closed overlay. | |
322 */ | |
323 closeAllOverlays: function() { | |
324 while (this.isOverlayVisible_()) { | |
325 this.closeOverlay(); | |
326 } | |
327 }, | |
328 | |
329 /** | |
330 * Cancels (closes) the overlay, due to the user pressing <Esc>. | |
331 */ | |
332 cancelOverlay: function() { | |
333 // Blur the active element to ensure any changed pref value is saved. | |
334 document.activeElement.blur(); | |
335 var overlay = this.getVisibleOverlay_(); | |
336 // Let the overlay handle the <Esc> if it wants to. | |
337 if (overlay.handleCancel) { | |
338 overlay.handleCancel(); | |
339 this.restoreLastFocusedElement_(); | |
340 } else { | |
341 this.closeOverlay(); | |
342 } | |
343 }, | |
344 | |
345 /** | |
346 * Hides the visible overlay. Does not affect the history state. | |
347 * @private | |
348 */ | |
349 hideOverlay_: function() { | |
350 var overlay = this.getVisibleOverlay_(); | |
351 if (overlay) | |
352 overlay.visible = false; | |
353 }, | |
354 | |
355 /** | |
356 * Returns the pages which are currently visible, ordered by nesting level | |
357 * (ascending). | |
358 * @return {Array.OptionPage} The pages which are currently visible, ordered | |
359 * by nesting level (ascending). | |
360 */ | |
361 getVisiblePages_: function() { | |
362 var visiblePages = []; | |
363 for (var name in this.registeredPages) { | |
364 var page = this.registeredPages[name]; | |
365 if (page.visible) | |
366 visiblePages[page.nestingLevel] = page; | |
367 } | |
368 return visiblePages; | |
369 }, | |
370 | |
371 /** | |
372 * Returns the topmost visible page (overlays excluded). | |
373 * @return {OptionPage} The topmost visible page aside any overlay. | |
374 * @private | |
375 */ | |
376 getTopmostVisibleNonOverlayPage_: function() { | |
377 var topPage = null; | |
378 for (var name in this.registeredPages) { | |
379 var page = this.registeredPages[name]; | |
380 if (page.visible && | |
381 (!topPage || page.nestingLevel > topPage.nestingLevel)) | |
382 topPage = page; | |
383 } | |
384 | |
385 return topPage; | |
386 }, | |
387 | |
388 /** | |
389 * Returns the topmost visible page, or null if no page is visible. | |
390 * @return {OptionPage} The topmost visible page. | |
391 */ | |
392 getTopmostVisiblePage: function() { | |
393 // Check overlays first since they're top-most if visible. | |
394 return this.getVisibleOverlay_() || | |
395 this.getTopmostVisibleNonOverlayPage_(); | |
396 }, | |
397 | |
398 /** | |
399 * Returns the currently visible bubble, or null if no bubble is visible. | |
400 * @return {AutoCloseBubble} The bubble currently being shown. | |
401 */ | |
402 getVisibleBubble: function() { | |
403 var bubble = PageManager.bubble_; | |
404 return bubble && !bubble.hidden ? bubble : null; | |
405 }, | |
406 | |
407 /** | |
408 * Shows an informational bubble displaying |content| and pointing at the | |
409 * |target| element. If |content| has focusable elements, they join the | |
410 * current page's tab order as siblings of |domSibling|. | |
411 * @param {HTMLDivElement} content The content of the bubble. | |
412 * @param {HTMLElement} target The element at which the bubble points. | |
413 * @param {HTMLElement} domSibling The element after which the bubble is | |
414 * added to the DOM. | |
415 * @param {cr.ui.ArrowLocation} location The arrow location. | |
416 */ | |
417 showBubble: function(content, target, domSibling, location) { | |
418 PageManager.hideBubble(); | |
419 | |
420 var bubble = new cr.ui.AutoCloseBubble; | |
421 bubble.anchorNode = target; | |
422 bubble.domSibling = domSibling; | |
423 bubble.arrowLocation = location; | |
424 bubble.content = content; | |
425 bubble.show(); | |
426 PageManager.bubble_ = bubble; | |
427 }, | |
428 | |
429 /** | |
430 * Hides the currently visible bubble, if any. | |
431 */ | |
432 hideBubble: function() { | |
433 if (PageManager.bubble_) | |
434 PageManager.bubble_.hide(); | |
435 }, | |
436 | |
437 /** | |
438 * Registers new page. | |
439 * @param {cr.ui.page_manager.Page} page Page to register. | |
440 */ | |
441 register: function(page) { | |
442 this.registeredPages[page.name.toLowerCase()] = page; | |
443 page.initializePage(); | |
444 }, | |
445 | |
446 /** | |
447 * Find an enclosing section for an element if it exists. | |
448 * @param {Element} element Element to search. | |
449 * @return {OptionPage} The section element, or null. | |
450 * @private | |
451 */ | |
452 findSectionForNode_: function(node) { | |
453 while (node = node.parentNode) { | |
454 if (node.nodeName == 'SECTION') | |
455 return node; | |
456 } | |
457 return null; | |
458 }, | |
459 | |
460 /** | |
461 * Registers a new Overlay page. | |
462 * @param {cr.ui.page_manager.Page} overlay Overlay to register. | |
463 * @param {cr.ui.page_manager.Page} parentPage Associated parent page for | |
464 * this overlay. | |
465 * @param {Array} associatedControls Array of control elements associated | |
466 * with this page. | |
467 */ | |
468 registerOverlay: function(overlay, | |
469 parentPage, | |
470 associatedControls) { | |
471 this.registeredOverlayPages[overlay.name.toLowerCase()] = overlay; | |
472 overlay.parentPage = parentPage; | |
473 if (associatedControls) { | |
474 overlay.associatedControls = associatedControls; | |
475 if (associatedControls.length) { | |
476 overlay.associatedSection = | |
477 this.findSectionForNode_(associatedControls[0]); | |
478 } | |
479 | |
480 // Sanity check. | |
481 for (var i = 0; i < associatedControls.length; ++i) { | |
482 assert(associatedControls[i], 'Invalid element passed.'); | |
483 } | |
484 } | |
485 | |
486 // Reverse the button strip for Windows and CrOS. See the documentation of | |
487 // reverseButtonStripIfNecessary_() for an explanation of why this is | |
488 // done. | |
489 if (cr.isWindows || cr.isChromeOS) | |
490 this.reverseButtonStripIfNecessary_(overlay); | |
491 | |
492 overlay.tab = undefined; | |
493 overlay.isOverlay = true; | |
494 overlay.initializePage(); | |
495 }, | |
496 | |
497 /** | |
498 * Reverses the child elements of a button strip if it hasn't already been | |
499 * reversed. This is necessary because WebKit does not alter the tab order | |
500 * for elements that are visually reversed using | |
501 * -webkit-box-direction: reverse, and the button order is reversed for | |
502 * views. See http://webk.it/62664 for more information. | |
503 * @param {Object} overlay The overlay containing the button strip to | |
504 * reverse. | |
505 * @private | |
506 */ | |
507 reverseButtonStripIfNecessary_: function(overlay) { | |
508 var buttonStrips = | |
509 overlay.pageDiv.querySelectorAll('.button-strip:not([reversed])'); | |
510 | |
511 // Reverse all button-strips in the overlay. | |
512 for (var j = 0; j < buttonStrips.length; j++) { | |
513 var buttonStrip = buttonStrips[j]; | |
514 | |
515 var childNodes = buttonStrip.childNodes; | |
516 for (var i = childNodes.length - 1; i >= 0; i--) | |
517 buttonStrip.appendChild(childNodes[i]); | |
518 | |
519 buttonStrip.setAttribute('reversed', ''); | |
520 } | |
521 }, | |
522 | |
523 /** | |
524 * Returns the name of the page from the current path. | |
525 */ | |
526 getPageNameFromPath: function() { | |
527 var path = location.pathname; | |
528 if (path.length <= 1) | |
529 return this.getDefaultPage().name; | |
530 | |
531 // Skip starting slash and remove trailing slash (if any). | |
532 return path.slice(1).replace(/\/$/, ''); | |
533 }, | |
534 | |
535 /** | |
536 * Callback for window.onpopstate to handle back/forward navigations. | |
537 * @param {string} pageName The current page name. | |
538 * @param {Object} data State data pushed into history. | |
539 */ | |
540 setState: function(pageName, data) { | |
541 var currentOverlay = this.getVisibleOverlay_(); | |
542 var lowercaseName = pageName.toLowerCase(); | |
543 var newPage = this.registeredPages[lowercaseName] || | |
544 this.registeredOverlayPages[lowercaseName] || | |
545 this.getDefaultPage(); | |
546 if (currentOverlay && !currentOverlay.isAncestorOfPage(newPage)) { | |
547 currentOverlay.visible = false; | |
548 if (currentOverlay.didClosePage) currentOverlay.didClosePage(); | |
549 } | |
550 this.showPageByName(pageName, false); | |
551 }, | |
552 | |
553 /** | |
554 * Callback for window.onbeforeunload. Used to notify overlays that they | |
555 * will be closed. | |
556 */ | |
557 willClose: function() { | |
558 var overlay = this.getVisibleOverlay_(); | |
559 if (overlay && overlay.didClosePage) | |
560 overlay.didClosePage(); | |
561 }, | |
562 | |
563 /** | |
564 * Freezes/unfreezes the scroll position of the root page container. | |
565 * @param {boolean} freeze Whether the page should be frozen. | |
566 * @private | |
567 */ | |
568 setRootPageFrozen_: function(freeze) { | |
569 var container = $('page-container'); | |
570 if (container.classList.contains('frozen') == freeze) | |
571 return; | |
572 | |
573 if (freeze) { | |
574 // Lock the width, since auto width computation may change. | |
575 container.style.width = window.getComputedStyle(container).width; | |
576 container.oldScrollTop = scrollTopForDocument(document); | |
577 container.classList.add('frozen'); | |
578 var verticalPosition = | |
579 container.getBoundingClientRect().top - container.oldScrollTop; | |
580 container.style.top = verticalPosition + 'px'; | |
581 this.updateFrozenElementHorizontalPosition_(container); | |
582 } else { | |
583 container.classList.remove('frozen'); | |
584 container.style.top = ''; | |
585 container.style.left = ''; | |
586 container.style.right = ''; | |
587 container.style.width = ''; | |
588 } | |
589 }, | |
590 | |
591 /** | |
592 * Freezes/unfreezes the scroll position of the root page based on the | |
593 * current page stack. | |
594 */ | |
595 updateRootPageFreezeState: function() { | |
596 var topPage = PageManager.getTopmostVisiblePage(); | |
597 if (topPage) | |
598 this.setRootPageFrozen_(topPage.isOverlay); | |
599 }, | |
600 | |
601 /** | |
602 * Initializes the complete page. | |
603 */ | |
604 initialize: function() { | |
605 FocusOutlineManager.forDocument(document); | |
606 document.addEventListener('scroll', this.handleScroll_.bind(this)); | |
607 | |
608 // Trigger the scroll handler manually to set the initial state. | |
609 this.handleScroll_(); | |
610 | |
611 // Shake the dialog if the user clicks outside the dialog bounds. | |
612 var containers = [$('overlay-container-1'), $('overlay-container-2')]; | |
613 for (var i = 0; i < containers.length; i++) { | |
614 var overlay = containers[i]; | |
615 cr.ui.overlay.setupOverlay(overlay); | |
616 overlay.addEventListener('cancelOverlay', | |
617 PageManager.cancelOverlay.bind(PageManager)); | |
618 } | |
619 | |
620 cr.ui.overlay.globalInitialization(); | |
621 }, | |
622 | |
623 /** | |
624 * Does a bounds check for the element on the given x, y client coordinates. | |
625 * @param {Element} e The DOM element. | |
626 * @param {number} x The client X to check. | |
627 * @param {number} y The client Y to check. | |
628 * @return {boolean} True if the point falls within the element's bounds. | |
629 * @private | |
630 */ | |
631 elementContainsPoint_: function(e, x, y) { | |
632 var clientRect = e.getBoundingClientRect(); | |
633 return x >= clientRect.left && x <= clientRect.right && | |
634 y >= clientRect.top && y <= clientRect.bottom; | |
635 }, | |
636 | |
637 /** | |
638 * Called when the page is scrolled; moves elements that are position:fixed | |
639 * but should only behave as if they are fixed for vertical scrolling. | |
640 * @private | |
641 */ | |
642 handleScroll_: function() { | |
643 this.updateAllFrozenElementPositions_(); | |
644 }, | |
645 | |
646 /** | |
647 * Updates all frozen pages to match the horizontal scroll position. | |
648 * @private | |
649 */ | |
650 updateAllFrozenElementPositions_: function() { | |
651 var frozenElements = document.querySelectorAll('.frozen'); | |
652 for (var i = 0; i < frozenElements.length; i++) | |
653 this.updateFrozenElementHorizontalPosition_(frozenElements[i]); | |
654 }, | |
655 | |
656 /** | |
657 * Updates the given frozen element to match the horizontal scroll position. | |
658 * @param {HTMLElement} e The frozen element to update. | |
659 * @private | |
660 */ | |
661 updateFrozenElementHorizontalPosition_: function(e) { | |
662 if (isRTL()) { | |
663 e.style.right = PageManager.horizontalOffset + 'px'; | |
664 } else { | |
665 var scrollLeft = scrollLeftForDocument(document); | |
666 e.style.left = PageManager.horizontalOffset - scrollLeft + 'px'; | |
667 } | |
668 }, | |
669 | |
670 /** | |
671 * Change the horizontal offset used to reposition elements while showing an | |
672 * overlay from the default. | |
673 */ | |
674 setHorizontalOffset: function(value) { | |
675 PageManager.horizontalOffset = value; | |
676 }, | |
677 | |
678 /** | |
679 * Whether the page is still loading (i.e. onload hasn't finished running). | |
680 * @return {boolean} Whether the page is still loading. | |
681 */ | |
682 isLoading: function() { | |
683 return document.documentElement.classList.contains('loading'); | |
684 }, | |
Dan Beam
2014/07/28 20:58:00
arguably these methods should be in public => prot
michaelpg
2014/07/29 01:08:31
Done. And I tried to arrange related methods toget
| |
685 }; | |
686 | |
687 // Export | |
688 return { | |
689 PageManager: PageManager | |
690 }; | |
691 }); | |
OLD | NEW |