Chromium Code Reviews| Index: ui/webui/resources/js/cr/ui/page_manager/page_manager.js |
| diff --git a/ui/webui/resources/js/cr/ui/page_manager/page_manager.js b/ui/webui/resources/js/cr/ui/page_manager/page_manager.js |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..85253c68a5df768ea86f63cdd7adb48603c9e0b2 |
| --- /dev/null |
| +++ b/ui/webui/resources/js/cr/ui/page_manager/page_manager.js |
| @@ -0,0 +1,691 @@ |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
|
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?
|
| +cr.define('cr.ui.pageManager', function() { |
| + /** @const */ var FocusOutlineManager = cr.ui.FocusOutlineManager; |
| + |
| + var PageManager = { |
| + /** |
| + * Offset of page container in pixels, to allow room for side menu. |
| + * Simplified settings pages can override this if they don't use the menu. |
| + * The default (155) comes from -webkit-margin-start in uber_shared.css |
| + * 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.
|
| + * @private |
| + */ |
| + horizontalOffset: 155, |
| + |
| + /** |
| + * Main level option pages. Maps lower-case page names to the respective |
| + * page object. |
| + * @protected |
| + */ |
| + registeredPages: {}, |
| + |
| + /** |
| + * Pages which are meant to behave like modal dialogs. Maps lower-case |
| + * overlay names to the respective overlay object. |
| + * @protected |
| + */ |
| + registeredOverlayPages: {}, |
| + |
| + /** |
| + * True if page is served from a dialog. |
| + */ |
| + isDialog: false, |
| + |
| + /** |
| + * Gets the default page (to be shown on initial load). |
| + */ |
| + getDefaultPage: function() { |
| + 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
|
| + }, |
| + |
| + /** |
| + * Shows the default page. |
| + */ |
| + showDefaultPage: function() { |
| + this.navigateToPage(this.getDefaultPage().name); |
| + }, |
| + |
| + /** |
| + * "Navigates" to a page, meaning that the page will be shown and the |
| + * appropriate entry is placed in the history. |
| + * @param {string} pageName Page name. |
| + */ |
| + navigateToPage: function(pageName) { |
| + 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.
|
| + }, |
| + |
| + /** |
| + * Shows a registered page. This handles both top-level and overlay pages. |
| + * @param {string} pageName Page name. |
| + * @param {boolean} updateHistory True if we should update the history after |
| + * showing the page. |
| + * @param {Object=} opt_propertyBag An optional bag of properties including |
| + * replaceState (if history state should be replaced instead of pushed). |
| + * @private |
|
Dan Beam
2014/07/28 20:58:00
this isn't private
michaelpg
2014/07/29 01:08:31
Done.
|
| + */ |
| + showPageByName: function(pageName, |
| + updateHistory, |
| + opt_propertyBag) { |
| + // If |opt_propertyBag| is non-truthy, homogenize to object. |
| + 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
|
| + |
| + // If a bubble is currently being shown, hide it. |
| + this.hideBubble(); |
| + |
| + // Find the currently visible root-level page. |
| + var rootPage = null; |
| + for (var name in this.registeredPages) { |
| + var page = this.registeredPages[name]; |
| + if (page.visible && !page.parentPage) { |
| + rootPage = page; |
| + break; |
| + } |
| + } |
| + |
| + // Find the target page. |
| + var targetPage = this.registeredPages[pageName.toLowerCase()]; |
| + if (!targetPage || !targetPage.canShowPage()) { |
| + // If it's not a page, try it as an overlay. |
| + if (!targetPage && this.showOverlay_(pageName, rootPage)) { |
| + if (updateHistory) |
| + this.updateHistoryState_(!!opt_propertyBag.replaceState); |
| + this.updateTitle_(); |
| + return; |
| + } else { |
|
stevenjb
2014/07/28 22:27:23
nit: no else after return
michaelpg
2014/07/29 01:08:30
Done.
|
| + targetPage = this.getDefaultPage(); |
| + } |
| + } |
| + |
| + pageName = targetPage.name.toLowerCase(); |
| + var targetPageWasVisible = targetPage.visible; |
| + |
| + // Determine if the root page is 'sticky', meaning that it |
| + // shouldn't change when showing an overlay. This can happen for special |
| + // pages like Search. |
| + var isRootPageLocked = |
| + rootPage && rootPage.sticky && targetPage.parentPage; |
| + |
| + var allPageNames = Array.prototype.concat.call( |
| + Object.keys(this.registeredPages), |
| + Object.keys(this.registeredOverlayPages)); |
| + |
| + // Notify pages if they will be hidden. |
| + for (var i = 0; i < allPageNames.length; ++i) { |
| + var name = allPageNames[i]; |
| + var page = this.registeredPages[name] || |
| + this.registeredOverlayPages[name]; |
| + if (!page.parentPage && isRootPageLocked) |
| + continue; |
| + if (page.willHidePage && name != pageName && |
| + !page.isAncestorOfPage(targetPage)) { |
| + page.willHidePage(); |
| + } |
| + } |
| + |
| + // Update visibilities to show only the hierarchy of the target page. |
| + for (var i = 0; i < allPageNames.length; ++i) { |
| + var name = allPageNames[i]; |
| + var page = this.registeredPages[name] || |
| + this.registeredOverlayPages[name]; |
| + if (!page.parentPage && isRootPageLocked) |
| + continue; |
| + page.visible = name == pageName || page.isAncestorOfPage(targetPage); |
| + } |
| + |
| + // Update the history and current location. |
| + if (updateHistory) |
| + this.updateHistoryState_(!!opt_propertyBag.replaceState); |
| + |
| + // Update focus if any other control was focused on the previous page, |
| + // or the previous page is not known. |
| + if (document.activeElement != document.body && |
| + (!rootPage || rootPage.pageDiv.contains(document.activeElement))) { |
| + targetPage.focus(); |
| + } |
| + |
| + // Notify pages if they were shown. |
| + for (var i = 0; i < allPageNames.length; ++i) { |
| + var name = allPageNames[i]; |
| + var page = this.registeredPages[name] || |
| + this.registeredOverlayPages[name]; |
| + if (!page.parentPage && isRootPageLocked) |
| + 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
|
| + if (!targetPageWasVisible && page.didShowPage && |
| + (name == pageName || page.isAncestorOfPage(targetPage))) { |
| + page.didShowPage(); |
| + } |
| + } |
| + |
| + // Update the document title. Do this after didShowPage was called, in |
| + // case a page decides to change its title. |
| + this.updateTitle_(); |
| + }, |
| + |
| + /** |
| + * Scrolls the page to the correct position (the top when opening an |
| + * overaly, or the old scroll position a previously hidden overlay |
| + * becomes visible). |
| + * @private |
| + */ |
| + updateScrollPosition_: function() { |
| + var container = $('page-container'); |
| + var scrollTop = container.oldScrollTop || 0; |
| + container.oldScrollTop = undefined; |
| + window.scroll(scrollLeftForDocument(document), scrollTop); |
| + }, |
| + |
| + /** |
| + * Updates the title to title of the current page. |
| + * @private |
| + */ |
| + updateTitle_: function() { |
| + var page = this.getTopmostVisiblePage(); |
| + // 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.
|
| + uber.setTitle(page.title); |
| + }, |
| + |
| + /** |
| + * Pushes the current page onto the history stack, replacing the current |
| + * entry if appropriate. |
| + * @param {boolean} replace If true, allow no history events to be created. |
| + * @param {object=} opt_params A bag of optional params, including: |
| + * {boolean} ignoreHash Whether to include the hash or not. |
| + * @private |
| + */ |
| + updateHistoryState_: function(replace, opt_params) { |
| + if (PageManager.isDialog) |
| + return; |
| + |
| + var page = this.getTopmostVisiblePage(); |
| + var path = window.location.pathname + window.location.hash; |
| + if (path) { |
| + // Remove trailing slash. |
| + path = path.slice(1).replace(/\/(?:#|$)/, ''); |
| + } |
| + |
| + // If the page is already in history (the user may have clicked the same |
| + // link twice, or this is the initial load), do nothing. |
| + var hash = opt_params && opt_params.ignoreHash ? |
| + '' : window.location.hash; |
| + var newPath = (page == this.getDefaultPage() ? '' : page.name) + hash; |
| + if (path == newPath) |
| + return; |
| + |
| + // 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.
|
| + var historyFunction = replace ? uber.replaceState : uber.pushState; |
| + historyFunction.call(uber, {}, newPath); |
| + }, |
| + |
| + /** |
| + * Shows a registered Overlay page. Does not update history. |
| + * @param {string} overlayName Page name. |
| + * @param {OptionPage} rootPage The currently visible root-level page. |
| + * @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.
|
| + */ |
| + showOverlay_: function(overlayName, rootPage) { |
| + var overlay = this.registeredOverlayPages[overlayName.toLowerCase()]; |
| + if (!overlay || !overlay.canShowPage()) |
| + return false; |
| + |
| + // Save the currently focused element in the page for restoration later. |
| + var currentPage = this.getTopmostVisiblePage(); |
| + if (currentPage) |
| + currentPage.lastFocusedElement = document.activeElement; |
| + |
| + if ((!rootPage || !rootPage.sticky) && |
| + overlay.parentPage && |
| + !overlay.parentPage.visible) { |
| + this.showPageByName(overlay.parentPage.name, false); |
| + } |
| + |
| + if (!overlay.visible) { |
| + overlay.visible = true; |
| + 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
|
| + } |
| + |
| + // Change focus to the overlay if any other control was focused by |
| + // keyboard before. Otherwise, no one should have focus. |
| + if (document.activeElement != document.body) { |
| + if (FocusOutlineManager.forDocument(document).visible) { |
| + overlay.focus(); |
| + } else if (!overlay.pageDiv.contains(document.activeElement)) { |
| + document.activeElement.blur(); |
| + } |
| + } |
| + |
| + if ($('search-field') && $('search-field').value == '') { |
| + var section = overlay.associatedSection; |
| + if (section) |
| + options.BrowserOptions.scrollToSection(section); |
| + } |
| + |
| + return true; |
| + }, |
| + |
| + /** |
| + * Returns whether or not an overlay is visible. |
| + * @return {boolean} True if an overlay is visible. |
| + * @private |
| + */ |
| + isOverlayVisible_: function() { |
| + return this.getVisibleOverlay_() != null; |
| + }, |
| + |
| + /** |
| + * Returns the currently visible overlay, or null if no page is visible. |
| + * @return {OptionPage} The visible overlay. |
| + */ |
| + getVisibleOverlay_: function() { |
| + var topmostPage = null; |
| + for (var name in this.registeredOverlayPages) { |
| + var page = this.registeredOverlayPages[name]; |
| + if (page.visible && |
| + (!topmostPage || page.nestingLevel > topmostPage.nestingLevel)) { |
| + topmostPage = page; |
| + } |
| + } |
| + return topmostPage; |
| + }, |
| + |
| + /** |
| + * Restores the last focused element on a given page. |
| + */ |
| + restoreLastFocusedElement_: function() { |
| + var currentPage = this.getTopmostVisiblePage(); |
| + if (currentPage.lastFocusedElement) |
| + currentPage.lastFocusedElement.focus(); |
| + }, |
| + |
| + /** |
| + * Closes the visible overlay. Updates the history state after closing the |
| + * overlay. |
| + */ |
| + closeOverlay: function() { |
| + var overlay = this.getVisibleOverlay_(); |
| + if (!overlay) |
| + return; |
| + |
| + overlay.visible = false; |
| + |
| + 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
|
| + this.updateHistoryState_(false, {ignoreHash: true}); |
| + this.updateTitle_(); |
| + |
| + this.restoreLastFocusedElement_(); |
| + }, |
| + |
| + /** |
| + * Closes all overlays and updates the history after each closed overlay. |
| + */ |
| + closeAllOverlays: function() { |
| + while (this.isOverlayVisible_()) { |
| + this.closeOverlay(); |
| + } |
| + }, |
| + |
| + /** |
| + * Cancels (closes) the overlay, due to the user pressing <Esc>. |
| + */ |
| + cancelOverlay: function() { |
| + // Blur the active element to ensure any changed pref value is saved. |
| + document.activeElement.blur(); |
| + var overlay = this.getVisibleOverlay_(); |
| + // Let the overlay handle the <Esc> if it wants to. |
| + if (overlay.handleCancel) { |
| + overlay.handleCancel(); |
| + this.restoreLastFocusedElement_(); |
| + } else { |
| + this.closeOverlay(); |
| + } |
| + }, |
| + |
| + /** |
| + * Hides the visible overlay. Does not affect the history state. |
| + * @private |
| + */ |
| + hideOverlay_: function() { |
| + var overlay = this.getVisibleOverlay_(); |
| + if (overlay) |
| + overlay.visible = false; |
| + }, |
| + |
| + /** |
| + * Returns the pages which are currently visible, ordered by nesting level |
| + * (ascending). |
| + * @return {Array.OptionPage} The pages which are currently visible, ordered |
| + * by nesting level (ascending). |
| + */ |
| + getVisiblePages_: function() { |
| + var visiblePages = []; |
| + for (var name in this.registeredPages) { |
| + var page = this.registeredPages[name]; |
| + if (page.visible) |
| + visiblePages[page.nestingLevel] = page; |
| + } |
| + return visiblePages; |
| + }, |
| + |
| + /** |
| + * Returns the topmost visible page (overlays excluded). |
| + * @return {OptionPage} The topmost visible page aside any overlay. |
| + * @private |
| + */ |
| + getTopmostVisibleNonOverlayPage_: function() { |
| + var topPage = null; |
| + for (var name in this.registeredPages) { |
| + var page = this.registeredPages[name]; |
| + if (page.visible && |
| + (!topPage || page.nestingLevel > topPage.nestingLevel)) |
| + topPage = page; |
| + } |
| + |
| + return topPage; |
| + }, |
| + |
| + /** |
| + * Returns the topmost visible page, or null if no page is visible. |
| + * @return {OptionPage} The topmost visible page. |
| + */ |
| + getTopmostVisiblePage: function() { |
| + // Check overlays first since they're top-most if visible. |
| + return this.getVisibleOverlay_() || |
| + this.getTopmostVisibleNonOverlayPage_(); |
| + }, |
| + |
| + /** |
| + * Returns the currently visible bubble, or null if no bubble is visible. |
| + * @return {AutoCloseBubble} The bubble currently being shown. |
| + */ |
| + getVisibleBubble: function() { |
| + var bubble = PageManager.bubble_; |
| + return bubble && !bubble.hidden ? bubble : null; |
| + }, |
| + |
| + /** |
| + * Shows an informational bubble displaying |content| and pointing at the |
| + * |target| element. If |content| has focusable elements, they join the |
| + * current page's tab order as siblings of |domSibling|. |
| + * @param {HTMLDivElement} content The content of the bubble. |
| + * @param {HTMLElement} target The element at which the bubble points. |
| + * @param {HTMLElement} domSibling The element after which the bubble is |
| + * added to the DOM. |
| + * @param {cr.ui.ArrowLocation} location The arrow location. |
| + */ |
| + showBubble: function(content, target, domSibling, location) { |
| + PageManager.hideBubble(); |
| + |
| + var bubble = new cr.ui.AutoCloseBubble; |
| + bubble.anchorNode = target; |
| + bubble.domSibling = domSibling; |
| + bubble.arrowLocation = location; |
| + bubble.content = content; |
| + bubble.show(); |
| + PageManager.bubble_ = bubble; |
| + }, |
| + |
| + /** |
| + * Hides the currently visible bubble, if any. |
| + */ |
| + hideBubble: function() { |
| + if (PageManager.bubble_) |
| + PageManager.bubble_.hide(); |
| + }, |
| + |
| + /** |
| + * Registers new page. |
| + * @param {cr.ui.page_manager.Page} page Page to register. |
| + */ |
| + register: function(page) { |
| + this.registeredPages[page.name.toLowerCase()] = page; |
| + page.initializePage(); |
| + }, |
| + |
| + /** |
| + * Find an enclosing section for an element if it exists. |
| + * @param {Element} element Element to search. |
| + * @return {OptionPage} The section element, or null. |
| + * @private |
| + */ |
| + findSectionForNode_: function(node) { |
| + while (node = node.parentNode) { |
| + if (node.nodeName == 'SECTION') |
| + return node; |
| + } |
| + return null; |
| + }, |
| + |
| + /** |
| + * Registers a new Overlay page. |
| + * @param {cr.ui.page_manager.Page} overlay Overlay to register. |
| + * @param {cr.ui.page_manager.Page} parentPage Associated parent page for |
| + * this overlay. |
| + * @param {Array} associatedControls Array of control elements associated |
| + * with this page. |
| + */ |
| + registerOverlay: function(overlay, |
| + parentPage, |
| + associatedControls) { |
| + this.registeredOverlayPages[overlay.name.toLowerCase()] = overlay; |
| + overlay.parentPage = parentPage; |
| + if (associatedControls) { |
| + overlay.associatedControls = associatedControls; |
| + if (associatedControls.length) { |
| + overlay.associatedSection = |
| + this.findSectionForNode_(associatedControls[0]); |
| + } |
| + |
| + // Sanity check. |
| + for (var i = 0; i < associatedControls.length; ++i) { |
| + assert(associatedControls[i], 'Invalid element passed.'); |
| + } |
| + } |
| + |
| + // Reverse the button strip for Windows and CrOS. See the documentation of |
| + // reverseButtonStripIfNecessary_() for an explanation of why this is |
| + // done. |
| + if (cr.isWindows || cr.isChromeOS) |
| + this.reverseButtonStripIfNecessary_(overlay); |
| + |
| + overlay.tab = undefined; |
| + overlay.isOverlay = true; |
| + overlay.initializePage(); |
| + }, |
| + |
| + /** |
| + * Reverses the child elements of a button strip if it hasn't already been |
| + * reversed. This is necessary because WebKit does not alter the tab order |
| + * for elements that are visually reversed using |
| + * -webkit-box-direction: reverse, and the button order is reversed for |
| + * views. See http://webk.it/62664 for more information. |
| + * @param {Object} overlay The overlay containing the button strip to |
| + * reverse. |
| + * @private |
| + */ |
| + reverseButtonStripIfNecessary_: function(overlay) { |
| + var buttonStrips = |
| + overlay.pageDiv.querySelectorAll('.button-strip:not([reversed])'); |
| + |
| + // Reverse all button-strips in the overlay. |
| + for (var j = 0; j < buttonStrips.length; j++) { |
| + var buttonStrip = buttonStrips[j]; |
| + |
| + var childNodes = buttonStrip.childNodes; |
| + for (var i = childNodes.length - 1; i >= 0; i--) |
| + buttonStrip.appendChild(childNodes[i]); |
| + |
| + buttonStrip.setAttribute('reversed', ''); |
| + } |
| + }, |
| + |
| + /** |
| + * Returns the name of the page from the current path. |
| + */ |
| + getPageNameFromPath: function() { |
| + var path = location.pathname; |
| + if (path.length <= 1) |
| + return this.getDefaultPage().name; |
| + |
| + // Skip starting slash and remove trailing slash (if any). |
| + return path.slice(1).replace(/\/$/, ''); |
| + }, |
| + |
| + /** |
| + * Callback for window.onpopstate to handle back/forward navigations. |
| + * @param {string} pageName The current page name. |
| + * @param {Object} data State data pushed into history. |
| + */ |
| + setState: function(pageName, data) { |
| + var currentOverlay = this.getVisibleOverlay_(); |
| + var lowercaseName = pageName.toLowerCase(); |
| + var newPage = this.registeredPages[lowercaseName] || |
| + this.registeredOverlayPages[lowercaseName] || |
| + this.getDefaultPage(); |
| + if (currentOverlay && !currentOverlay.isAncestorOfPage(newPage)) { |
| + currentOverlay.visible = false; |
| + if (currentOverlay.didClosePage) currentOverlay.didClosePage(); |
| + } |
| + this.showPageByName(pageName, false); |
| + }, |
| + |
| + /** |
| + * Callback for window.onbeforeunload. Used to notify overlays that they |
| + * will be closed. |
| + */ |
| + willClose: function() { |
| + var overlay = this.getVisibleOverlay_(); |
| + if (overlay && overlay.didClosePage) |
| + overlay.didClosePage(); |
| + }, |
| + |
| + /** |
| + * Freezes/unfreezes the scroll position of the root page container. |
| + * @param {boolean} freeze Whether the page should be frozen. |
| + * @private |
| + */ |
| + setRootPageFrozen_: function(freeze) { |
| + var container = $('page-container'); |
| + if (container.classList.contains('frozen') == freeze) |
| + return; |
| + |
| + if (freeze) { |
| + // Lock the width, since auto width computation may change. |
| + container.style.width = window.getComputedStyle(container).width; |
| + container.oldScrollTop = scrollTopForDocument(document); |
| + container.classList.add('frozen'); |
| + var verticalPosition = |
| + container.getBoundingClientRect().top - container.oldScrollTop; |
| + container.style.top = verticalPosition + 'px'; |
| + this.updateFrozenElementHorizontalPosition_(container); |
| + } else { |
| + container.classList.remove('frozen'); |
| + container.style.top = ''; |
| + container.style.left = ''; |
| + container.style.right = ''; |
| + container.style.width = ''; |
| + } |
| + }, |
| + |
| + /** |
| + * Freezes/unfreezes the scroll position of the root page based on the |
| + * current page stack. |
| + */ |
| + updateRootPageFreezeState: function() { |
| + var topPage = PageManager.getTopmostVisiblePage(); |
| + if (topPage) |
| + this.setRootPageFrozen_(topPage.isOverlay); |
| + }, |
| + |
| + /** |
| + * Initializes the complete page. |
| + */ |
| + initialize: function() { |
| + FocusOutlineManager.forDocument(document); |
| + document.addEventListener('scroll', this.handleScroll_.bind(this)); |
| + |
| + // Trigger the scroll handler manually to set the initial state. |
| + this.handleScroll_(); |
| + |
| + // Shake the dialog if the user clicks outside the dialog bounds. |
| + var containers = [$('overlay-container-1'), $('overlay-container-2')]; |
| + for (var i = 0; i < containers.length; i++) { |
| + var overlay = containers[i]; |
| + cr.ui.overlay.setupOverlay(overlay); |
| + overlay.addEventListener('cancelOverlay', |
| + PageManager.cancelOverlay.bind(PageManager)); |
| + } |
| + |
| + cr.ui.overlay.globalInitialization(); |
| + }, |
| + |
| + /** |
| + * Does a bounds check for the element on the given x, y client coordinates. |
| + * @param {Element} e The DOM element. |
| + * @param {number} x The client X to check. |
| + * @param {number} y The client Y to check. |
| + * @return {boolean} True if the point falls within the element's bounds. |
| + * @private |
| + */ |
| + elementContainsPoint_: function(e, x, y) { |
| + var clientRect = e.getBoundingClientRect(); |
| + return x >= clientRect.left && x <= clientRect.right && |
| + y >= clientRect.top && y <= clientRect.bottom; |
| + }, |
| + |
| + /** |
| + * Called when the page is scrolled; moves elements that are position:fixed |
| + * but should only behave as if they are fixed for vertical scrolling. |
| + * @private |
| + */ |
| + handleScroll_: function() { |
| + this.updateAllFrozenElementPositions_(); |
| + }, |
| + |
| + /** |
| + * Updates all frozen pages to match the horizontal scroll position. |
| + * @private |
| + */ |
| + updateAllFrozenElementPositions_: function() { |
| + var frozenElements = document.querySelectorAll('.frozen'); |
| + for (var i = 0; i < frozenElements.length; i++) |
| + this.updateFrozenElementHorizontalPosition_(frozenElements[i]); |
| + }, |
| + |
| + /** |
| + * Updates the given frozen element to match the horizontal scroll position. |
| + * @param {HTMLElement} e The frozen element to update. |
| + * @private |
| + */ |
| + updateFrozenElementHorizontalPosition_: function(e) { |
| + if (isRTL()) { |
| + e.style.right = PageManager.horizontalOffset + 'px'; |
| + } else { |
| + var scrollLeft = scrollLeftForDocument(document); |
| + e.style.left = PageManager.horizontalOffset - scrollLeft + 'px'; |
| + } |
| + }, |
| + |
| + /** |
| + * Change the horizontal offset used to reposition elements while showing an |
| + * overlay from the default. |
| + */ |
| + setHorizontalOffset: function(value) { |
| + PageManager.horizontalOffset = value; |
| + }, |
| + |
| + /** |
| + * Whether the page is still loading (i.e. onload hasn't finished running). |
| + * @return {boolean} Whether the page is still loading. |
| + */ |
| + isLoading: function() { |
| + return document.documentElement.classList.contains('loading'); |
| + }, |
|
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
|
| + }; |
| + |
| + // Export |
| + return { |
| + PageManager: PageManager |
| + }; |
| +}); |