/**
* Observers will be notified when opening and closing overlays.
- * @type {!Array.<!PageManager.Observer>}
+ * @type {!Array.<!cr.ui.pageManager.PageManager.Observer>}
*/
observers_: [],
/**
* Registers new page.
- * @param {cr.ui.page_manager.Page} page Page to register.
+ * @param {!cr.ui.pageManager.Page} page Page to register.
*/
register: function(page) {
this.registeredPages[page.name.toLowerCase()] = page;
/**
* 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
+ * @param {!cr.ui.pageManager.Page} overlay Overlay to register.
+ * @param {cr.ui.pageManager.Page} parentPage Associated parent page for
* this overlay.
* @param {Array} associatedControls Array of control elements associated
* with this page.
overlay.tab = undefined;
overlay.isOverlay = true;
- // Reverse the button strip for Windows and CrOS. See the documentation of
- // cr.ui.pageManager.Page.reverseButtonStrip() for an explanation of why
- // this is done.
- if (cr.isWindows || cr.isChromeOS)
- overlay.reverseButtonStrip();
-
+ overlay.reverseButtonStrip();
overlay.initializePage();
},
* showing the page (defaults to true).
* @param {Object=} opt_propertyBag An optional bag of properties including
* replaceState (if history state should be replaced instead of pushed).
+ * hash (a hash state to attach to the page).
*/
showPageByName: function(pageName,
opt_updateHistory,
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)) {
+ var hash = opt_propertyBag.hash || '';
+ if (!targetPage && this.showOverlay_(pageName, hash, rootPage)) {
if (opt_updateHistory)
this.updateHistoryState_(!!opt_propertyBag.replaceState);
this.updateTitle_();
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.
- // TODO(michaelpg): Resolve code duplication.
- 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 &&
- !this.isAncestorOfPage(page, targetPage)) {
+ this.forEachPage_(!isRootPageLocked, function(page) {
+ if (page.name != pageName && !this.isAncestorOfPage(page, targetPage))
page.willHidePage();
- }
- }
+ });
+
+ // Update the page's hash.
+ targetPage.hash = opt_propertyBag.hash || '';
// 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 ||
+ this.forEachPage_(!isRootPageLocked, function(page) {
+ page.visible = page.name == pageName ||
this.isAncestorOfPage(page, targetPage);
- }
+ });
// Update the history and current location.
if (opt_updateHistory)
}
// 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;
- if (!targetPageWasVisible && page.didShowPage &&
- (name == pageName || this.isAncestorOfPage(page, targetPage))) {
+ this.forEachPage_(!isRootPageLocked, function(page) {
+ if (!targetPageWasVisible &&
+ (page.name == pageName ||
+ this.isAncestorOfPage(page, targetPage))) {
page.didShowPage();
}
- }
+ });
+
+ // If the target page was already visible, notify it that its hash
+ // changed externally.
+ if (targetPageWasVisible)
+ targetPage.didChangeHash();
// Update the document title. Do this after didShowPage was called, in
// case a page decides to change its title.
},
/**
+ * Called when a page's hash changes. If the page is the topmost visible
+ * page, the history state is updated.
+ * @param {cr.ui.pageManager.Page} page The page whose hash has changed.
+ */
+ onPageHashChanged: function(page) {
+ if (page == this.getTopmostVisiblePage())
+ this.updateHistoryState_(false);
+ },
+
+ /**
* Returns the topmost visible page, or null if no page is visible.
* @return {cr.ui.pageManager.Page} The topmost visible page.
*/
return;
overlay.visible = false;
+ overlay.didClosePage();
- if (overlay.didClosePage)
- overlay.didClosePage();
- this.updateHistoryState_(false, {ignoreHash: true});
+ this.updateHistoryState_(false);
this.updateTitle_();
this.restoreLastFocusedElement_();
/**
* Returns the currently visible bubble, or null if no bubble is visible.
- * @return {AutoCloseBubble} The bubble currently being shown.
+ * @return {cr.ui.AutoCloseBubble} The bubble currently being shown.
*/
getVisibleBubble: function() {
var bubble = this.bubble_;
/**
* Callback for window.onpopstate to handle back/forward navigations.
* @param {string} pageName The current page name.
+ * @param {string} hash The hash to pass into the page.
* @param {Object} data State data pushed into history.
*/
- setState: function(pageName, data) {
+ setState: function(pageName, hash, data) {
var currentOverlay = this.getVisibleOverlay_();
var lowercaseName = pageName.toLowerCase();
var newPage = this.registeredPages[lowercaseName] ||
this.defaultPage_;
if (currentOverlay && !this.isAncestorOfPage(currentOverlay, newPage)) {
currentOverlay.visible = false;
- if (currentOverlay.didClosePage) currentOverlay.didClosePage();
+ currentOverlay.didClosePage();
}
- this.showPageByName(pageName, false);
+ this.showPageByName(pageName, false, {hash: hash});
},
*/
willClose: function() {
var overlay = this.getVisibleOverlay_();
- if (overlay && overlay.didClosePage)
+ if (overlay)
overlay.didClosePage();
},
},
/**
- * @param {PageManager.Observer} observer The observer to register.
+ * @param {!cr.ui.pageManager.PageManager.Observer} observer The observer to
+ * register.
*/
addObserver: function(observer) {
this.observers_.push(observer);
/**
* Shows a registered overlay page. Does not update history.
* @param {string} overlayName Page name.
+ * @param {string} hash The hash state to associate with the overlay.
* @param {cr.ui.pageManager.Page} rootPage The currently visible root-level
* page.
* @return {boolean} Whether we showed an overlay.
* @private
*/
- showOverlay_: function(overlayName, rootPage) {
+ showOverlay_: function(overlayName, hash, rootPage) {
var overlay = this.registeredOverlayPages[overlayName.toLowerCase()];
if (!overlay || !overlay.canShowPage())
return false;
this.showPageByName(overlay.parentPage.name, false);
}
+ overlay.hash = hash;
if (!overlay.visible) {
overlay.visible = true;
- if (overlay.didShowPage)
- overlay.didShowPage();
+ overlay.didShowPage();
+ } else {
+ overlay.didChangeHash();
}
// Change focus to the overlay if any other control was focused by
* to update the history.
* @param {boolean} replace If true, handlers should replace the current
* history event rather than create new ones.
- * @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) {
+ updateHistoryState_: function(replace) {
if (this.isDialog)
return;
// 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.defaultPage_ ? '' : page.name) + hash;
+ var newPath = (page == this.defaultPage_ ? '' : page.name) + page.hash;
if (path == newPath)
return;
/**
* Find an enclosing section for an element if it exists.
- * @param {Element} element Element to search.
- * @return {Element} The section element, or null.
+ * @param {Node} node Element to search.
+ * @return {Node} The section element, or null.
* @private
*/
findSectionForNode_: function(node) {
e.style.left = this.horizontalOffset - scrollLeft + 'px';
}
},
+
+ /**
+ * Calls the given callback with each registered page.
+ * @param {boolean} includeRootPages Whether the callback should be called
+ * for the root pages.
+ * @param {function(cr.ui.pageManager.Page)} callback The callback.
+ * @private
+ */
+ forEachPage_: function(includeRootPages, callback) {
+ var pageNames = Object.keys(this.registeredOverlayPages);
+ if (includeRootPages)
+ pageNames = Object.keys(this.registeredPages).concat(pageNames);
+
+ pageNames.forEach(function(name) {
+ callback.call(this, this.registeredOverlayPages[name] ||
+ this.registeredPages[name]);
+ }, this);
+ },
};
/**