cr.define('options', function() {
/** @const */ var FocusOutlineManager = cr.ui.FocusOutlineManager;
+ /** @const */ var PageManager = cr.ui.pageManager.PageManager;
- /////////////////////////////////////////////////////////////////////////////
- // OptionsPage class:
-
- /**
- * Base class for options page.
- * @constructor
- * @param {string} name Options page name.
- * @param {string} title Options page title, used for history.
- * @extends {EventTarget}
- */
- function OptionsPage(name, title, pageDivName) {
- this.name = name;
- this.title = title;
- this.pageDivName = pageDivName;
- this.pageDiv = $(this.pageDivName);
- // |pageDiv.page| is set to the page object (this) when the page is visible
- // to track which page is being shown when multiple pages can share the same
- // underlying div.
- this.pageDiv.page = null;
- this.tab = null;
- this.lastFocusedElement = null;
- }
-
- /**
- * This is the absolute difference maintained between standard and
- * fixed-width font sizes. Refer http://crbug.com/91922.
- * @const
- */
- OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD = 3;
-
- /**
- * 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
- * @private
- */
- OptionsPage.horizontalOffset = 155;
-
- /**
- * Main level option pages. Maps lower-case page names to the respective page
- * object.
- * @protected
- */
- OptionsPage.registeredPages = {};
-
- /**
- * Pages which are meant to behave like modal dialogs. Maps lower-case overlay
- * names to the respective overlay object.
- * @protected
- */
- OptionsPage.registeredOverlayPages = {};
-
- /**
- * True if options page is served from a dialog.
- */
- OptionsPage.isDialog = false;
-
- /**
- * Gets the default page (to be shown on initial load).
- */
- OptionsPage.getDefaultPage = function() {
- return BrowserOptions.getInstance();
- };
-
- /**
- * Shows the default page.
- */
- OptionsPage.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.
- */
- OptionsPage.navigateToPage = function(pageName) {
- this.showPageByName(pageName, true);
- };
-
- /**
- * 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
- */
- OptionsPage.showPageByName = function(pageName,
- updateHistory,
- opt_propertyBag) {
- // If |opt_propertyBag| is non-truthy, homogenize to object.
- opt_propertyBag = opt_propertyBag || {};
-
- // 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 {
- 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;
- 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 overlay,
- * or the old scroll position a previously hidden overlay becomes visible).
- * @private
- */
- OptionsPage.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
- */
- OptionsPage.updateTitle_ = function() {
- var page = this.getTopmostVisiblePage();
- 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
- */
- OptionsPage.updateHistoryState_ = function(replace, opt_params) {
- if (OptionsPage.isDialog)
- return;
-
- var page = this.getTopmostVisiblePage();
- var path = window.location.pathname + window.location.hash;
- if (path)
- path = path.slice(1).replace(/\/(?:#|$)/, ''); // Remove trailing slash.
-
- // 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;
-
- 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.
- */
- OptionsPage.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();
- }
-
- // 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
- */
- OptionsPage.isOverlayVisible_ = function() {
- return this.getVisibleOverlay_() != null;
- };
-
- /**
- * Returns the currently visible overlay, or null if no page is visible.
- * @return {OptionPage} The visible overlay.
- */
- OptionsPage.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.
- */
- OptionsPage.restoreLastFocusedElement_ = function() {
- var currentPage = this.getTopmostVisiblePage();
- if (currentPage.lastFocusedElement)
- currentPage.lastFocusedElement.focus();
- };
-
- /**
- * Closes the visible overlay. Updates the history state after closing the
- * overlay.
- */
- OptionsPage.closeOverlay = function() {
- var overlay = this.getVisibleOverlay_();
- if (!overlay)
- return;
-
- overlay.visible = false;
-
- if (overlay.didClosePage) overlay.didClosePage();
- this.updateHistoryState_(false, {ignoreHash: true});
- this.updateTitle_();
-
- this.restoreLastFocusedElement_();
- };
-
- /**
- * Closes all overlays and updates the history after each closed overlay.
- */
- OptionsPage.closeAllOverlays = function() {
- while (this.isOverlayVisible_()) {
- this.closeOverlay();
- }
- };
-
- /**
- * Cancels (closes) the overlay, due to the user pressing <Esc>.
- */
- OptionsPage.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
- */
- OptionsPage.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).
- */
- OptionsPage.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
- */
- OptionsPage.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.
- */
- OptionsPage.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.
- */
- OptionsPage.getVisibleBubble = function() {
- var bubble = OptionsPage.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.
- */
- OptionsPage.showBubble = function(content, target, domSibling, location) {
- OptionsPage.hideBubble();
-
- var bubble = new cr.ui.AutoCloseBubble;
- bubble.anchorNode = target;
- bubble.domSibling = domSibling;
- bubble.arrowLocation = location;
- bubble.content = content;
- bubble.show();
- OptionsPage.bubble_ = bubble;
- };
-
- /**
- * Hides the currently visible bubble, if any.
- */
- OptionsPage.hideBubble = function() {
- if (OptionsPage.bubble_)
- OptionsPage.bubble_.hide();
- };
-
- /**
- * Shows the tab contents for the given navigation tab.
- * @param {!Element} tab The tab that the user clicked.
- */
- OptionsPage.showTab = function(tab) {
- // Search parents until we find a tab, or the nav bar itself. This allows
- // tabs to have child nodes, e.g. labels in separately-styled spans.
- while (tab && !tab.classList.contains('subpages-nav-tabs') &&
- !tab.classList.contains('tab')) {
- tab = tab.parentNode;
- }
- if (!tab || !tab.classList.contains('tab'))
- return;
-
- // Find tab bar of the tab.
- var tabBar = tab;
- while (tabBar && !tabBar.classList.contains('subpages-nav-tabs')) {
- tabBar = tabBar.parentNode;
- }
- if (!tabBar)
- return;
-
- if (tabBar.activeNavTab != null) {
- tabBar.activeNavTab.classList.remove('active-tab');
- $(tabBar.activeNavTab.getAttribute('tab-contents')).classList.
- remove('active-tab-contents');
- }
-
- tab.classList.add('active-tab');
- $(tab.getAttribute('tab-contents')).classList.add('active-tab-contents');
- tabBar.activeNavTab = tab;
- };
-
- /**
- * Registers new options page.
- * @param {OptionsPage} page Page to register.
- */
- OptionsPage.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
- */
- OptionsPage.findSectionForNode_ = function(node) {
- while (node = node.parentNode) {
- if (node.nodeName == 'SECTION')
- return node;
- }
- return null;
- };
-
- /**
- * Registers a new Overlay page.
- * @param {OptionsPage} overlay Overlay to register.
- * @param {OptionsPage} parentPage Associated parent page for this overlay.
- * @param {Array} associatedControls Array of control elements associated with
- * this page.
- */
- OptionsPage.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
- */
- OptionsPage.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.
- */
- OptionsPage.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.
- */
- OptionsPage.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.
- */
- OptionsPage.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
- */
- OptionsPage.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.
- */
- OptionsPage.updateRootPageFreezeState = function() {
- var topPage = OptionsPage.getTopmostVisiblePage();
- if (topPage)
- this.setRootPageFrozen_(topPage.isOverlay);
- };
-
- /**
- * Initializes the complete options page. This will cause all C++ handlers to
- * be invoked to do final setup.
- */
- OptionsPage.initialize = function() {
- chrome.send('coreOptionsInitialize');
- uber.onContentFrameLoaded();
- 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',
- OptionsPage.cancelOverlay.bind(OptionsPage));
- }
-
- 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
- */
- OptionsPage.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
- */
- OptionsPage.handleScroll_ = function() {
- this.updateAllFrozenElementPositions_();
- };
-
- /**
- * Updates all frozen pages to match the horizontal scroll position.
- * @private
- */
- OptionsPage.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
- */
- OptionsPage.updateFrozenElementHorizontalPosition_ = function(e) {
- if (isRTL()) {
- e.style.right = OptionsPage.horizontalOffset + 'px';
- } else {
- var scrollLeft = scrollLeftForDocument(document);
- e.style.left = OptionsPage.horizontalOffset - scrollLeft + 'px';
- }
- };
-
- /**
- * Change the horizontal offset used to reposition elements while showing an
- * overlay from the default.
- */
- OptionsPage.setHorizontalOffset = function(value) {
- OptionsPage.horizontalOffset = value;
- };
-
- OptionsPage.setClearPluginLSODataEnabled = function(enabled) {
- if (enabled) {
- document.documentElement.setAttribute(
- 'flashPluginSupportsClearSiteData', '');
- } else {
- document.documentElement.removeAttribute(
- 'flashPluginSupportsClearSiteData');
- }
- if (navigator.plugins['Shockwave Flash'])
- document.documentElement.setAttribute('hasFlashPlugin', '');
- };
-
- OptionsPage.setPepperFlashSettingsEnabled = function(enabled) {
- if (enabled) {
- document.documentElement.setAttribute(
- 'enablePepperFlashSettings', '');
- } else {
- document.documentElement.removeAttribute(
- 'enablePepperFlashSettings');
- }
- };
-
- OptionsPage.setIsSettingsApp = function() {
- document.documentElement.classList.add('settings-app');
- };
-
- OptionsPage.isSettingsApp = function() {
- return document.documentElement.classList.contains('settings-app');
- };
-
- /**
- * Whether the page is still loading (i.e. onload hasn't finished running).
- * @return {boolean} Whether the page is still loading.
- */
- OptionsPage.isLoading = function() {
- return document.documentElement.classList.contains('loading');
- };
-
- OptionsPage.prototype = {
- __proto__: cr.EventTarget.prototype,
-
- /**
- * The parent page of this option page, or null for top-level pages.
- * @type {OptionsPage}
- */
- parentPage: null,
-
- /**
- * The section on the parent page that is associated with this page.
- * Can be null.
- * @type {Element}
- */
- associatedSection: null,
-
- /**
- * An array of controls that are associated with this page. The first
- * control should be located on a top-level page.
- * @type {OptionsPage}
- */
- associatedControls: null,
-
- /**
- * Initializes page content.
- */
- initializePage: function() {},
-
- /**
- * Sets focus on the first focusable element. Override for a custom focus
- * strategy.
- */
- focus: function() {
- // Do not change focus if any control on this page is already focused.
- if (this.pageDiv.contains(document.activeElement))
- return;
-
- var elements = this.pageDiv.querySelectorAll(
- 'input, list, select, textarea, button');
- for (var i = 0; i < elements.length; i++) {
- var element = elements[i];
- // Try to focus. If fails, then continue.
- element.focus();
- if (document.activeElement == element)
- return;
- }
- },
-
+ var OptionsPage = {
/**
- * Gets the container div for this page if it is an overlay.
- * @type {HTMLElement}
+ * This is the absolute difference maintained between standard and
+ * fixed-width font sizes. Refer http://crbug.com/91922.
+ * @const
*/
- get container() {
- assert(this.isOverlay);
- return this.pageDiv.parentNode;
- },
+ SIZE_DIFFERENCE_FIXED_STANDARD: 3,
/**
- * Gets page visibility state.
- * @type {boolean}
+ * Initializes the complete options page. This will cause all C++ handlers
+ * to be invoked to do final setup.
*/
- get visible() {
- // If this is an overlay dialog it is no longer considered visible while
- // the overlay is fading out. See http://crbug.com/118629.
- if (this.isOverlay &&
- this.container.classList.contains('transparent')) {
- return false;
- }
- if (this.pageDiv.hidden)
- return false;
- return this.pageDiv.page == this;
+ initialize: function() {
+ chrome.send('coreOptionsInitialize');
},
/**
- * Sets page visibility.
- * @type {boolean}
+ * Shows the tab contents for the given navigation tab.
+ * @param {!Element} tab The tab that the user clicked.
*/
- set visible(visible) {
- if ((this.visible && visible) || (!this.visible && !visible))
+ showTab: function(tab) {
+ // Search parents until we find a tab, or the nav bar itself. This allows
+ // tabs to have child nodes, e.g. labels in separately-styled spans.
+ while (tab && !tab.classList.contains('subpages-nav-tabs') &&
+ !tab.classList.contains('tab')) {
+ tab = tab.parentNode;
+ }
+ if (!tab || !tab.classList.contains('tab'))
return;
- // If using an overlay, the visibility of the dialog is toggled at the
- // same time as the overlay to show the dialog's out transition. This
- // is handled in setOverlayVisible.
- if (this.isOverlay) {
- this.setOverlayVisible_(visible);
- } else {
- this.pageDiv.page = this;
- this.pageDiv.hidden = !visible;
- this.onVisibilityChanged_();
+ // Find tab bar of the tab.
+ var tabBar = tab;
+ while (tabBar && !tabBar.classList.contains('subpages-nav-tabs')) {
+ tabBar = tabBar.parentNode;
}
-
- cr.dispatchPropertyChange(this, 'visible', visible, !visible);
- },
-
- /**
- * Shows or hides an overlay (including any visible dialog).
- * @param {boolean} visible Whether the overlay should be visible or not.
- * @private
- */
- setOverlayVisible_: function(visible) {
- assert(this.isOverlay);
- var pageDiv = this.pageDiv;
- var container = this.container;
-
- if (visible)
- uber.invokeMethodOnParent('beginInterceptingEvents');
-
- if (container.hidden != visible) {
- if (visible) {
- // If the container is set hidden and then immediately set visible
- // again, the fadeCompleted_ callback would cause it to be erroneously
- // hidden again. Removing the transparent tag avoids that.
- container.classList.remove('transparent');
-
- // Hide all dialogs in this container since a different one may have
- // been previously visible before fading out.
- var pages = container.querySelectorAll('.page');
- for (var i = 0; i < pages.length; i++)
- pages[i].hidden = true;
- // Show the new dialog.
- pageDiv.hidden = false;
- pageDiv.page = this;
- }
+ if (!tabBar)
return;
- }
-
- var self = this;
- var loading = OptionsPage.isLoading();
- if (!loading) {
- // TODO(flackr): Use an event delegate to avoid having to subscribe and
- // unsubscribe for webkitTransitionEnd events.
- container.addEventListener('webkitTransitionEnd', function f(e) {
- var propName = e.propertyName;
- if (e.target != e.currentTarget ||
- (propName && propName != 'opacity')) {
- return;
- }
- container.removeEventListener('webkitTransitionEnd', f);
- self.fadeCompleted_();
- });
- // -webkit-transition is 200ms. Let's wait for 400ms.
- ensureTransitionEndEvent(container, 400);
- }
-
- if (visible) {
- container.hidden = false;
- pageDiv.hidden = false;
- pageDiv.page = this;
- // NOTE: This is a hacky way to force the container to layout which
- // will allow us to trigger the webkit transition.
- container.scrollTop;
- this.pageDiv.removeAttribute('aria-hidden');
- if (this.parentPage) {
- this.parentPage.pageDiv.parentElement.setAttribute('aria-hidden',
- true);
- }
- container.classList.remove('transparent');
- this.onVisibilityChanged_();
- } else {
- // Kick change events for text fields.
- if (pageDiv.contains(document.activeElement))
- document.activeElement.blur();
- container.classList.add('transparent');
+ if (tabBar.activeNavTab != null) {
+ tabBar.activeNavTab.classList.remove('active-tab');
+ $(tabBar.activeNavTab.getAttribute('tab-contents')).classList.
+ remove('active-tab-contents');
}
- if (loading)
- this.fadeCompleted_();
+ tab.classList.add('active-tab');
+ $(tab.getAttribute('tab-contents')).classList.add('active-tab-contents');
+ tabBar.activeNavTab = tab;
},
/**
- * Called when a container opacity transition finishes.
- * @private
+ * Shows or hides options for clearing Flash LSOs.
+ * @param {boolean} enabled Whether plugin data can be cleared.
*/
- fadeCompleted_: function() {
- if (this.container.classList.contains('transparent')) {
- this.pageDiv.hidden = true;
- this.container.hidden = true;
-
- if (this.parentPage)
- this.parentPage.pageDiv.parentElement.removeAttribute('aria-hidden');
-
- if (this.nestingLevel == 1)
- uber.invokeMethodOnParent('stopInterceptingEvents');
-
- this.onVisibilityChanged_();
+ setClearPluginLSODataEnabled: function(enabled) {
+ if (enabled) {
+ document.documentElement.setAttribute(
+ 'flashPluginSupportsClearSiteData', '');
+ } else {
+ document.documentElement.removeAttribute(
+ 'flashPluginSupportsClearSiteData');
}
+ if (navigator.plugins['Shockwave Flash'])
+ document.documentElement.setAttribute('hasFlashPlugin', '');
},
/**
- * Called when a page is shown or hidden to update the root options page
- * based on this page's visibility.
- * @private
- */
- onVisibilityChanged_: function() {
- OptionsPage.updateRootPageFreezeState();
-
- if (this.isOverlay && !this.visible)
- OptionsPage.updateScrollPosition_();
- },
-
- /**
- * The nesting level of this page.
- * @type {number} The nesting level of this page (0 for top-level page)
+ * Shows or hides Pepper Flash settings.
+ * @param {boolean} enabled Whether Pepper Flash settings should be enabled.
*/
- get nestingLevel() {
- var level = 0;
- var parent = this.parentPage;
- while (parent) {
- level++;
- parent = parent.parentPage;
+ setPepperFlashSettingsEnabled: function(enabled) {
+ if (enabled) {
+ document.documentElement.setAttribute(
+ 'enablePepperFlashSettings', '');
+ } else {
+ document.documentElement.removeAttribute(
+ 'enablePepperFlashSettings');
}
- return level;
- },
-
- /**
- * Whether the page is considered 'sticky', such that it will
- * remain a top-level page even if sub-pages change.
- * @type {boolean} True if this page is sticky.
- */
- get sticky() {
- return false;
},
/**
- * Checks whether this page is an ancestor of the given page in terms of
- * subpage nesting.
- * @param {OptionsPage} page The potential descendent of this page.
- * @return {boolean} True if |page| is nested under this page.
+ * Sets whether Settings is shown as a standalone page in a window for the
+ * app launcher settings "app".
+ * @param {boolean} isSettingsApp Whether this page is shown standalone.
*/
- isAncestorOfPage: function(page) {
- var parent = page.parentPage;
- while (parent) {
- if (parent == this)
- return true;
- parent = parent.parentPage;
- }
- return false;
+ setIsSettingsApp: function(isSettingsApp) {
+ document.documentElement.classList.toggle('settings-app', isSettingsApp);
},
/**
- * Whether it should be possible to show the page.
- * @return {boolean} True if the page should be shown.
+ * Returns true if Settings is shown as an "app" (in a window by itself)
+ * for the app launcher settings "app".
+ * @return {boolean} Whether this page is shown standalone.
*/
- canShowPage: function() {
- return true;
+ isSettingsApp: function() {
+ return document.documentElement.classList.contains('settings-app');
},
};