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 |
+ }; |
+}); |