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

Side by Side Diff: ui/webui/resources/js/cr/ui/page_manager/page_manager.js

Issue 410293004: Split OptionsPage into Page and PageManager (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed feedback Created 6 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 2014 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('cr.ui.pageManager', function() {
6 /** @const */ var FocusOutlineManager = cr.ui.FocusOutlineManager;
7
8 /**
9 * PageManager contains a list of root Page and overlay Page objects and
10 * handles "navigation" by showing and hiding these pages and overlays. On
11 * initial load, PageManager can use the path to open the correct hierarchy
12 * of pages and overlay(s). Handlers for user events, like pressing buttons,
13 * can call into PageManager to open a particular overlay or cancel an
14 * existing overlay.
15 */
16 var PageManager = {
17 /**
18 * True if page is served from a dialog.
19 * @type {boolean}
20 */
21 isDialog: false,
22
23 /**
24 * Offset of page container in pixels, to allow room for side menu.
25 * Simplified settings pages can override this if they don't use the menu.
26 * The default (155) comes from -webkit-margin-start in uber_shared.css
27 * TODO(michaelpg): Remove dependency on uber menu (crbug.com/313244).
28 * @type {number}
29 */
30 horizontalOffset: 155,
31
32 /**
33 * Root pages. Maps lower-case page names to the respective page object.
34 * @type {Array.Page}
Dan Beam 2014/07/30 01:00:02 !Object.<!Page> (you don't need to get crazy with
michaelpg 2014/07/30 21:42:20 or this? !Object.<string, !Page>
Dan Beam 2014/07/31 02:21:32 there are no other keys than string in JS
michaelpg 2014/07/31 03:21:16 Still looks like the correct syntax for closure. h
Dan Beam 2014/07/31 03:30:37 both work and both are ignored, annoyingly enough
35 */
36 registeredPages: {},
37
38 /**
39 * Pages which are meant to behave like modal dialogs. Maps lower-case
40 * overlay names to the respective overlay object.
41 * @type {Array.Page}
42 * @private
43 */
44 registeredOverlayPages: {},
45
46 /**
47 * Initializes the complete page.
48 * @param {Page} defaultPage The page to be shown when no page is specified
Dan Beam 2014/07/30 01:00:02 cr.ui.page_manager.Page or Page? (be consistent ei
michaelpg 2014/07/30 21:42:20 Done. (fully qualified just to help show that Page
49 * in the path.
50 */
51 initialize: function(defaultPage) {
52 this.defaultPage_ = defaultPage;
53
54 FocusOutlineManager.forDocument(document);
55 document.addEventListener('scroll', this.handleScroll_.bind(this));
56
57 // Trigger the scroll handler manually to set the initial state.
58 this.handleScroll_();
59
60 // Shake the dialog if the user clicks outside the dialog bounds.
61 var containers = [$('overlay-container-1'), $('overlay-container-2')];
62 for (var i = 0; i < containers.length; i++) {
63 var overlay = containers[i];
64 cr.ui.overlay.setupOverlay(overlay);
65 overlay.addEventListener('cancelOverlay',
66 this.cancelOverlay.bind(this));
67 }
68
69 cr.ui.overlay.globalInitialization();
70 },
71
72 /**
73 * Registers new page.
74 * @param {cr.ui.page_manager.Page} page Page to register.
75 */
76 register: function(page) {
77 this.registeredPages[page.name.toLowerCase()] = page;
78 page.initializePage();
79 },
80
81 /**
82 * Registers a new Overlay page.
83 * @param {cr.ui.page_manager.Page} overlay Overlay to register.
84 * @param {cr.ui.page_manager.Page} parentPage Associated parent page for
85 * this overlay.
86 * @param {Array} associatedControls Array of control elements associated
87 * with this page.
88 */
89 registerOverlay: function(overlay,
90 parentPage,
91 associatedControls) {
92 this.registeredOverlayPages[overlay.name.toLowerCase()] = overlay;
93 overlay.parentPage = parentPage;
94 if (associatedControls) {
95 overlay.associatedControls = associatedControls;
96 if (associatedControls.length) {
97 overlay.associatedSection =
98 this.findSectionForNode_(associatedControls[0]);
99 }
100
101 // Sanity check.
102 for (var i = 0; i < associatedControls.length; ++i) {
103 assert(associatedControls[i], 'Invalid element passed.');
104 }
105 }
106
107 overlay.tab = undefined;
108 overlay.isOverlay = true;
109
110 // Reverse the button strip for Windows and CrOS. See the documentation of
111 // Page.reverseButtonStripIfNecessary() for an explanation of why this is
112 // done.
113 if (cr.isWindows || cr.isChromeOS)
114 overlay.reverseButtonStripIfNecessary();
115
116 overlay.initializePage();
117 },
118
119 /**
120 * Shows the default page.
121 * @param {boolean=} opt_updateHistory If we should update the history after
122 * showing the page (defaults to true).
123 */
124 showDefaultPage: function(opt_updateHistory) {
125 assert(this.defaultPage_ instanceof cr.ui.pageManager.Page,
126 'PageManager must be initialized with a default page.');
127 this.showPageByName(this.defaultPage_.name, opt_updateHistory);
128 },
129
130 /**
131 * Shows a registered page. This handles both root and overlay pages.
132 * @param {string} pageName Page name.
133 * @param {boolean=} opt_updateHistory If we should update the history after
134 * showing the page (defaults to true).
135 * @param {Object=} opt_propertyBag An optional bag of properties including
136 * replaceState (if history state should be replaced instead of pushed).
137 */
138 showPageByName: function(pageName,
139 opt_updateHistory,
140 opt_propertyBag) {
141 opt_updateHistory = opt_updateHistory !== false;
Dan Beam 2014/07/30 01:00:02 it's weird to have an undefined argument end up be
michaelpg 2014/07/30 21:42:20 I think it's fair to have a "default" value of tru
142 opt_propertyBag = opt_propertyBag || {};
143
144 // If a bubble is currently being shown, hide it.
145 this.hideBubble();
146
147 // Find the currently visible root-level page.
148 var rootPage = null;
149 for (var name in this.registeredPages) {
150 var page = this.registeredPages[name];
151 if (page.visible && !page.parentPage) {
152 rootPage = page;
153 break;
154 }
155 }
156
157 // Find the target page.
158 var targetPage = this.registeredPages[pageName.toLowerCase()];
159 if (!targetPage || !targetPage.canShowPage()) {
160 // If it's not a page, try it as an overlay.
161 if (!targetPage && this.showOverlay_(pageName, rootPage)) {
162 if (opt_updateHistory)
163 this.updateHistoryState_(!!opt_propertyBag.replaceState);
164 this.updateTitle_();
165 return;
166 }
167 targetPage = this.defaultPage_;
168 }
169
170 pageName = targetPage.name.toLowerCase();
171 var targetPageWasVisible = targetPage.visible;
172
173 // Determine if the root page is 'sticky', meaning that it
174 // shouldn't change when showing an overlay. This can happen for special
175 // pages like Search.
176 var isRootPageLocked =
177 rootPage && rootPage.sticky && targetPage.parentPage;
178
179 var allPageNames = Array.prototype.concat.call(
180 Object.keys(this.registeredPages),
181 Object.keys(this.registeredOverlayPages));
182
183 // Notify pages if they will be hidden.
184 // TODO(michaelpg): Resolve code duplication.
Dan Beam 2014/07/30 01:00:02 seems unlikely that you'll be able to do this if s
michaelpg 2014/07/30 21:42:20 Yeah, but we can at least make a helper function t
185 for (var i = 0; i < allPageNames.length; ++i) {
186 var name = allPageNames[i];
187 var page = this.registeredPages[name] ||
188 this.registeredOverlayPages[name];
189 if (!page.parentPage && isRootPageLocked)
190 continue;
191 if (page.willHidePage && name != pageName &&
192 !this.isAncestorOfPage(page, targetPage)) {
193 page.willHidePage();
194 }
195 }
196
197 // Update visibilities to show only the hierarchy of the target page.
198 for (var i = 0; i < allPageNames.length; ++i) {
199 var name = allPageNames[i];
200 var page = this.registeredPages[name] ||
201 this.registeredOverlayPages[name];
202 if (!page.parentPage && isRootPageLocked)
203 continue;
204 page.visible = name == pageName ||
205 this.isAncestorOfPage(page, targetPage);
206 }
207
208 // Update the history and current location.
209 if (opt_updateHistory)
210 this.updateHistoryState_(!!opt_propertyBag.replaceState);
211
212 // Update focus if any other control was focused on the previous page,
213 // or the previous page is not known.
214 if (document.activeElement != document.body &&
215 (!rootPage || rootPage.pageDiv.contains(document.activeElement))) {
216 targetPage.focus();
217 }
218
219 // Notify pages if they were shown.
220 for (var i = 0; i < allPageNames.length; ++i) {
221 var name = allPageNames[i];
222 var page = this.registeredPages[name] ||
223 this.registeredOverlayPages[name];
224 if (!page.parentPage && isRootPageLocked)
225 continue;
226 if (!targetPageWasVisible && page.didShowPage &&
227 (name == pageName || this.isAncestorOfPage(page, targetPage))) {
228 page.didShowPage();
229 }
230 }
231
232 // Update the document title. Do this after didShowPage was called, in
233 // case a page decides to change its title.
234 this.updateTitle_();
235 },
236
237 /**
238 * Returns the name of the page from the current path.
239 * @return {string} Name of the page specified by the current path.
240 */
241 getPageNameFromPath: function() {
242 var path = location.pathname;
243 if (path.length <= 1)
244 return this.defaultPage_.name;
245
246 // Skip starting slash and remove trailing slash (if any).
247 return path.slice(1).replace(/\/$/, '');
248 },
249
250 /**
251 * Gets the level of the page. The root page (e.g., BrowserOptions) has
252 * level 0.
Dan Beam 2014/07/30 01:00:02 don't 4 \s indent doc continuations (but you /do/
michaelpg 2014/07/30 21:42:20 Done.
253 * @return {number} How far down this page is from the root page.
254 */
255 getNestingLevel: function(page) {
256 if (typeof page.nestingLevel != "undefined")
257 return page.nestingLevel;
258
259 var level = 0;
260 var parent = page.parentPage;
261 while (parent) {
262 level++;
263 parent = parent.parentPage;
264 }
265 return level;
266 },
267
268 /**
269 * Checks whether one page is an ancestor of the other page in terms of
270 * subpage nesting.
271 * @param {Page} potential_ancestor The potential ancestor.
272 * @param {Page} potential_descendent The potential descendent.
273 * @return {boolean} True if |potential_descendent| is nested under
274 * |potential_descendent|.
275 */
276 isAncestorOfPage: function(potential_ancestor, potential_descendent) {
Dan Beam 2014/07/30 01:00:02 nit: jsVarsLikeThis
michaelpg 2014/07/30 21:42:20 nitDone = true;
277 var parent = potential_descendent.parentPage;
278 while (parent) {
279 if (parent == potential_ancestor)
280 return true;
281 parent = parent.parentPage;
282 }
283 return false;
284 },
285
286 /**
287 * Called when an page is shown or hidden to update the root page
288 * based on the page's new visibility.
289 * @param {Page} page The page being made visible or invisible.
290 */
291 onPageVisibilityChanged: function(page) {
292 this.updateRootPageFreezeState();
293
294 if (page.isOverlay && !page.visible)
295 this.updateScrollPosition_();
296 },
297
298 /**
299 * Returns the topmost visible page, or null if no page is visible.
300 * @return {Page} The topmost visible page.
301 */
302 getTopmostVisiblePage: function() {
303 // Check overlays first since they're top-most if visible.
304 return this.getVisibleOverlay_() ||
305 this.getTopmostVisibleNonOverlayPage_();
306 },
307
308 /**
309 * Closes the visible overlay. Updates the history state after closing the
310 * overlay.
311 */
312 closeOverlay: function() {
313 var overlay = this.getVisibleOverlay_();
314 if (!overlay)
315 return;
316
317 overlay.visible = false;
318
319 if (overlay.didClosePage)
320 overlay.didClosePage();
321 this.updateHistoryState_(false, {ignoreHash: true});
322 this.updateTitle_();
323
324 this.restoreLastFocusedElement_();
325 },
326
327 /**
328 * Closes all overlays and updates the history after each closed overlay.
329 */
330 closeAllOverlays: function() {
331 while (this.isOverlayVisible_()) {
332 this.closeOverlay();
333 }
334 },
335
336 /**
337 * Cancels (closes) the overlay, due to the user pressing <Esc>.
338 */
339 cancelOverlay: function() {
340 // Blur the active element to ensure any changed pref value is saved.
341 document.activeElement.blur();
342 var overlay = this.getVisibleOverlay_();
343 if (!overlay)
344 return;
345 // Let the overlay handle the <Esc> if it wants to.
346 if (overlay.handleCancel) {
347 overlay.handleCancel();
348 this.restoreLastFocusedElement_();
349 } else {
350 this.closeOverlay();
351 }
352 },
353
354 /**
355 * Shows an informational bubble displaying |content| and pointing at the
356 * |target| element. If |content| has focusable elements, they join the
357 * current page's tab order as siblings of |domSibling|.
358 * @param {HTMLDivElement} content The content of the bubble.
359 * @param {HTMLElement} target The element at which the bubble points.
360 * @param {HTMLElement} domSibling The element after which the bubble is
361 * added to the DOM.
362 * @param {cr.ui.ArrowLocation} location The arrow location.
363 */
364 showBubble: function(content, target, domSibling, location) {
365 this.hideBubble();
366
367 var bubble = new cr.ui.AutoCloseBubble;
368 bubble.anchorNode = target;
369 bubble.domSibling = domSibling;
370 bubble.arrowLocation = location;
371 bubble.content = content;
372 bubble.show();
373 this.bubble_ = bubble;
374 },
375
376 /**
377 * Hides the currently visible bubble, if any.
378 */
379 hideBubble: function() {
380 if (this.bubble_)
381 this.bubble_.hide();
382 },
383
384 /**
385 * Returns the currently visible bubble, or null if no bubble is visible.
386 * @return {AutoCloseBubble} The bubble currently being shown.
387 */
388 getVisibleBubble: function() {
389 var bubble = this.bubble_;
390 return bubble && !bubble.hidden ? bubble : null;
391 },
392
393 /**
394 * Callback for window.onpopstate to handle back/forward navigations.
395 * @param {string} pageName The current page name.
396 * @param {Object} data State data pushed into history.
397 */
398 setState: function(pageName, data) {
399 var currentOverlay = this.getVisibleOverlay_();
400 var lowercaseName = pageName.toLowerCase();
401 var newPage = this.registeredPages[lowercaseName] ||
402 this.registeredOverlayPages[lowercaseName] ||
403 this.defaultPage_;
404 if (currentOverlay && !this.isAncestorOfPage(currentOverlay, newPage)) {
405 currentOverlay.visible = false;
406 if (currentOverlay.didClosePage) currentOverlay.didClosePage();
407 }
408 this.showPageByName(pageName, false);
409 },
410
411
412 /**
413 * Whether the page is still loading (i.e. onload hasn't finished running).
414 * @return {boolean} Whether the page is still loading.
415 */
416 isLoading: function() {
417 return document.documentElement.classList.contains('loading');
418 },
419
420 /**
421 * Callback for window.onbeforeunload. Used to notify overlays that they
422 * will be closed.
423 */
424 willClose: function() {
425 var overlay = this.getVisibleOverlay_();
426 if (overlay && overlay.didClosePage)
427 overlay.didClosePage();
428 },
429
430 /**
431 * Freezes/unfreezes the scroll position of the root page based on the
432 * current page stack.
433 */
434 updateRootPageFreezeState: function() {
435 var topPage = this.getTopmostVisiblePage();
436 if (topPage)
437 this.setRootPageFrozen_(topPage.isOverlay);
438 },
439
440 /**
441 * Change the horizontal offset used to reposition elements while showing an
442 * overlay from the default.
443 */
444 setHorizontalOffset: function(value) {
445 this.horizontalOffset = value;
Dan Beam 2014/07/30 01:00:02 nit: i'd make a private horizontalOffset_ and pote
michaelpg 2014/07/30 21:42:20 I'll keep this as is for now: we'll probably want
446 },
447
448 /**
449 * Shows a registered overlay page. Does not update history.
450 * @param {string} overlayName Page name.
451 * @param {Page} rootPage The currently visible root-level page.
452 * @return {boolean} Whether we showed an overlay.
453 * @private
454 */
455 showOverlay_: function(overlayName, rootPage) {
456 var overlay = this.registeredOverlayPages[overlayName.toLowerCase()];
457 if (!overlay || !overlay.canShowPage())
458 return false;
459
460 // Save the currently focused element in the page for restoration later.
461 var currentPage = this.getTopmostVisiblePage();
462 if (currentPage)
463 currentPage.lastFocusedElement = document.activeElement;
464
465 if ((!rootPage || !rootPage.sticky) &&
466 overlay.parentPage &&
467 !overlay.parentPage.visible) {
468 this.showPageByName(overlay.parentPage.name, false);
469 }
470
471 if (!overlay.visible) {
472 overlay.visible = true;
473 if (overlay.didShowPage)
474 overlay.didShowPage();
475 }
476
477 // Change focus to the overlay if any other control was focused by
478 // keyboard before. Otherwise, no one should have focus.
479 if (document.activeElement != document.body) {
480 if (FocusOutlineManager.forDocument(document).visible) {
481 overlay.focus();
482 } else if (!overlay.pageDiv.contains(document.activeElement)) {
483 document.activeElement.blur();
484 }
485 }
486
487 if ($('search-field') && $('search-field').value == '') {
488 var section = overlay.associatedSection;
489 if (section)
490 options.BrowserOptions.scrollToSection(section);
491 }
492
493 return true;
494 },
495
496 /**
497 * Returns whether or not an overlay is visible.
498 * @return {boolean} True if an overlay is visible.
499 * @private
500 */
501 isOverlayVisible_: function() {
502 return this.getVisibleOverlay_() != null;
503 },
504
505 /**
506 * Returns the currently visible overlay, or null if no page is visible.
507 * @return {Page} The visible overlay.
508 * @private
509 */
510 getVisibleOverlay_: function() {
511 var topmostPage = null;
512 for (var name in this.registeredOverlayPages) {
513 var page = this.registeredOverlayPages[name];
514 if (page.visible &&
515 (!topmostPage || this.getNestingLevel(page) >
516 this.getNestingLevel(topmostPage))) {
517 topmostPage = page;
518 }
519 }
520 return topmostPage;
521 },
522
523 /**
524 * Hides the visible overlay. Does not affect the history state.
525 * @private
526 */
527 hideOverlay_: function() {
528 var overlay = this.getVisibleOverlay_();
529 if (overlay)
530 overlay.visible = false;
531 },
532
533 /**
534 * Returns the pages which are currently visible, ordered by nesting level
535 * (ascending).
536 * @return {Array.Page} The pages which are currently visible, ordered
537 * by nesting level (ascending).
538 * @private
539 */
540 getVisiblePages_: function() {
541 var visiblePages = [];
542 for (var name in this.registeredPages) {
543 var page = this.registeredPages[name];
544 if (page.visible)
545 visiblePages[this.getNestingLevel(page)] = page;
546 }
547 return visiblePages;
548 },
549
550 /**
551 * Returns the topmost visible page (overlays excluded).
552 * @return {Page} The topmost visible page aside from any overlays.
553 * @private
554 */
555 getTopmostVisibleNonOverlayPage_: function() {
556 var topPage = null;
557 for (var name in this.registeredPages) {
558 var page = this.registeredPages[name];
559 if (page.visible &&
560 (!topPage || this.getNestingLevel(page) >
561 this.getNestingLevel(topPage))) {
562 topPage = page;
563 }
564 }
565
566 return topPage;
567 },
568
569 /**
570 * Scrolls the page to the correct position (the top when opening an
571 * overlay, or the old scroll position a previously hidden overlay
572 * becomes visible).
573 * @private
574 */
575 updateScrollPosition_: function() {
576 var container = $('page-container');
577 var scrollTop = container.oldScrollTop || 0;
578 container.oldScrollTop = undefined;
579 window.scroll(scrollLeftForDocument(document), scrollTop);
580 },
581
582 /**
583 * Updates the title to title of the current page.
584 * @private
585 */
586 updateTitle_: function() {
587 var page = this.getTopmostVisiblePage();
588 // TODO(michaelpg): Remove dependency on uber (crbug.com/313244).
589 uber.setTitle(page.title);
590 },
591
592 /**
593 * Pushes the current page onto the history stack, replacing the current
594 * entry if appropriate.
595 * @param {boolean} replace If true, allow no history events to be created.
596 * @param {object=} opt_params A bag of optional params, including:
597 * {boolean} ignoreHash Whether to include the hash or not.
598 * @private
599 */
600 updateHistoryState_: function(replace, opt_params) {
601 if (this.isDialog)
602 return;
603
604 var page = this.getTopmostVisiblePage();
605 var path = window.location.pathname + window.location.hash;
606 if (path) {
607 // Remove trailing slash.
608 path = path.slice(1).replace(/\/(?:#|$)/, '');
609 }
610
611 // If the page is already in history (the user may have clicked the same
612 // link twice, or this is the initial load), do nothing.
613 var hash = opt_params && opt_params.ignoreHash ?
614 '' : window.location.hash;
615 var newPath = (page == this.defaultPage_ ? '' : page.name) + hash;
616 if (path == newPath)
617 return;
618
619 // TODO(michaelpg): Remove dependency on uber (crbug.com/313244).
620 var historyFunction = replace ? uber.replaceState : uber.pushState;
621 historyFunction.call(uber, {}, newPath);
622 },
623
624 /**
625 * Restores the last focused element on a given page.
626 * @private
627 */
628 restoreLastFocusedElement_: function() {
629 var currentPage = this.getTopmostVisiblePage();
630 if (currentPage.lastFocusedElement)
631 currentPage.lastFocusedElement.focus();
632 },
633
634 /**
635 * Find an enclosing section for an element if it exists.
636 * @param {Element} element Element to search.
637 * @return {Element} The section element, or null.
638 * @private
639 */
640 findSectionForNode_: function(node) {
641 while (node = node.parentNode) {
642 if (node.nodeName == 'SECTION')
643 return node;
644 }
645 return null;
646 },
647
648 /**
649 * Freezes/unfreezes the scroll position of the root page container.
650 * @param {boolean} freeze Whether the page should be frozen.
651 * @private
652 */
653 setRootPageFrozen_: function(freeze) {
654 var container = $('page-container');
655 if (container.classList.contains('frozen') == freeze)
656 return;
657
658 if (freeze) {
659 // Lock the width, since auto width computation may change.
660 container.style.width = window.getComputedStyle(container).width;
661 container.oldScrollTop = scrollTopForDocument(document);
662 container.classList.add('frozen');
663 var verticalPosition =
664 container.getBoundingClientRect().top - container.oldScrollTop;
665 container.style.top = verticalPosition + 'px';
666 this.updateFrozenElementHorizontalPosition_(container);
667 } else {
668 container.classList.remove('frozen');
669 container.style.top = '';
670 container.style.left = '';
671 container.style.right = '';
672 container.style.width = '';
673 }
674 },
675
676 /**
677 * Does a bounds check for the element on the given x, y client coordinates.
678 * @param {Element} e The DOM element.
679 * @param {number} x The client X to check.
680 * @param {number} y The client Y to check.
681 * @return {boolean} True if the point falls within the element's bounds.
682 * @private
683 */
684 elementContainsPoint_: function(e, x, y) {
685 var clientRect = e.getBoundingClientRect();
686 return x >= clientRect.left && x <= clientRect.right &&
687 y >= clientRect.top && y <= clientRect.bottom;
688 },
689
690 /**
691 * Called when the page is scrolled; moves elements that are position:fixed
692 * but should only behave as if they are fixed for vertical scrolling.
693 * @private
694 */
695 handleScroll_: function() {
696 this.updateAllFrozenElementPositions_();
697 },
698
699 /**
700 * Updates all frozen pages to match the horizontal scroll position.
701 * @private
702 */
703 updateAllFrozenElementPositions_: function() {
704 var frozenElements = document.querySelectorAll('.frozen');
705 for (var i = 0; i < frozenElements.length; i++)
706 this.updateFrozenElementHorizontalPosition_(frozenElements[i]);
707 },
708
709 /**
710 * Updates the given frozen element to match the horizontal scroll position.
711 * @param {HTMLElement} e The frozen element to update.
712 * @private
713 */
714 updateFrozenElementHorizontalPosition_: function(e) {
715 if (isRTL()) {
716 e.style.right = this.horizontalOffset + 'px';
717 } else {
718 var scrollLeft = scrollLeftForDocument(document);
719 e.style.left = this.horizontalOffset - scrollLeft + 'px';
720 }
721 },
722 };
723
724 // Export
725 return {
726 PageManager: PageManager
727 };
728 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698