home *** CD-ROM | disk | FTP | other *** search
Text File | 2013-04-03 | 78.8 KB | 2,475 lines |
- // Copyright (c) 2012 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.
-
- // TODO(sail): Refactor options_page and remove this include.
- // Copyright (c) 2012 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.
-
- cr.define('options', function() {
- /////////////////////////////////////////////////////////////////////////////
- // 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);
- 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 = {};
-
- /**
- * 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);
- 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 tab title.
- this.setTitle_(targetPage.title);
-
- // Update focus if any other control was focused before.
- if (document.activeElement != document.body)
- 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();
- }
- }
- };
-
- /**
- * Sets the title of the page. This is accomplished by calling into the
- * parent page API.
- * @param {String} title The title string.
- * @private
- */
- OptionsPage.setTitle_ = function(title) {
- uber.invokeMethodOnParent('setTitle', {title: title});
- };
-
- /**
- * 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(document.body.scrollLeft, scrollTop);
- };
-
- /**
- * Pushes the current page onto the history stack, overriding the last page
- * if it is the generic chrome://settings/.
- * @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) {
- var page = this.getTopmostVisiblePage();
- var path = window.location.pathname + window.location.hash;
- if (path)
- path = path.slice(1).replace(/\/(?:#|$)/, ''); // Remove trailing slash.
-
- // Update tab title.
- this.setTitle_(page.title);
-
- // The page is already in history (the user may have clicked the same link
- // twice). Do nothing.
- if (path == page.name &&
- !document.documentElement.classList.contains('loading')) {
- return;
- }
-
- var hash = opt_params && opt_params.ignoreHash ? '' : window.location.hash;
-
- // If settings are embedded, tell the outer page to set its "path" to the
- // inner frame's path.
- var outerPath = (page == this.getDefaultPage() ? '' : page.name) + hash;
- uber.invokeMethodOnParent('setPath', {path: outerPath});
-
- // If there is no path, the current location is chrome://settings/.
- // Override this with the new page.
- var historyFunction = path && !replace ? window.history.pushState :
- window.history.replaceState;
- historyFunction.call(window.history,
- {pageName: page.name},
- page.title,
- '/' + page.name + hash);
- };
-
- /**
- * 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)
- this.showPageByName(overlay.parentPage.name, false);
-
- if (!overlay.visible) {
- overlay.visible = true;
- if (overlay.didShowPage) overlay.didShowPage();
- }
-
- // Update tab title.
- this.setTitle_(overlay.title);
-
- // Change focus to the overlay if any other control was focused before.
- if (document.activeElement != document.body)
- overlay.focus();
-
- $('searchBox').setAttribute('aria-hidden', true);
-
- if ($('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.restoreLastFocusedElement_();
- if (!this.isOverlayVisible_())
- $('searchBox').removeAttribute('aria-hidden');
- };
-
- /**
- * 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 {OptionsBubble} 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 options.OptionsBubble;
- 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 views. See the documentation of
- // reverseButtonStrip_() for an explanation of why this is necessary.
- if (cr.isViews)
- this.reverseButtonStrip_(overlay);
-
- overlay.tab = undefined;
- overlay.isOverlay = true;
- overlay.initializePage();
- };
-
- /**
- * Reverses the child elements of a button strip. 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 https://bugs.webkit.org/show_bug.cgi?id=62664 for more
- * information.
- * @param {Object} overlay The overlay containing the button strip to reverse.
- * @private
- */
- OptionsPage.reverseButtonStrip_ = function(overlay) {
- var buttonStrips = overlay.pageDiv.querySelectorAll('.button-strip');
-
- // 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]);
- }
- };
-
- /**
- * Callback for window.onpopstate.
- * @param {Object} data State data pushed into history.
- */
- OptionsPage.setState = function(data) {
- if (data && data.pageName) {
- this.willClose();
- this.showPageByName(data.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 = document.body.scrollTop;
- 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();
-
- 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));
- }
- };
-
- /**
- * 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 {
- e.style.left = OptionsPage.horizontalOffset -
- document.body.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');
- }
- };
-
- OptionsPage.setPepperFlashSettingsEnabled = function(enabled) {
- if (enabled) {
- document.documentElement.setAttribute(
- 'enablePepperFlashSettings', '');
- } else {
- document.documentElement.removeAttribute(
- 'enablePepperFlashSettings');
- }
- };
-
- 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() {
- 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;
- }
- },
-
- /**
- * Gets the container div for this page if it is an overlay.
- * @type {HTMLElement}
- */
- get container() {
- assert(this.isOverlay);
- return this.pageDiv.parentNode;
- },
-
- /**
- * Gets page visibility state.
- * @type {boolean}
- */
- 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;
- }
- return !this.pageDiv.hidden;
- },
-
- /**
- * Sets page visibility.
- * @type {boolean}
- */
- set visible(visible) {
- if ((this.visible && visible) || (!this.visible && !visible))
- 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.hidden = !visible;
- this.onVisibilityChanged_();
- }
-
- 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');
- this.pageDiv.removeAttribute('aria-hidden');
- if (this.parentPage)
- this.parentPage.pageDiv.setAttribute('aria-hidden', true);
- } else {
- if (this.parentPage)
- this.parentPage.pageDiv.removeAttribute('aria-hidden');
- }
-
- 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;
- }
- return;
- }
-
- if (visible) {
- container.hidden = false;
- pageDiv.hidden = false;
- // NOTE: This is a hacky way to force the container to layout which
- // will allow us to trigger the webkit transition.
- container.scrollTop;
- container.classList.remove('transparent');
- this.onVisibilityChanged_();
- } else {
- var self = this;
- // TODO: Use an event delegate to avoid having to subscribe and
- // unsubscribe for webkitTransitionEnd events.
- container.addEventListener('webkitTransitionEnd', function f(e) {
- if (e.target != e.currentTarget || e.propertyName != 'opacity')
- return;
- container.removeEventListener('webkitTransitionEnd', f);
- self.fadeCompleted_();
- });
- container.classList.add('transparent');
- }
- },
-
- /**
- * Called when a container opacity transition finishes.
- * @private
- */
- fadeCompleted_: function() {
- if (this.container.classList.contains('transparent')) {
- this.pageDiv.hidden = true;
- this.container.hidden = true;
- this.onVisibilityChanged_();
- if (this.nestingLevel == 1)
- uber.invokeMethodOnParent('stopInterceptingEvents');
- }
- },
-
- /**
- * 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)
- */
- get nestingLevel() {
- var level = 0;
- var parent = this.parentPage;
- while (parent) {
- level++;
- parent = parent.parentPage;
- }
- 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.
- */
- isAncestorOfPage: function(page) {
- var parent = page.parentPage;
- while (parent) {
- if (parent == this)
- return true;
- parent = parent.parentPage;
- }
- return false;
- },
-
- /**
- * Whether it should be possible to show the page.
- * @return {boolean} True if the page should be shown.
- */
- canShowPage: function() {
- return true;
- },
- };
-
- // Export
- return {
- OptionsPage: OptionsPage
- };
- });
-
- // Copyright (c) 2012 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.
-
- /**
- * The global object.
- * @type {!Object}
- * @const
- */
- var global = this;
-
- /**
- * Alias for document.getElementById.
- * @param {string} id The ID of the element to find.
- * @return {HTMLElement} The found element or null if not found.
- */
- function $(id) {
- return document.getElementById(id);
- }
-
- /**
- * Calls chrome.send with a callback and restores the original afterwards.
- * @param {string} name The name of the message to send.
- * @param {!Array} params The parameters to send.
- * @param {string} callbackName The name of the function that the backend calls.
- * @param {!Function} callback The function to call.
- */
- function chromeSend(name, params, callbackName, callback) {
- var old = global[callbackName];
- global[callbackName] = function() {
- // restore
- global[callbackName] = old;
-
- var args = Array.prototype.slice.call(arguments);
- return callback.apply(global, args);
- };
- chrome.send(name, params);
- }
-
- /**
- * Generates a CSS url string.
- * @param {string} s The URL to generate the CSS url for.
- * @return {string} The CSS url string.
- */
- function url(s) {
- // http://www.w3.org/TR/css3-values/#uris
- // Parentheses, commas, whitespace characters, single quotes (') and double
- // quotes (") appearing in a URI must be escaped with a backslash
- var s2 = s.replace(/(\(|\)|\,|\s|\'|\"|\\)/g, '\\$1');
- // WebKit has a bug when it comes to URLs that end with \
- // https://bugs.webkit.org/show_bug.cgi?id=28885
- if (/\\\\$/.test(s2)) {
- // Add a space to work around the WebKit bug.
- s2 += ' ';
- }
- return 'url("' + s2 + '")';
- }
-
- /**
- * Parses query parameters from Location.
- * @param {string} location The URL to generate the CSS url for.
- * @return {object} Dictionary containing name value pairs for URL
- */
- function parseQueryParams(location) {
- var params = {};
- var query = unescape(location.search.substring(1));
- var vars = query.split('&');
- for (var i = 0; i < vars.length; i++) {
- var pair = vars[i].split('=');
- params[pair[0]] = pair[1];
- }
- return params;
- }
-
- function findAncestorByClass(el, className) {
- return findAncestor(el, function(el) {
- if (el.classList)
- return el.classList.contains(className);
- return null;
- });
- }
-
- /**
- * Return the first ancestor for which the {@code predicate} returns true.
- * @param {Node} node The node to check.
- * @param {function(Node) : boolean} predicate The function that tests the
- * nodes.
- * @return {Node} The found ancestor or null if not found.
- */
- function findAncestor(node, predicate) {
- var last = false;
- while (node != null && !(last = predicate(node))) {
- node = node.parentNode;
- }
- return last ? node : null;
- }
-
- function swapDomNodes(a, b) {
- var afterA = a.nextSibling;
- if (afterA == b) {
- swapDomNodes(b, a);
- return;
- }
- var aParent = a.parentNode;
- b.parentNode.replaceChild(a, b);
- aParent.insertBefore(b, afterA);
- }
-
- /**
- * Disables text selection and dragging, with optional whitelist callbacks.
- * @param {function(Event):boolean=} opt_allowSelectStart Unless this function
- * is defined and returns true, the onselectionstart event will be
- * surpressed.
- * @param {function(Event):boolean=} opt_allowDragStart Unless this function
- * is defined and returns true, the ondragstart event will be surpressed.
- */
- function disableTextSelectAndDrag(opt_allowSelectStart, opt_allowDragStart) {
- // Disable text selection.
- document.onselectstart = function(e) {
- if (!(opt_allowSelectStart && opt_allowSelectStart.call(this, e)))
- e.preventDefault();
- };
-
- // Disable dragging.
- document.ondragstart = function(e) {
- if (!(opt_allowDragStart && opt_allowDragStart.call(this, e)))
- e.preventDefault();
- };
- }
-
- /**
- * Call this to stop clicks on <a href="#"> links from scrolling to the top of
- * the page (and possibly showing a # in the link).
- */
- function preventDefaultOnPoundLinkClicks() {
- document.addEventListener('click', function(e) {
- var anchor = findAncestor(e.target, function(el) {
- return el.tagName == 'A';
- });
- // Use getAttribute() to prevent URL normalization.
- if (anchor && anchor.getAttribute('href') == '#')
- e.preventDefault();
- });
- }
-
- /**
- * Check the directionality of the page.
- * @return {boolean} True if Chrome is running an RTL UI.
- */
- function isRTL() {
- return document.documentElement.dir == 'rtl';
- }
-
- /**
- * Simple common assertion API
- * @param {*} condition The condition to test. Note that this may be used to
- * test whether a value is defined or not, and we don't want to force a
- * cast to Boolean.
- * @param {string=} opt_message A message to use in any error.
- */
- function assert(condition, opt_message) {
- 'use strict';
- if (!condition) {
- var msg = 'Assertion failed';
- if (opt_message)
- msg = msg + ': ' + opt_message;
- throw new Error(msg);
- }
- }
-
- /**
- * Get an element that's known to exist by its ID. We use this instead of just
- * calling getElementById and not checking the result because this lets us
- * satisfy the JSCompiler type system.
- * @param {string} id The identifier name.
- * @return {!Element} the Element.
- */
- function getRequiredElement(id) {
- var element = $(id);
- assert(element, 'Missing required element: ' + id);
- return element;
- }
-
- // Handle click on a link. If the link points to a chrome: or file: url, then
- // call into the browser to do the navigation.
- document.addEventListener('click', function(e) {
- // Allow preventDefault to work.
- if (!e.returnValue)
- return;
-
- var el = e.target;
- if (el.nodeType == Node.ELEMENT_NODE &&
- el.webkitMatchesSelector('A, A *')) {
- while (el.tagName != 'A') {
- el = el.parentElement;
- }
-
- if ((el.protocol == 'file:' || el.protocol == 'about:') &&
- (e.button == 0 || e.button == 1)) {
- chrome.send('navigateToUrl', [
- el.href,
- el.target,
- e.button,
- e.altKey,
- e.ctrlKey,
- e.metaKey,
- e.shiftKey
- ]);
- e.preventDefault();
- }
- }
- });
-
- /**
- * Creates a new URL which is the old URL with a GET param of key=value.
- * @param {string} url The base URL. There is not sanity checking on the URL so
- * it must be passed in a proper format.
- * @param {string} key The key of the param.
- * @param {string} value The value of the param.
- * @return {string} The new URL.
- */
- function appendParam(url, key, value) {
- var param = encodeURIComponent(key) + '=' + encodeURIComponent(value);
-
- if (url.indexOf('?') == -1)
- return url + '?' + param;
- return url + '&' + param;
- }
-
- /**
- * Creates a new URL for a favicon request.
- * @param {string} url The url for the favicon.
- * @param {number=} opt_size Optional preferred size of the favicon.
- * @return {string} Updated URL for the favicon.
- */
- function getFaviconURL(url, opt_size) {
- var size = opt_size || 16;
- return 'chrome://favicon/size/' + size + '@' +
- window.devicePixelRatio + 'x/' + url;
- }
-
- // Copyright (c) 2012 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.
-
- cr.define('options', function() {
- /** @const */ var OptionsPage = options.OptionsPage;
-
- // Variable to track if a captcha challenge was issued. If this gets set to
- // true, it stays that way until we are told about successful login from
- // the browser. This means subsequent errors (like invalid password) are
- // rendered in the captcha state, which is basically identical except we
- // don't show the top error blurb 'Error Signing in' or the 'Create
- // account' link.
- var captchaChallengeActive_ = false;
-
- // When true, the password value may be empty when submitting auth info.
- // This is true when requesting an access code or when requesting an OTP or
- // captcha with the oauth sign in flow.
- var allowEmptyPassword_ = false;
-
- // True if the synced account uses a custom passphrase.
- var usePassphrase_ = false;
-
- // True if the synced account uses 'encrypt everything'.
- var useEncryptEverything_ = false;
-
- // True if the support for keystore encryption is enabled. Controls whether
- // the new unified encryption UI is displayed instead of the old encryption
- // ui (where passphrase and encrypted types could be set independently of
- // each other).
- var keystoreEncryptionEnabled_ = false;
-
- // The last email address that this profile was connected to. If the profile
- // was never connected this is an empty string. Otherwise it is a normalized
- // email address.
- var lastEmailAddress_ = '';
-
- /**
- * SyncSetupOverlay class
- * Encapsulated handling of the 'Sync Setup' overlay page.
- * @class
- */
- function SyncSetupOverlay() {
- OptionsPage.call(this, 'syncSetup',
- loadTimeData.getString('syncSetupOverlayTabTitle'),
- 'sync-setup-overlay');
- }
-
- cr.addSingletonGetter(SyncSetupOverlay);
-
- SyncSetupOverlay.prototype = {
- __proto__: OptionsPage.prototype,
-
- /**
- * Initializes the page.
- */
- initializePage: function() {
- OptionsPage.prototype.initializePage.call(this);
-
- var self = this;
- $('gaia-login-form').onsubmit = function() {
- self.sendCredentialsAndClose_();
- return false;
- };
- $('google-option').onchange = $('explicit-option').onchange = function() {
- self.onPassphraseRadioChanged_();
- };
- $('basic-encryption-option').onchange =
- $('full-encryption-option').onchange = function() {
- self.onEncryptionRadioChanged_();
- }
- $('choose-datatypes-cancel').onclick =
- $('sync-setup-cancel').onclick =
- $('confirm-everything-cancel').onclick =
- $('stop-syncing-cancel').onclick =
- $('sync-spinner-cancel').onclick = function() {
- self.closeOverlay_();
- };
- $('confirm-everything-ok').onclick = function() {
- self.sendConfiguration_();
- };
- $('timeout-ok').onclick = function() {
- chrome.send('CloseTimeout');
- self.closeOverlay_();
- };
- $('stop-syncing-ok').onclick = function() {
- chrome.send('SyncSetupStopSyncing');
- self.closeOverlay_();
- };
- $('different-email').innerHTML = loadTimeData.getString('differentEmail');
- },
-
- showOverlay_: function() {
- OptionsPage.navigateToPage('syncSetup');
- },
-
- closeOverlay_: function() {
- OptionsPage.closeOverlay();
- },
-
- /** @override */
- didShowPage: function() {
- var forceLogin = document.location.hash == '#forceLogin';
- var result = JSON.stringify({'forceLogin': forceLogin});
- chrome.send('SyncSetupAttachHandler', [result]);
- },
-
- /** @override */
- didClosePage: function() {
- chrome.send('SyncSetupDidClosePage');
- },
-
- getEncryptionRadioCheckedValue_: function() {
- var f = $('choose-data-types-form');
- for (var i = 0; i < f.encrypt.length; ++i) {
- if (f.encrypt[i].checked)
- return f.encrypt[i].value;
- }
-
- return undefined;
- },
-
- getPassphraseRadioCheckedValue_: function() {
- var f = $('choose-data-types-form');
- for (var i = 0; i < f.option.length; ++i) {
- if (f.option[i].checked) {
- return f.option[i].value;
- }
- }
-
- return undefined;
- },
-
- disableEncryptionRadioGroup_: function() {
- var f = $('choose-data-types-form');
- for (var i = 0; i < f.encrypt.length; ++i)
- f.encrypt[i].disabled = true;
- },
-
- onPassphraseRadioChanged_: function() {
- var visible = this.getPassphraseRadioCheckedValue_() == 'explicit';
- $('sync-custom-passphrase').hidden = !visible;
- },
-
- onEncryptionRadioChanged_: function() {
- var visible = $('full-encryption-option').checked;
- $('sync-custom-passphrase').hidden = !visible;
- },
-
- checkAllDataTypeCheckboxes_: function() {
- // Only check the visible ones (since there's no way to uncheck
- // the invisible ones).
- var checkboxes = $('choose-data-types-body').querySelectorAll(
- '.sync-type-checkbox:not([hidden]) input');
- for (var i = 0; i < checkboxes.length; i++) {
- checkboxes[i].checked = true;
- }
- },
-
- setDataTypeCheckboxesEnabled_: function(enabled) {
- var checkboxes = $('choose-data-types-body').querySelectorAll('input');
- for (var i = 0; i < checkboxes.length; i++) {
- checkboxes[i].disabled = !enabled;
- }
- },
-
- setCheckboxesToKeepEverythingSynced_: function(value) {
- this.setDataTypeCheckboxesEnabled_(!value);
- if (value)
- this.checkAllDataTypeCheckboxes_();
- },
-
- // Returns true if none of the visible checkboxes are checked.
- noDataTypesChecked_: function() {
- var query = '.sync-type-checkbox:not([hidden]) input:checked';
- var checkboxes = $('choose-data-types-body').querySelectorAll(query);
- return checkboxes.length == 0;
- },
-
- checkPassphraseMatch_: function() {
- var emptyError = $('empty-error');
- var mismatchError = $('mismatch-error');
- emptyError.hidden = true;
- mismatchError.hidden = true;
-
- var f = $('choose-data-types-form');
- if ((this.getPassphraseRadioCheckedValue_() != 'explicit' ||
- $('google-option').disabled) &&
- (!$('full-encryption-option').checked ||
- $('basic-encryption-option').disabled)) {
- return true;
- }
-
- var customPassphrase = $('custom-passphrase');
- if (customPassphrase.value.length == 0) {
- emptyError.hidden = false;
- return false;
- }
-
- var confirmPassphrase = $('confirm-passphrase');
- if (confirmPassphrase.value != customPassphrase.value) {
- mismatchError.hidden = false;
- return false;
- }
-
- return true;
- },
-
- sendConfiguration_: function() {
- // Trying to submit, so hide previous errors.
- $('error-text').hidden = true;
-
- var syncAll = $('sync-select-datatypes').selectedIndex == 0;
- if (!syncAll && this.noDataTypesChecked_()) {
- $('error-text').hidden = false;
- return;
- }
-
- var encryptAllData = this.getEncryptionRadioCheckedValue_() == 'all';
- if (!encryptAllData &&
- $('full-encryption-option').checked &&
- this.keystoreEncryptionEnabled_) {
- encryptAllData = true;
- }
-
- var usePassphrase;
- var customPassphrase;
- var googlePassphrase = false;
- if (!$('sync-existing-passphrase-container').hidden) {
- // If we were prompted for an existing passphrase, use it.
- customPassphrase = $('choose-data-types-form').passphrase.value;
- usePassphrase = true;
- // If we were displaying the 'enter your old google password' prompt,
- // then that means this is the user's google password.
- googlePassphrase = !$('google-passphrase-needed-body').hidden;
- // We allow an empty passphrase, in case the user has disabled
- // all their encrypted datatypes. In that case, the PSS will accept
- // the passphrase and finish configuration. If the user has enabled
- // encrypted datatypes, the PSS will prompt again specifying that the
- // passphrase failed.
- } else if ((!$('google-option').disabled &&
- this.getPassphraseRadioCheckedValue_() == 'explicit') ||
- (!$('basic-encryption-option').disabled &&
- $('full-encryption-option').checked)) {
- // The user is setting a custom passphrase for the first time.
- if (!this.checkPassphraseMatch_())
- return;
- customPassphrase = $('custom-passphrase').value;
- usePassphrase = true;
- } else {
- // The user is not setting a custom passphrase.
- usePassphrase = false;
- }
-
- // Don't allow the user to tweak the settings once we send the
- // configuration to the backend.
- this.setInputElementsDisabledState_(true);
- this.animateDisableLink_($('use-default-link'), true, null);
-
- // These values need to be kept in sync with where they are read in
- // SyncSetupFlow::GetDataTypeChoiceData().
- var result = JSON.stringify({
- 'syncAllDataTypes': syncAll,
- 'bookmarksSynced': syncAll || $('bookmarks-checkbox').checked,
- 'preferencesSynced': syncAll || $('preferences-checkbox').checked,
- 'themesSynced': syncAll || $('themes-checkbox').checked,
- 'passwordsSynced': syncAll || $('passwords-checkbox').checked,
- 'autofillSynced': syncAll || $('autofill-checkbox').checked,
- 'extensionsSynced': syncAll || $('extensions-checkbox').checked,
- 'typedUrlsSynced': syncAll || $('typed-urls-checkbox').checked,
- 'appsSynced': syncAll || $('apps-checkbox').checked,
- 'sessionsSynced': syncAll || $('sessions-checkbox').checked,
- 'encryptAllData': encryptAllData,
- 'usePassphrase': usePassphrase,
- 'isGooglePassphrase': googlePassphrase,
- 'passphrase': customPassphrase
- });
- chrome.send('SyncSetupConfigure', [result]);
- },
-
- /**
- * Sets the disabled property of all input elements within the 'Customize
- * Sync Preferences' screen. This is used to prohibit the user from changing
- * the inputs after confirming the customized sync preferences, or resetting
- * the state when re-showing the dialog.
- * @param {boolean} disabled True if controls should be set to disabled.
- * @private
- */
- setInputElementsDisabledState_: function(disabled) {
- var configureElements =
- $('customize-sync-preferences').querySelectorAll('input');
- for (var i = 0; i < configureElements.length; i++)
- configureElements[i].disabled = disabled;
- $('sync-select-datatypes').disabled = disabled;
-
- var self = this;
- this.animateDisableLink_($('customize-link'), disabled, function() {
- self.showCustomizePage_(null, true);
- });
- },
-
- /**
- * Animate a link being enabled/disabled. The link is hidden by animating
- * its opacity, but to ensure the user doesn't click it during that time,
- * its onclick handler is changed to null as well.
- * @param {HTMLElement} elt The anchor element to enable/disable.
- * @param {boolean} disabled True if the link should be disabled.
- * @param {function} enabledFunction The onclick handler when the link is
- * enabled.
- * @private
- */
- animateDisableLink_: function(elt, disabled, enabledFunction) {
- if (disabled) {
- elt.classList.add('transparent');
- elt.onclick = null;
- elt.addEventListener('webkitTransitionEnd', function f(e) {
- if (e.propertyName != 'opacity')
- return;
- elt.removeEventListener('webkitTransitionEnd', f);
- elt.classList.remove('transparent');
- elt.hidden = true;
- });
- } else {
- elt.hidden = false;
- elt.onclick = enabledFunction;
- }
- },
-
- /**
- * Shows or hides the Sync data type checkboxes in the advanced
- * configuration screen.
- * @param {Object} args The configuration data used to show/hide UI.
- * @private
- */
- setChooseDataTypesCheckboxes_: function(args) {
- var datatypeSelect = $('sync-select-datatypes');
- datatypeSelect.selectedIndex = args.syncAllDataTypes ? 0 : 1;
-
- $('bookmarks-checkbox').checked = args.bookmarksSynced;
- $('preferences-checkbox').checked = args.preferencesSynced;
- $('themes-checkbox').checked = args.themesSynced;
-
- if (args.passwordsRegistered) {
- $('passwords-checkbox').checked = args.passwordsSynced;
- $('passwords-item').hidden = false;
- } else {
- $('passwords-item').hidden = true;
- }
- if (args.autofillRegistered) {
- $('autofill-checkbox').checked = args.autofillSynced;
- $('autofill-item').hidden = false;
- } else {
- $('autofill-item').hidden = true;
- }
- if (args.extensionsRegistered) {
- $('extensions-checkbox').checked = args.extensionsSynced;
- $('extensions-item').hidden = false;
- } else {
- $('extensions-item').hidden = true;
- }
- if (args.typedUrlsRegistered) {
- $('typed-urls-checkbox').checked = args.typedUrlsSynced;
- $('omnibox-item').hidden = false;
- } else {
- $('omnibox-item').hidden = true;
- }
- if (args.appsRegistered) {
- $('apps-checkbox').checked = args.appsSynced;
- $('apps-item').hidden = false;
- } else {
- $('apps-item').hidden = true;
- }
- if (args.sessionsRegistered) {
- $('sessions-checkbox').checked = args.sessionsSynced;
- $('sessions-item').hidden = false;
- } else {
- $('sessions-item').hidden = true;
- }
-
- this.setCheckboxesToKeepEverythingSynced_(args.syncAllDataTypes);
- },
-
- setEncryptionRadios_: function(args) {
- if (args.encryptAllData) {
- $('encrypt-all-option').checked = true;
- this.disableEncryptionRadioGroup_();
- } else {
- $('encrypt-sensitive-option').checked = true;
- }
-
- if (!args.encryptAllData && !args.usePassphrase) {
- $('basic-encryption-option').checked = true;
- } else {
- $('full-encryption-option').checked = true;
- $('full-encryption-option').disabled = true;
- $('basic-encryption-option').disabled = true;
- }
- },
-
- setPassphraseRadios_: function(args) {
- if (args.usePassphrase) {
- $('explicit-option').checked = true;
-
- // The passphrase, once set, cannot be unset, but we show a reset link.
- $('explicit-option').disabled = true;
- $('google-option').disabled = true;
- $('sync-custom-passphrase').hidden = true;
- } else {
- $('google-option').checked = true;
- }
- },
-
- setCheckboxesAndErrors_: function(args) {
- this.setChooseDataTypesCheckboxes_(args);
- this.setEncryptionRadios_(args);
- this.setPassphraseRadios_(args);
- },
-
- showConfigure_: function(args) {
- var datatypeSelect = $('sync-select-datatypes');
- var self = this;
- datatypeSelect.onchange = function() {
- var syncAll = this.selectedIndex == 0;
- self.setCheckboxesToKeepEverythingSynced_(syncAll);
- };
-
- this.resetPage_('sync-setup-configure');
- $('sync-setup-configure').hidden = false;
-
- // onsubmit is changed when submitting a passphrase. Reset it to its
- // default.
- $('choose-data-types-form').onsubmit = function() {
- self.sendConfiguration_();
- return false;
- };
-
- if (args) {
- this.setCheckboxesAndErrors_(args);
-
- this.useEncryptEverything_ = args.encryptAllData;
-
- // Whether to display the 'Sync everything' confirmation page or the
- // customize data types page.
- var syncAllDataTypes = args.syncAllDataTypes;
- this.usePassphrase_ = args.usePassphrase;
- this.keystoreEncryptionEnabled_ = args.keystoreEncryptionEnabled;
- if (args.showSyncEverythingPage == false || this.usePassphrase_ ||
- syncAllDataTypes == false || args.showPassphrase) {
- this.showCustomizePage_(args, syncAllDataTypes);
- } else {
- this.showSyncEverythingPage_();
- }
- }
- },
-
- showSpinner_: function() {
- this.resetPage_('sync-setup-spinner');
- $('sync-setup-spinner').hidden = false;
- },
-
- showTimeoutPage_: function() {
- this.resetPage_('sync-setup-timeout');
- $('sync-setup-timeout').hidden = false;
- },
-
- showSyncEverythingPage_: function() {
- $('confirm-sync-preferences').hidden = false;
- $('customize-sync-preferences').hidden = true;
-
- // Reset the selection to 'Sync everything'.
- $('sync-select-datatypes').selectedIndex = 0;
-
- // The default state is to sync everything.
- this.setCheckboxesToKeepEverythingSynced_(true);
-
- // Encrypt passwords is the default, but don't set it if the previously
- // synced account is already set to encrypt everything.
- if (!this.useEncryptEverything_)
- $('encrypt-sensitive-option').checked = true;
-
- // If the account is not synced with a custom passphrase, reset the
- // passphrase radio when switching to the 'Sync everything' page.
- if (!this.usePassphrase_) {
- $('google-option').checked = true;
- $('sync-custom-passphrase').hidden = true;
- }
-
- if (!this.useEncryptEverything_ && !this.usePassphrase_)
- $('basic-encryption-option').checked = true;
-
- $('confirm-everything-ok').focus();
- },
-
- /**
- * Reveals the UI for entering a custom passphrase during initial setup.
- * This happens if the user has previously enabled a custom passphrase on a
- * different machine.
- * @param {Array} args The args that contain the passphrase UI
- * configuration.
- * @private
- */
- showPassphraseContainer_: function(args) {
- // Once we require a passphrase, we prevent the user from returning to
- // the Sync Everything pane.
- $('use-default-link').hidden = true;
- $('sync-custom-passphrase-container').hidden = true;
- $('sync-existing-passphrase-container').hidden = false;
-
- // Hide the selection options within the new encryption section when
- // prompting for a passphrase.
- $('sync-new-encryption-section-container').hidden = true;
-
- $('normal-body').hidden = true;
- $('google-passphrase-needed-body').hidden = true;
- // Display the correct prompt to the user depending on what type of
- // passphrase is needed.
- if (args.usePassphrase)
- $('normal-body').hidden = false;
- else
- $('google-passphrase-needed-body').hidden = false;
-
- $('passphrase-learn-more').hidden = false;
- // Warn the user about their incorrect passphrase if we need a passphrase
- // and the passphrase field is non-empty (meaning they tried to set it
- // previously but failed).
- $('incorrect-passphrase').hidden =
- !(args.usePassphrase && args.passphraseFailed);
-
- $('sync-passphrase-warning').hidden = false;
- $('passphrase').focus();
- },
-
- /** @private */
- showCustomizePage_: function(args, syncEverything) {
- $('confirm-sync-preferences').hidden = true;
- $('customize-sync-preferences').hidden = false;
-
- $('sync-custom-passphrase-container').hidden = false;
-
- if (this.keystoreEncryptionEnabled_) {
- $('customize-sync-encryption').hidden = true;
- $('sync-custom-passphrase-options').hidden = true;
- $('sync-new-encryption-section-container').hidden = false;
- $('customize-sync-encryption-new').hidden = false;
- } else {
- $('customize-sync-encryption').hidden = false;
- $('sync-custom-passphrase-options').hidden = false;
- $('customize-sync-encryption-new').hidden = true;
- }
-
- $('sync-existing-passphrase-container').hidden = true;
-
- // If the user has selected the 'Customize' page on initial set up, it's
- // likely he intends to change the data types. Select the
- // 'Choose data types' option in this case.
- var index = syncEverything ? 0 : 1;
- $('sync-select-datatypes').selectedIndex = index;
- this.setDataTypeCheckboxesEnabled_(!syncEverything);
-
- // The passphrase input may need to take over focus from the OK button, so
- // set focus before that logic.
- $('choose-datatypes-ok').focus();
-
- if (args && args.showPassphrase) {
- this.showPassphraseContainer_(args);
- } else {
- // We only show the 'Use Default' link if we're not prompting for an
- // existing passphrase.
- var self = this;
- this.animateDisableLink_($('use-default-link'), false, function() {
- self.showSyncEverythingPage_();
- });
- }
- },
-
- /**
- * Shows the appropriate sync setup page.
- * @param {string} page A page of the sync setup to show.
- * @param {object} args Data from the C++ to forward on to the right
- * section.
- */
- showSyncSetupPage_: function(page, args) {
- this.setThrobbersVisible_(false);
-
- // Hide an existing visible overlay (ensuring the close button is not
- // hidden).
- var children = document.querySelectorAll(
- '#sync-setup-overlay > *:not(.close-button)');
- for (var i = 0; i < children.length; i++)
- children[i].hidden = true;
-
- this.setInputElementsDisabledState_(false);
-
- // If new passphrase bodies are present, overwrite the existing ones.
- if (args && args.enterPassphraseBody != undefined)
- $('normal-body').innerHTML = args.enterPassphraseBody;
- if (args && args.enterGooglePassphraseBody != undefined) {
- $('google-passphrase-needed-body').innerHTML =
- args.enterGooglePassphraseBody;
- }
- if (args && args.fullEncryptionBody != undefined)
- $('full-encryption-body').innerHTML = args.fullEncryptionBody;
-
- // NOTE: Because both showGaiaLogin_() and showConfigure_() change the
- // focus, we need to ensure that the overlay container and dialog aren't
- // [hidden] (as trying to focus() nodes inside of a [hidden] DOM section
- // doesn't work).
- if (page == 'done')
- this.closeOverlay_();
- else
- this.showOverlay_();
-
- if (page == 'login')
- this.showGaiaLogin_(args);
- else if (page == 'configure' || page == 'passphrase')
- this.showConfigure_(args);
- else if (page == 'spinner')
- this.showSpinner_();
- else if (page == 'timeout')
- this.showTimeoutPage_();
- },
-
- /**
- * Changes the visibility of throbbers on this page.
- * @param {boolean} visible Whether or not to set all throbber nodes
- * visible.
- */
- setThrobbersVisible_: function(visible) {
- var throbbers = document.getElementsByClassName('throbber');
- for (var i = 0; i < throbbers.length; i++)
- throbbers[i].style.visibility = visible ? 'visible' : 'hidden';
- },
-
- /**
- * Set the appropriate focus on the GAIA login section of the overlay.
- * @private
- */
- loginSetFocus_: function() {
- var email = this.getLoginEmail_();
- if (email && !email.value) {
- email.focus();
- return;
- }
-
- var passwd = this.getLoginPasswd_();
- if (passwd)
- passwd.focus();
- },
-
- /**
- * Get the login email text input DOM element.
- * @return {DOMElement} The login email text input.
- * @private
- */
- getLoginEmail_: function() {
- return $('gaia-email');
- },
-
- /**
- * Get the login password text input DOM element.
- * @return {DOMElement} The login password text input.
- * @private
- */
- getLoginPasswd_: function() {
- return $('gaia-passwd');
- },
-
- /**
- * Get the sign in button DOM element.
- * @return {DOMElement} The sign in button.
- * @private
- */
- getSignInButton_: function() {
- return $('sign-in');
- },
-
- showAccessCodeRequired_: function() {
- this.allowEmptyPassword_ = true;
-
- $('password-row').hidden = true;
- $('email-row').hidden = true;
- $('otp-input-row').hidden = true;
-
- $('access-code-input-row').hidden = false;
- $('access-code').disabled = false;
- $('access-code').focus();
- },
-
- showOtpRequired_: function() {
- this.allowEmptyPassword_ = true;
-
- $('password-row').hidden = true;
- $('email-row').hidden = true;
- $('access-code-input-row').hidden = true;
-
- $('otp-input-row').hidden = false;
- $('otp').disabled = false;
- $('otp').focus();
- },
-
- showCaptcha_: function(args) {
- this.allowEmptyPassword_ = args.hideEmailAndPassword;
- this.captchaChallengeActive_ = true;
-
- if (args.hideEmailAndPassword) {
- $('password-row').hidden = true;
- $('email-row').hidden = true;
- $('create-account-div').hidden = true;
- } else {
- // The captcha takes up lots of space, so make room.
- $('top-blurb-error').hidden = true;
- $('create-account-div').hidden = true;
- $('gaia-email').disabled = true;
- $('gaia-passwd').disabled = false;
- }
-
- // It's showtime for the captcha now.
- $('captcha-div').hidden = false;
- $('captcha-value').disabled = false;
- $('captcha-wrapper').style.backgroundImage = url(args.captchaUrl);
- },
-
- /**
- * Reset the state of all descendant elements of a root element to their
- * initial state.
- * The initial state is specified by adding a class to the descendant
- * element in sync_setup_overlay.html.
- * @param {HTMLElement} pageElementId The root page element id.
- * @private
- */
- resetPage_: function(pageElementId) {
- var page = $(pageElementId);
- var forEach = function(arr, fn) {
- var length = arr.length;
- for (var i = 0; i < length; i++) {
- fn(arr[i]);
- }
- };
-
- forEach(page.getElementsByClassName('reset-hidden'),
- function(elt) { elt.hidden = true; });
- forEach(page.getElementsByClassName('reset-shown'),
- function(elt) { elt.hidden = false; });
- forEach(page.getElementsByClassName('reset-disabled'),
- function(elt) { elt.disabled = true; });
- forEach(page.getElementsByClassName('reset-enabled'),
- function(elt) { elt.disabled = false; });
- forEach(page.getElementsByClassName('reset-value'),
- function(elt) { elt.value = ''; });
- forEach(page.getElementsByClassName('reset-opaque'),
- function(elt) { elt.classList.remove('transparent'); });
- },
-
- showGaiaLogin_: function(args) {
- var oldAccessCodeValue = $('access-code').value;
- this.resetPage_('sync-setup-login');
- $('sync-setup-login').hidden = false;
- this.allowEmptyPassword_ = false;
- this.captchaChallengeActive_ = false;
- this.lastEmailAddress_ = args.lastEmailAddress;
-
- var f = $('gaia-login-form');
- var email = $('gaia-email');
- var passwd = $('gaia-passwd');
- if (f) {
- if (args.user != undefined) {
- if (email.value != args.user)
- passwd.value = ''; // Reset the password field
- email.value = args.user;
- }
-
- if (!args.editableUser) {
- $('email-row').hidden = true;
- var span = $('email-readonly');
- span.textContent = email.value;
- $('email-readonly-row').hidden = false;
- $('create-account-div').hidden = true;
- }
-
- f.accessCode.disabled = true;
- f.otp.disabled = true;
- }
-
- if (1 == args.error) {
- if (oldAccessCodeValue) {
- $('errormsg-0-access-code').hidden = false;
- this.showAccessCodeRequired_();
- } else {
- $('errormsg-1-password').hidden = (args.errorMessage != undefined);
- }
- this.setBlurbError_(args.errorMessage);
- } else if (3 == args.error) {
- $('errormsg-0-connection').hidden = false;
- this.setBlurbError_(args.errorMessage);
- } else if (4 == args.error) {
- this.showCaptcha_(args);
- } else if (7 == args.error) {
- this.setBlurbError_(loadTimeData.getString('serviceUnavailableError'));
- } else if (8 == args.error) {
- if (args.askForOtp) {
- this.showOtpRequired_();
- } else {
- if (oldAccessCodeValue)
- $('errormsg-0-access-code').hidden = false;
- this.showAccessCodeRequired_();
- }
- } else if (args.errorMessage) {
- this.setBlurbError_(args.errorMessage);
- }
-
- if (args.fatalError) {
- $('errormsg-fatal').hidden = false;
- $('sign-in').disabled = true;
- return;
- }
-
- $('sign-in').disabled = false;
- $('sign-in').value = loadTimeData.getString('signin');
- this.loginSetFocus_();
- },
-
- resetErrorVisibility_: function() {
- $('errormsg-0-email').hidden = true;
- $('errormsg-0-password').hidden = true;
- $('errormsg-1-password').hidden = true;
- $('errormsg-different-email').hidden = true;
- $('errormsg-0-connection').hidden = true;
- $('errormsg-0-access-code').hidden = true;
- $('errormsg-0-otp').hidden = true;
- },
-
- setBlurbError_: function(errorMessage) {
- if (this.captchaChallengeActive_)
- return; // No blurb in captcha challenge mode.
-
- if (errorMessage) {
- $('error-signing-in').hidden = true;
- $('error-custom').hidden = false;
- $('error-custom').textContent = errorMessage;
- } else {
- $('error-signing-in').hidden = false;
- $('error-custom').hidden = true;
- }
-
- $('top-blurb-error').hidden = false;
- $('gaia-email').disabled = false;
- $('gaia-passwd').disabled = false;
- },
-
- matchesASPRegex_: function(toMatch) {
- var noSpaces = /[a-z]{16}/;
- var withSpaces = /([a-z]{4}\s){3}[a-z]{4}/;
- if (toMatch.match(noSpaces) || toMatch.match(withSpaces))
- return true;
- return false;
- },
-
- setErrorVisibility_: function() {
- var errormsgDifferentEmail = $('errormsg-different-email');
- var isErrormsgDifferentEmailHidden = errormsgDifferentEmail.hidden;
- this.resetErrorVisibility_();
- var f = $('gaia-login-form');
- var email = $('gaia-email');
- var passwd = $('gaia-passwd');
- if (!email.value) {
- $('errormsg-0-email').hidden = false;
- this.setBlurbError_();
- return false;
- }
- // If email is different from last email, and we have not already warned
- // the user, tell them now. Otherwise proceed as usual. When comparing
- // email ids, use @gmail.com as the domain if not provided.
- function normalized_email(id) {
- return ((id.indexOf('@') != -1) ? id : id + '@gmail.com');
- }
- if (this.lastEmailAddress_.length > 0 &&
- normalized_email(email.value) !=
- normalized_email(this.lastEmailAddress_) &&
- isErrormsgDifferentEmailHidden) {
- errormsgDifferentEmail.hidden = false;
- return false;
- }
- // Don't enforce password being non-blank when checking access code (it
- // will have been cleared when the page was displayed).
- if (!this.allowEmptyPassword_ && !passwd.value) {
- $('errormsg-0-password').hidden = false;
- this.setBlurbError_();
- return false;
- }
-
- if (!f.accessCode.disabled && !f.accessCode.value) {
- $('errormsg-0-access-code').hidden = false;
- return false;
- }
-
- if (f.accessCode.disabled && this.matchesASPRegex_(passwd.value) &&
- $('asp-warning-div').hidden) {
- $('asp-warning-div').hidden = false;
- $('gaia-passwd').value = '';
- return false;
- }
-
- if (!f.otp.disabled && !f.otp.value) {
- $('errormsg-0-otp').hidden = false;
- return false;
- }
-
- return true;
- },
-
- sendCredentialsAndClose_: function() {
- if (!this.setErrorVisibility_()) {
- return false;
- }
-
- $('gaia-email').disabled = true;
- $('gaia-passwd').disabled = true;
- $('captcha-value').disabled = true;
- $('access-code').disabled = true;
- $('otp').disabled = true;
-
- this.setThrobbersVisible_(true);
-
- var f = $('gaia-login-form');
- var email = $('gaia-email');
- var passwd = $('gaia-passwd');
- var result = JSON.stringify({'user': email.value,
- 'pass': passwd.value,
- 'captcha': f.captchaValue.value,
- 'otp': f.otp.value,
- 'accessCode': f.accessCode.value
- });
- $('sign-in').disabled = true;
- chrome.send('SyncSetupSubmitAuth', [result]);
- },
-
- showSuccessAndClose_: function() {
- $('sign-in').value = loadTimeData.getString('loginSuccess');
- setTimeout(this.closeOverlay_, 1600);
- },
-
- showSuccessAndSettingUp_: function() {
- $('sign-in').value = loadTimeData.getString('settingUp');
- this.setThrobbersVisible_(true);
- $('top-blurb-error').hidden = true;
- },
-
- /**
- * Displays the stop syncing dialog.
- * @private
- */
- showStopSyncingUI_: function() {
- // Hide any visible children of the overlay.
- var overlay = $('sync-setup-overlay');
- for (var i = 0; i < overlay.children.length; i++)
- overlay.children[i].hidden = true;
-
- // Bypass OptionsPage.navigateToPage because it will call didShowPage
- // which will set its own visible page, based on the flow state.
- this.visible = true;
-
- $('sync-setup-stop-syncing').hidden = false;
- $('stop-syncing-cancel').focus();
- },
-
- /**
- * Steps into the appropriate Sync Setup error UI.
- * @private
- */
- showErrorUI_: function() {
- chrome.send('SyncSetupShowErrorUI');
- },
-
- /**
- * Determines the appropriate page to show in the Sync Setup UI based on
- * the state of the Sync backend.
- * @private
- */
- showSetupUI_: function() {
- chrome.send('SyncSetupShowSetupUI');
- },
-
- /**
- * Shows advanced configuration UI, skipping the login dialog.
- * @private
- */
- showSetupUIWithoutLogin_: function() {
- chrome.send('SyncSetupShowSetupUIWithoutLogin');
- },
-
- /**
- * Forces user to sign out of Chrome for Chrome OS.
- * @private
- */
- doSignOutOnAuthError_: function() {
- chrome.send('SyncSetupDoSignOutOnAuthError');
- },
-
- /**
- * Hides the outer elements of the login UI. This is used by the sync promo
- * to customize the look of the login box.
- */
- hideOuterLoginUI_: function() {
- $('sync-setup-overlay-title').hidden = true;
- $('sync-setup-cancel').hidden = true;
- }
- };
-
- // These get methods should only be called by the WebUI tests.
- SyncSetupOverlay.getLoginEmail = function() {
- return SyncSetupOverlay.getInstance().getLoginEmail_();
- };
-
- SyncSetupOverlay.getLoginPasswd = function() {
- return SyncSetupOverlay.getInstance().getLoginPasswd_();
- };
-
- SyncSetupOverlay.getSignInButton = function() {
- return SyncSetupOverlay.getInstance().getSignInButton_();
- };
-
- // These methods are for general consumption.
- SyncSetupOverlay.showErrorUI = function() {
- SyncSetupOverlay.getInstance().showErrorUI_();
- };
-
- SyncSetupOverlay.showSetupUI = function() {
- SyncSetupOverlay.getInstance().showSetupUI_();
- };
-
- SyncSetupOverlay.showSetupUIWithoutLogin = function() {
- SyncSetupOverlay.getInstance().showSetupUIWithoutLogin_();
- };
-
- SyncSetupOverlay.doSignOutOnAuthError = function() {
- SyncSetupOverlay.getInstance().doSignOutOnAuthError_();
- };
-
- SyncSetupOverlay.showSyncSetupPage = function(page, args) {
- SyncSetupOverlay.getInstance().showSyncSetupPage_(page, args);
- };
-
- SyncSetupOverlay.showSuccessAndClose = function() {
- SyncSetupOverlay.getInstance().showSuccessAndClose_();
- };
-
- SyncSetupOverlay.showSuccessAndSettingUp = function() {
- SyncSetupOverlay.getInstance().showSuccessAndSettingUp_();
- };
-
- SyncSetupOverlay.showStopSyncingUI = function() {
- SyncSetupOverlay.getInstance().showStopSyncingUI_();
- };
-
- // Export
- return {
- SyncSetupOverlay: SyncSetupOverlay
- };
- });
-
-
- cr.define('sync_promo', function() {
- /**
- * SyncPromo class
- * Subclass of options.SyncSetupOverlay that customizes the sync setup
- * overlay for use in the new tab page.
- * @class
- */
- function SyncPromo() {
- options.SyncSetupOverlay.call(this, 'syncSetup',
- loadTimeData.getString('syncSetupOverlayTabTitle'),
- 'sync-setup-overlay');
- }
-
- // Replicating enum from chrome/common/extensions/extension_constants.h.
- /** @const */ var actions = (function() {
- var i = 0;
- return {
- VIEWED: i++,
- LEARN_MORE_CLICKED: i++,
- ACCOUNT_HELP_CLICKED: i++,
- CREATE_ACCOUNT_CLICKED: i++,
- SKIP_CLICKED: i++,
- SIGN_IN_ATTEMPTED: i++,
- SIGNED_IN_SUCCESSFULLY: i++,
- ADVANCED_CLICKED: i++,
- ENCRYPTION_HELP_CLICKED: i++,
- CANCELLED_AFTER_SIGN_IN: i++,
- CONFIRMED_AFTER_SIGN_IN: i++,
- CLOSED_TAB: i++,
- CLOSED_WINDOW: i++,
- LEFT_DURING_THROBBER: i++,
- };
- }());
-
- cr.addSingletonGetter(SyncPromo);
-
- SyncPromo.prototype = {
- __proto__: options.SyncSetupOverlay.prototype,
-
- showOverlay_: function() {
- $('sync-setup-overlay').hidden = false;
- },
-
- closeOverlay_: function() {
- chrome.send('SyncPromo:Close');
- },
-
- // Initializes the page.
- initializePage: function() {
- options.SyncSetupOverlay.prototype.initializePage.call(this);
-
- // Hide parts of the login UI and show the promo UI.
- this.hideOuterLoginUI_();
- $('promo-skip').hidden = false;
-
- chrome.send('SyncPromo:Initialize');
-
- var self = this;
-
- $('promo-skip-button').addEventListener('click', function() {
- chrome.send('SyncPromo:UserSkipped');
- self.closeOverlay_();
- });
-
- var learnMoreClickedAlready = false;
- $('promo-learn-more').addEventListener('click', function() {
- if (!learnMoreClickedAlready)
- chrome.send('SyncPromo:UserFlowAction', [actions.LEARN_MORE_CLICKED]);
- learnMoreClickedAlready = true;
- });
-
- $('promo-advanced').addEventListener('click', function() {
- chrome.send('SyncPromo:ShowAdvancedSettings');
- });
-
- var accountHelpClickedAlready = false;
- $('cannot-access-account-link').addEventListener('click', function() {
- if (!accountHelpClickedAlready)
- chrome.send('SyncPromo:UserFlowAction',
- [actions.ACCOUNT_HELP_CLICKED]);
- accountHelpClickedAlready = true;
- });
-
- var createAccountClickedAlready = false;
- $('create-account-link').addEventListener('click', function() {
- if (!createAccountClickedAlready)
- chrome.send('SyncPromo:UserFlowAction',
- [actions.CREATE_ACCOUNT_CLICKED]);
- createAccountClickedAlready = true;
- });
-
- // We listen to the <form>'s submit vs. the <input type="submit"> click so
- // we also track users that use the keyboard and press enter.
- var signInAttemptedAlready = false;
- $('gaia-login-form').addEventListener('submit', function() {
- ++self.signInAttempts_;
- if (!signInAttemptedAlready)
- chrome.send('SyncPromo:UserFlowAction', [actions.SIGN_IN_ATTEMPTED]);
- signInAttemptedAlready = true;
- });
-
- var encryptionHelpClickedAlready = false;
- $('encryption-help-link').addEventListener('click', function() {
- if (!encryptionHelpClickedAlready)
- chrome.send('SyncPromo:UserFlowAction',
- [actions.ENCRYPTION_HELP_CLICKED]);
- encryptionHelpClickedAlready = true;
- });
-
- var advancedOptionsClickedAlready = false;
- $('customize-link').addEventListener('click', function() {
- if (!advancedOptionsClickedAlready)
- chrome.send('SyncPromo:UserFlowAction', [actions.ADVANCED_CLICKED]);
- advancedOptionsClickedAlready = true;
- });
-
- // Re-used across both cancel buttons after a successful sign in.
- var cancelFunc = function() {
- chrome.send('SyncPromo:UserFlowAction',
- [actions.CANCELLED_AFTER_SIGN_IN]);
- };
- $('confirm-everything-cancel').addEventListener('click', cancelFunc);
- $('choose-datatypes-cancel').addEventListener('click', cancelFunc);
-
- // Add the source parameter to the document so that it can be used to
- // selectively show and hide elements using CSS.
- var params = parseQueryParams(document.location);
- if (params.source == '0')
- document.documentElement.setAttribute('isstartup', '');
- },
-
- /**
- * Called when the page is unloading.
- * @private
- */
- onUnload: function() {
- // Record number of times a user tried to sign in and if they left
- // while a throbber was running.
- chrome.send('SyncPromo:RecordSignInAttempts', [this.signInAttempts_]);
- if (this.throbberStart_)
- chrome.send('SyncPromo:UserFlowAction', [actions.LEFT_DURING_THROBBER]);
- chrome.send('SyncSetupDidClosePage');
- },
-
- /** @override */
- sendConfiguration_: function() {
- chrome.send('SyncPromo:UserFlowAction',
- [actions.CONFIRMED_AFTER_SIGN_IN]);
- options.SyncSetupOverlay.prototype.sendConfiguration_.apply(this,
- arguments);
- },
-
- /** @override */
- setThrobbersVisible_: function(visible) {
- if (visible) {
- this.throbberStart_ = Date.now();
- } else {
- if (this.throbberStart_) {
- chrome.send('SyncPromo:RecordThrobberTime',
- [Date.now() - this.throbberStart_]);
- }
- this.throbberStart_ = 0;
- }
- // Pass through to SyncSetupOverlay to handle display logic.
- options.SyncSetupOverlay.prototype.setThrobbersVisible_.apply(
- this, arguments);
- },
-
- /**
- * Number of times a user attempted to sign in to GAIA during this page
- * view.
- * @private
- */
- signInAttempts_: 0,
-
- /**
- * The start time of a throbber on the page.
- * @private
- */
- throbberStart_: 0,
- };
-
- SyncPromo.showErrorUI = function() {
- SyncPromo.getInstance().showErrorUI_();
- };
-
- SyncPromo.showSyncSetupPage = function(page, args) {
- SyncPromo.getInstance().showSyncSetupPage_(page, args);
- };
-
- SyncPromo.showSuccessAndClose = function() {
- SyncPromo.getInstance().showSuccessAndClose_();
- };
-
- SyncPromo.showSuccessAndSettingUp = function() {
- chrome.send('SyncPromo:UserFlowAction', [actions.SIGNED_IN_SUCCESSFULLY]);
- SyncPromo.getInstance().showSuccessAndSettingUp_();
- };
-
- SyncPromo.showStopSyncingUI = function() {
- SyncPromo.getInstance().showStopSyncingUI_();
- };
-
- SyncPromo.initialize = function() {
- SyncPromo.getInstance().initializePage();
- };
-
- SyncPromo.onUnload = function() {
- SyncPromo.getInstance().onUnload();
- };
-
- SyncPromo.populatePromoMessage = function(resName) {
- SyncPromo.getInstance().populatePromoMessage_(resName);
- };
-
- // Export
- return {
- SyncPromo: SyncPromo
- };
- });
-
- var OptionsPage = options.OptionsPage;
- var SyncSetupOverlay = sync_promo.SyncPromo;
- window.addEventListener('DOMContentLoaded', sync_promo.SyncPromo.initialize);
- window.addEventListener('unload',
- sync_promo.SyncPromo.onUnload.bind(sync_promo.SyncPromo));
-