Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(65)

Side by Side Diff: chrome/browser/resources/options2/options_page.js

Issue 10809005: Options: Rename chrome/browser/resources/options2 -> chrome/browser/resources/options. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix. Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 cr.define('options', function() {
6 /////////////////////////////////////////////////////////////////////////////
7 // OptionsPage class:
8
9 /**
10 * Base class for options page.
11 * @constructor
12 * @param {string} name Options page name.
13 * @param {string} title Options page title, used for history.
14 * @extends {EventTarget}
15 */
16 function OptionsPage(name, title, pageDivName) {
17 this.name = name;
18 this.title = title;
19 this.pageDivName = pageDivName;
20 this.pageDiv = $(this.pageDivName);
21 this.tab = null;
22 this.lastFocusedElement = null;
23 }
24
25 /** @const */ var HORIZONTAL_OFFSET = 155;
26
27 /**
28 * This is the absolute difference maintained between standard and
29 * fixed-width font sizes. Refer http://crbug.com/91922.
30 */
31 OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD = 3;
32
33 /**
34 * Main level option pages. Maps lower-case page names to the respective page
35 * object.
36 * @protected
37 */
38 OptionsPage.registeredPages = {};
39
40 /**
41 * Pages which are meant to behave like modal dialogs. Maps lower-case overlay
42 * names to the respective overlay object.
43 * @protected
44 */
45 OptionsPage.registeredOverlayPages = {};
46
47 /**
48 * Gets the default page (to be shown on initial load).
49 */
50 OptionsPage.getDefaultPage = function() {
51 return BrowserOptions.getInstance();
52 };
53
54 /**
55 * Shows the default page.
56 */
57 OptionsPage.showDefaultPage = function() {
58 this.navigateToPage(this.getDefaultPage().name);
59 };
60
61 /**
62 * "Navigates" to a page, meaning that the page will be shown and the
63 * appropriate entry is placed in the history.
64 * @param {string} pageName Page name.
65 */
66 OptionsPage.navigateToPage = function(pageName) {
67 this.showPageByName(pageName, true);
68 };
69
70 /**
71 * Shows a registered page. This handles both top-level and overlay pages.
72 * @param {string} pageName Page name.
73 * @param {boolean} updateHistory True if we should update the history after
74 * showing the page.
75 * @param {Object=} opt_propertyBag An optional bag of properties including
76 * replaceState (if history state should be replaced instead of pushed).
77 * @private
78 */
79 OptionsPage.showPageByName = function(pageName,
80 updateHistory,
81 opt_propertyBag) {
82 // If |opt_propertyBag| is non-truthy, homogenize to object.
83 opt_propertyBag = opt_propertyBag || {};
84
85 // Find the currently visible root-level page.
86 var rootPage = null;
87 for (var name in this.registeredPages) {
88 var page = this.registeredPages[name];
89 if (page.visible && !page.parentPage) {
90 rootPage = page;
91 break;
92 }
93 }
94
95 // Find the target page.
96 var targetPage = this.registeredPages[pageName.toLowerCase()];
97 if (!targetPage || !targetPage.canShowPage()) {
98 // If it's not a page, try it as an overlay.
99 if (!targetPage && this.showOverlay_(pageName, rootPage)) {
100 if (updateHistory)
101 this.updateHistoryState_(!!opt_propertyBag.replaceState);
102 return;
103 } else {
104 targetPage = this.getDefaultPage();
105 }
106 }
107
108 pageName = targetPage.name.toLowerCase();
109 var targetPageWasVisible = targetPage.visible;
110
111 // Determine if the root page is 'sticky', meaning that it
112 // shouldn't change when showing an overlay. This can happen for special
113 // pages like Search.
114 var isRootPageLocked =
115 rootPage && rootPage.sticky && targetPage.parentPage;
116
117 var allPageNames = Array.prototype.concat.call(
118 Object.keys(this.registeredPages),
119 Object.keys(this.registeredOverlayPages));
120
121 // Notify pages if they will be hidden.
122 for (var i = 0; i < allPageNames.length; ++i) {
123 var name = allPageNames[i];
124 var page = this.registeredPages[name] ||
125 this.registeredOverlayPages[name];
126 if (!page.parentPage && isRootPageLocked)
127 continue;
128 if (page.willHidePage && name != pageName &&
129 !page.isAncestorOfPage(targetPage)) {
130 page.willHidePage();
131 }
132 }
133
134 // Update visibilities to show only the hierarchy of the target page.
135 for (var i = 0; i < allPageNames.length; ++i) {
136 var name = allPageNames[i];
137 var page = this.registeredPages[name] ||
138 this.registeredOverlayPages[name];
139 if (!page.parentPage && isRootPageLocked)
140 continue;
141 page.visible = name == pageName || page.isAncestorOfPage(targetPage);
142 }
143
144 // Update the history and current location.
145 if (updateHistory)
146 this.updateHistoryState_(!!opt_propertyBag.replaceState);
147
148 // Update tab title.
149 this.setTitle_(targetPage.title);
150
151 // Notify pages if they were shown.
152 for (var i = 0; i < allPageNames.length; ++i) {
153 var name = allPageNames[i];
154 var page = this.registeredPages[name] ||
155 this.registeredOverlayPages[name];
156 if (!page.parentPage && isRootPageLocked)
157 continue;
158 if (!targetPageWasVisible && page.didShowPage &&
159 (name == pageName || page.isAncestorOfPage(targetPage))) {
160 page.didShowPage();
161 }
162 }
163 };
164
165 /**
166 * Sets the title of the page. This is accomplished by calling into the
167 * parent page API.
168 * @param {String} title The title string.
169 * @private
170 */
171 OptionsPage.setTitle_ = function(title) {
172 uber.invokeMethodOnParent('setTitle', {title: title});
173 };
174
175 /**
176 * Scrolls the page to the correct position (the top when opening an overlay,
177 * or the old scroll position a previously hidden overlay becomes visible).
178 * @private
179 */
180 OptionsPage.updateScrollPosition_ = function() {
181 var container = $('page-container');
182 var scrollTop = container.oldScrollTop || 0;
183 container.oldScrollTop = undefined;
184 window.scroll(document.body.scrollLeft, scrollTop);
185 };
186
187 /**
188 * Pushes the current page onto the history stack, overriding the last page
189 * if it is the generic chrome://settings/.
190 * @param {boolean} replace If true, allow no history events to be created.
191 * @param {object=} opt_params A bag of optional params, including:
192 * {boolean} ignoreHash Whether to include the hash or not.
193 * @private
194 */
195 OptionsPage.updateHistoryState_ = function(replace, opt_params) {
196 var page = this.getTopmostVisiblePage();
197 var path = window.location.pathname + window.location.hash;
198 if (path)
199 path = path.slice(1).replace(/\/(?:#|$)/, ''); // Remove trailing slash.
200 // The page is already in history (the user may have clicked the same link
201 // twice). Do nothing.
202 if (path == page.name &&
203 !document.documentElement.classList.contains('loading')) {
204 return;
205 }
206
207 var hash = opt_params && opt_params.ignoreHash ? '' : window.location.hash;
208
209 // If settings are embedded, tell the outer page to set its "path" to the
210 // inner frame's path.
211 var outerPath = (page == this.getDefaultPage() ? '' : page.name) + hash;
212 uber.invokeMethodOnParent('setPath', {path: outerPath});
213
214 // If there is no path, the current location is chrome://settings/.
215 // Override this with the new page.
216 var historyFunction = path && !replace ? window.history.pushState :
217 window.history.replaceState;
218 historyFunction.call(window.history,
219 {pageName: page.name},
220 page.title,
221 '/' + page.name + hash);
222
223 // Update tab title.
224 this.setTitle_(page.title);
225 };
226
227 /**
228 * Shows a registered Overlay page. Does not update history.
229 * @param {string} overlayName Page name.
230 * @param {OptionPage} rootPage The currently visible root-level page.
231 * @return {boolean} whether we showed an overlay.
232 */
233 OptionsPage.showOverlay_ = function(overlayName, rootPage) {
234 var overlay = this.registeredOverlayPages[overlayName.toLowerCase()];
235 if (!overlay || !overlay.canShowPage())
236 return false;
237
238 // Save the currently focused element in the page for restoration later.
239 var currentPage = this.getTopmostVisiblePage();
240 if (currentPage)
241 currentPage.lastFocusedElement = document.activeElement;
242
243 if ((!rootPage || !rootPage.sticky) && overlay.parentPage)
244 this.showPageByName(overlay.parentPage.name, false);
245
246 if (!overlay.visible) {
247 overlay.visible = true;
248 if (overlay.didShowPage) overlay.didShowPage();
249 }
250
251 // Update tab title.
252 this.setTitle_(overlay.title);
253
254 $('searchBox').setAttribute('aria-hidden', true);
255
256 return true;
257 };
258
259 /**
260 * Returns whether or not an overlay is visible.
261 * @return {boolean} True if an overlay is visible.
262 * @private
263 */
264 OptionsPage.isOverlayVisible_ = function() {
265 return this.getVisibleOverlay_() != null;
266 };
267
268 /**
269 * Returns the currently visible overlay, or null if no page is visible.
270 * @return {OptionPage} The visible overlay.
271 */
272 OptionsPage.getVisibleOverlay_ = function() {
273 var topmostPage = null;
274 for (var name in this.registeredOverlayPages) {
275 var page = this.registeredOverlayPages[name];
276 if (page.visible &&
277 (!topmostPage || page.nestingLevel > topmostPage.nestingLevel)) {
278 topmostPage = page;
279 }
280 }
281 return topmostPage;
282 };
283
284 /**
285 * Restores the last focused element on a given page.
286 */
287 OptionsPage.restoreLastFocusedElement_ = function() {
288 var currentPage = this.getTopmostVisiblePage();
289 if (currentPage.lastFocusedElement)
290 currentPage.lastFocusedElement.focus();
291 };
292
293 /**
294 * Closes the visible overlay. Updates the history state after closing the
295 * overlay.
296 */
297 OptionsPage.closeOverlay = function() {
298 var overlay = this.getVisibleOverlay_();
299 if (!overlay)
300 return;
301
302 overlay.visible = false;
303
304 if (overlay.didClosePage) overlay.didClosePage();
305 this.updateHistoryState_(false, {ignoreHash: true});
306
307 this.restoreLastFocusedElement_();
308 if (!this.isOverlayVisible_())
309 $('searchBox').removeAttribute('aria-hidden');
310 };
311
312 /**
313 * Cancels (closes) the overlay, due to the user pressing <Esc>.
314 */
315 OptionsPage.cancelOverlay = function() {
316 // Blur the active element to ensure any changed pref value is saved.
317 document.activeElement.blur();
318 var overlay = this.getVisibleOverlay_();
319 // Let the overlay handle the <Esc> if it wants to.
320 if (overlay.handleCancel) {
321 overlay.handleCancel();
322 this.restoreLastFocusedElement_();
323 } else {
324 this.closeOverlay();
325 }
326 };
327
328 /**
329 * Hides the visible overlay. Does not affect the history state.
330 * @private
331 */
332 OptionsPage.hideOverlay_ = function() {
333 var overlay = this.getVisibleOverlay_();
334 if (overlay)
335 overlay.visible = false;
336 };
337
338 /**
339 * Returns the pages which are currently visible, ordered by nesting level
340 * (ascending).
341 * @return {Array.OptionPage} The pages which are currently visible, ordered
342 * by nesting level (ascending).
343 */
344 OptionsPage.getVisiblePages_ = function() {
345 var visiblePages = [];
346 for (var name in this.registeredPages) {
347 var page = this.registeredPages[name];
348 if (page.visible)
349 visiblePages[page.nestingLevel] = page;
350 }
351 return visiblePages;
352 };
353
354 /**
355 * Returns the topmost visible page (overlays excluded).
356 * @return {OptionPage} The topmost visible page aside any overlay.
357 * @private
358 */
359 OptionsPage.getTopmostVisibleNonOverlayPage_ = function() {
360 var topPage = null;
361 for (var name in this.registeredPages) {
362 var page = this.registeredPages[name];
363 if (page.visible &&
364 (!topPage || page.nestingLevel > topPage.nestingLevel))
365 topPage = page;
366 }
367
368 return topPage;
369 };
370
371 /**
372 * Returns the topmost visible page, or null if no page is visible.
373 * @return {OptionPage} The topmost visible page.
374 */
375 OptionsPage.getTopmostVisiblePage = function() {
376 // Check overlays first since they're top-most if visible.
377 return this.getVisibleOverlay_() || this.getTopmostVisibleNonOverlayPage_();
378 };
379
380 /**
381 * Updates managed banner visibility state based on the topmost page.
382 */
383 OptionsPage.updateManagedBannerVisibility = function() {
384 var topPage = this.getTopmostVisiblePage();
385 if (topPage)
386 topPage.updateManagedBannerVisibility();
387 };
388
389 /**
390 * Shows the tab contents for the given navigation tab.
391 * @param {!Element} tab The tab that the user clicked.
392 */
393 OptionsPage.showTab = function(tab) {
394 // Search parents until we find a tab, or the nav bar itself. This allows
395 // tabs to have child nodes, e.g. labels in separately-styled spans.
396 while (tab && !tab.classList.contains('subpages-nav-tabs') &&
397 !tab.classList.contains('tab')) {
398 tab = tab.parentNode;
399 }
400 if (!tab || !tab.classList.contains('tab'))
401 return;
402
403 // Find tab bar of the tab.
404 var tabBar = tab;
405 while (tabBar && !tabBar.classList.contains('subpages-nav-tabs')) {
406 tabBar = tabBar.parentNode;
407 }
408 if (!tabBar)
409 return;
410
411 if (tabBar.activeNavTab != null) {
412 tabBar.activeNavTab.classList.remove('active-tab');
413 $(tabBar.activeNavTab.getAttribute('tab-contents')).classList.
414 remove('active-tab-contents');
415 }
416
417 tab.classList.add('active-tab');
418 $(tab.getAttribute('tab-contents')).classList.add('active-tab-contents');
419 tabBar.activeNavTab = tab;
420 };
421
422 /**
423 * Registers new options page.
424 * @param {OptionsPage} page Page to register.
425 */
426 OptionsPage.register = function(page) {
427 this.registeredPages[page.name.toLowerCase()] = page;
428 page.initializePage();
429 };
430
431 /**
432 * Find an enclosing section for an element if it exists.
433 * @param {Element} element Element to search.
434 * @return {OptionPage} The section element, or null.
435 * @private
436 */
437 OptionsPage.findSectionForNode_ = function(node) {
438 while (node = node.parentNode) {
439 if (node.nodeName == 'SECTION')
440 return node;
441 }
442 return null;
443 };
444
445 /**
446 * Registers a new Overlay page.
447 * @param {OptionsPage} overlay Overlay to register.
448 * @param {OptionsPage} parentPage Associated parent page for this overlay.
449 * @param {Array} associatedControls Array of control elements associated with
450 * this page.
451 */
452 OptionsPage.registerOverlay = function(overlay,
453 parentPage,
454 associatedControls) {
455 this.registeredOverlayPages[overlay.name.toLowerCase()] = overlay;
456 overlay.parentPage = parentPage;
457 if (associatedControls) {
458 overlay.associatedControls = associatedControls;
459 if (associatedControls.length) {
460 overlay.associatedSection =
461 this.findSectionForNode_(associatedControls[0]);
462 }
463 }
464
465 // Reverse the button strip for views. See the documentation of
466 // reverseButtonStrip_() for an explanation of why this is necessary.
467 if (cr.isViews)
468 this.reverseButtonStrip_(overlay);
469
470 overlay.tab = undefined;
471 overlay.isOverlay = true;
472 overlay.initializePage();
473 };
474
475 /**
476 * Reverses the child elements of a button strip. This is necessary because
477 * WebKit does not alter the tab order for elements that are visually reversed
478 * using -webkit-box-direction: reverse, and the button order is reversed for
479 * views. See https://bugs.webkit.org/show_bug.cgi?id=62664 for more
480 * information.
481 * @param {Object} overlay The overlay containing the button strip to reverse.
482 * @private
483 */
484 OptionsPage.reverseButtonStrip_ = function(overlay) {
485 var buttonStrips = overlay.pageDiv.querySelectorAll('.button-strip');
486
487 // Reverse all button-strips in the overlay.
488 for (var j = 0; j < buttonStrips.length; j++) {
489 var buttonStrip = buttonStrips[j];
490
491 var childNodes = buttonStrip.childNodes;
492 for (var i = childNodes.length - 1; i >= 0; i--)
493 buttonStrip.appendChild(childNodes[i]);
494 }
495 };
496
497 /**
498 * Callback for window.onpopstate.
499 * @param {Object} data State data pushed into history.
500 */
501 OptionsPage.setState = function(data) {
502 if (data && data.pageName) {
503 this.willClose();
504 this.showPageByName(data.pageName, false);
505 }
506 };
507
508 /**
509 * Callback for window.onbeforeunload. Used to notify overlays that they will
510 * be closed.
511 */
512 OptionsPage.willClose = function() {
513 var overlay = this.getVisibleOverlay_();
514 if (overlay && overlay.didClosePage)
515 overlay.didClosePage();
516 };
517
518 /**
519 * Freezes/unfreezes the scroll position of the root page container.
520 * @param {boolean} freeze Whether the page should be frozen.
521 * @private
522 */
523 OptionsPage.setRootPageFrozen_ = function(freeze) {
524 var container = $('page-container');
525 if (container.classList.contains('frozen') == freeze)
526 return;
527
528 if (freeze) {
529 // Lock the width, since auto width computation may change.
530 container.style.width = window.getComputedStyle(container).width;
531 container.oldScrollTop = document.body.scrollTop;
532 container.classList.add('frozen');
533 var verticalPosition =
534 container.getBoundingClientRect().top - container.oldScrollTop;
535 container.style.top = verticalPosition + 'px';
536 this.updateFrozenElementHorizontalPosition_(container);
537 } else {
538 container.classList.remove('frozen');
539 container.style.top = '';
540 container.style.left = '';
541 container.style.right = '';
542 container.style.width = '';
543 }
544 };
545
546 /**
547 * Freezes/unfreezes the scroll position of the root page based on the current
548 * page stack.
549 */
550 OptionsPage.updateRootPageFreezeState = function() {
551 var topPage = OptionsPage.getTopmostVisiblePage();
552 if (topPage)
553 this.setRootPageFrozen_(topPage.isOverlay);
554 };
555
556 /**
557 * Initializes the complete options page. This will cause all C++ handlers to
558 * be invoked to do final setup.
559 */
560 OptionsPage.initialize = function() {
561 chrome.send('coreOptionsInitialize');
562 uber.onContentFrameLoaded();
563
564 document.addEventListener('scroll', this.handleScroll_.bind(this));
565
566 // Trigger the scroll handler manually to set the initial state.
567 this.handleScroll_();
568
569 // Shake the dialog if the user clicks outside the dialog bounds.
570 var containers = [$('overlay-container-1'), $('overlay-container-2')];
571 for (var i = 0; i < containers.length; i++) {
572 var overlay = containers[i];
573 cr.ui.overlay.setupOverlay(overlay);
574 overlay.addEventListener('cancelOverlay',
575 OptionsPage.cancelOverlay.bind(OptionsPage));
576 }
577 };
578
579 /**
580 * Does a bounds check for the element on the given x, y client coordinates.
581 * @param {Element} e The DOM element.
582 * @param {number} x The client X to check.
583 * @param {number} y The client Y to check.
584 * @return {boolean} True if the point falls within the element's bounds.
585 * @private
586 */
587 OptionsPage.elementContainsPoint_ = function(e, x, y) {
588 var clientRect = e.getBoundingClientRect();
589 return x >= clientRect.left && x <= clientRect.right &&
590 y >= clientRect.top && y <= clientRect.bottom;
591 };
592
593 /**
594 * Called when the page is scrolled; moves elements that are position:fixed
595 * but should only behave as if they are fixed for vertical scrolling.
596 * @private
597 */
598 OptionsPage.handleScroll_ = function() {
599 this.updateAllFrozenElementPositions_();
600 };
601
602 /**
603 * Updates all frozen pages to match the horizontal scroll position.
604 * @private
605 */
606 OptionsPage.updateAllFrozenElementPositions_ = function() {
607 var frozenElements = document.querySelectorAll('.frozen');
608 for (var i = 0; i < frozenElements.length; i++)
609 this.updateFrozenElementHorizontalPosition_(frozenElements[i]);
610 };
611
612 /**
613 * Updates the given frozen element to match the horizontal scroll position.
614 * @param {HTMLElement} e The frozen element to update.
615 * @private
616 */
617 OptionsPage.updateFrozenElementHorizontalPosition_ = function(e) {
618 if (isRTL())
619 e.style.right = HORIZONTAL_OFFSET + 'px';
620 else
621 e.style.left = HORIZONTAL_OFFSET - document.body.scrollLeft + 'px';
622 };
623
624 OptionsPage.setClearPluginLSODataEnabled = function(enabled) {
625 if (enabled) {
626 document.documentElement.setAttribute(
627 'flashPluginSupportsClearSiteData', '');
628 } else {
629 document.documentElement.removeAttribute(
630 'flashPluginSupportsClearSiteData');
631 }
632 };
633
634 OptionsPage.setPepperFlashSettingsEnabled = function(enabled) {
635 if (enabled) {
636 document.documentElement.setAttribute(
637 'enablePepperFlashSettings', '');
638 } else {
639 document.documentElement.removeAttribute(
640 'enablePepperFlashSettings');
641 }
642 };
643
644 OptionsPage.prototype = {
645 __proto__: cr.EventTarget.prototype,
646
647 /**
648 * The parent page of this option page, or null for top-level pages.
649 * @type {OptionsPage}
650 */
651 parentPage: null,
652
653 /**
654 * The section on the parent page that is associated with this page.
655 * Can be null.
656 * @type {Element}
657 */
658 associatedSection: null,
659
660 /**
661 * An array of controls that are associated with this page. The first
662 * control should be located on a top-level page.
663 * @type {OptionsPage}
664 */
665 associatedControls: null,
666
667 /**
668 * Initializes page content.
669 */
670 initializePage: function() {},
671
672 /**
673 * Updates managed banner visibility state. This function iterates over
674 * all input fields of a page and if any of these is marked as managed
675 * it triggers the managed banner to be visible. The banner can be enforced
676 * being on through the managed flag of this class but it can not be forced
677 * being off if managed items exist.
678 */
679 updateManagedBannerVisibility: function() {
680 var bannerDiv = this.pageDiv.querySelector('.managed-prefs-banner');
681 // Create a banner for the overlay if we don't have one.
682 if (!bannerDiv) {
683 bannerDiv = $('managed-prefs-banner').cloneNode(true);
684 bannerDiv.id = null;
685
686 if (this.isOverlay) {
687 var content = this.pageDiv.querySelector('.content-area');
688 content.parentElement.insertBefore(bannerDiv, content);
689 } else {
690 bannerDiv.classList.add('main-page-banner');
691 var header = this.pageDiv.querySelector('header');
692 header.appendChild(bannerDiv);
693 }
694 }
695
696 var controlledByPolicy = false;
697 var controlledByExtension = false;
698 var inputElements = this.pageDiv.querySelectorAll('input[controlled-by]');
699 for (var i = 0; i < inputElements.length; i++) {
700 if (inputElements[i].controlledBy == 'policy')
701 controlledByPolicy = true;
702 else if (inputElements[i].controlledBy == 'extension')
703 controlledByExtension = true;
704 }
705
706 if (!controlledByPolicy && !controlledByExtension) {
707 this.pageDiv.classList.remove('showing-banner');
708 } else {
709 this.pageDiv.classList.add('showing-banner');
710
711 var text = bannerDiv.querySelector('#managed-prefs-text');
712 if (controlledByPolicy && !controlledByExtension) {
713 text.textContent =
714 loadTimeData.getString('policyManagedPrefsBannerText');
715 } else if (!controlledByPolicy && controlledByExtension) {
716 text.textContent =
717 loadTimeData.getString('extensionManagedPrefsBannerText');
718 } else if (controlledByPolicy && controlledByExtension) {
719 text.textContent = loadTimeData.getString(
720 'policyAndExtensionManagedPrefsBannerText');
721 }
722 }
723 },
724
725 /**
726 * Gets the container div for this page if it is an overlay.
727 * @type {HTMLElement}
728 */
729 get container() {
730 assert(this.isOverlay);
731 return this.pageDiv.parentNode;
732 },
733
734 /**
735 * Gets page visibility state.
736 * @type {boolean}
737 */
738 get visible() {
739 // If this is an overlay dialog it is no longer considered visible while
740 // the overlay is fading out. See http://crbug.com/118629.
741 if (this.isOverlay &&
742 this.container.classList.contains('transparent')) {
743 return false;
744 }
745 return !this.pageDiv.hidden;
746 },
747
748 /**
749 * Sets page visibility.
750 * @type {boolean}
751 */
752 set visible(visible) {
753 if ((this.visible && visible) || (!this.visible && !visible))
754 return;
755
756 // If using an overlay, the visibility of the dialog is toggled at the
757 // same time as the overlay to show the dialog's out transition. This
758 // is handled in setOverlayVisible.
759 if (this.isOverlay) {
760 this.setOverlayVisible_(visible);
761 } else {
762 this.pageDiv.hidden = !visible;
763 this.onVisibilityChanged_();
764 }
765
766 cr.dispatchPropertyChange(this, 'visible', visible, !visible);
767 },
768
769 /**
770 * Shows or hides an overlay (including any visible dialog).
771 * @param {boolean} visible Whether the overlay should be visible or not.
772 * @private
773 */
774 setOverlayVisible_: function(visible) {
775 assert(this.isOverlay);
776 var pageDiv = this.pageDiv;
777 var container = this.container;
778
779 if (visible) {
780 uber.invokeMethodOnParent('beginInterceptingEvents');
781 this.pageDiv.removeAttribute('aria-hidden');
782 if (this.parentPage)
783 this.parentPage.pageDiv.setAttribute('aria-hidden', true);
784 } else {
785 if (this.parentPage)
786 this.parentPage.pageDiv.removeAttribute('aria-hidden');
787 }
788
789 if (container.hidden != visible) {
790 if (visible) {
791 // If the container is set hidden and then immediately set visible
792 // again, the fadeCompleted_ callback would cause it to be erroneously
793 // hidden again. Removing the transparent tag avoids that.
794 container.classList.remove('transparent');
795
796 // Hide all dialogs in this container since a different one may have
797 // been previously visible before fading out.
798 var pages = container.querySelectorAll('.page');
799 for (var i = 0; i < pages.length; i++)
800 pages[i].hidden = true;
801 // Show the new dialog.
802 pageDiv.hidden = false;
803 }
804 return;
805 }
806
807 if (visible) {
808 container.hidden = false;
809 pageDiv.hidden = false;
810 // NOTE: This is a hacky way to force the container to layout which
811 // will allow us to trigger the webkit transition.
812 container.scrollTop;
813 container.classList.remove('transparent');
814 this.onVisibilityChanged_();
815 } else {
816 var self = this;
817 // TODO: Use an event delegate to avoid having to subscribe and
818 // unsubscribe for webkitTransitionEnd events.
819 container.addEventListener('webkitTransitionEnd', function f(e) {
820 if (e.target != e.currentTarget || e.propertyName != 'opacity')
821 return;
822 container.removeEventListener('webkitTransitionEnd', f);
823 self.fadeCompleted_();
824 });
825 container.classList.add('transparent');
826 }
827 },
828
829 /**
830 * Called when a container opacity transition finishes.
831 * @private
832 */
833 fadeCompleted_: function() {
834 if (this.container.classList.contains('transparent')) {
835 this.pageDiv.hidden = true;
836 this.container.hidden = true;
837 this.onVisibilityChanged_();
838 if (this.nestingLevel == 1)
839 uber.invokeMethodOnParent('stopInterceptingEvents');
840 }
841 },
842
843 /**
844 * Called when a page is shown or hidden to update the root options page
845 * based on this page's visibility.
846 * @private
847 */
848 onVisibilityChanged_: function() {
849 OptionsPage.updateRootPageFreezeState();
850 OptionsPage.updateManagedBannerVisibility();
851
852 if (this.isOverlay && !this.visible)
853 OptionsPage.updateScrollPosition_();
854 },
855
856 /**
857 * The nesting level of this page.
858 * @type {number} The nesting level of this page (0 for top-level page)
859 */
860 get nestingLevel() {
861 var level = 0;
862 var parent = this.parentPage;
863 while (parent) {
864 level++;
865 parent = parent.parentPage;
866 }
867 return level;
868 },
869
870 /**
871 * Whether the page is considered 'sticky', such that it will
872 * remain a top-level page even if sub-pages change.
873 * @type {boolean} True if this page is sticky.
874 */
875 get sticky() {
876 return false;
877 },
878
879 /**
880 * Checks whether this page is an ancestor of the given page in terms of
881 * subpage nesting.
882 * @param {OptionsPage} page The potential descendent of this page.
883 * @return {boolean} True if |page| is nested under this page.
884 */
885 isAncestorOfPage: function(page) {
886 var parent = page.parentPage;
887 while (parent) {
888 if (parent == this)
889 return true;
890 parent = parent.parentPage;
891 }
892 return false;
893 },
894
895 /**
896 * Whether it should be possible to show the page.
897 * @return {boolean} True if the page should be shown.
898 */
899 canShowPage: function() {
900 return true;
901 },
902 };
903
904 // Export
905 return {
906 OptionsPage: OptionsPage
907 };
908 });
OLDNEW
« no previous file with comments | « chrome/browser/resources/options2/options_page.css ('k') | chrome/browser/resources/options2/password_manager.css » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698