OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 cr.define('uber', function() { | 5 cr.define('uber', function() { |
6 | 6 |
7 /** | 7 /** |
8 * Handles page initialization. | 8 * Handles page initialization. |
9 */ | 9 */ |
10 function onLoad() { | 10 function onLoad() { |
11 var navigationItems = document.querySelectorAll('#navigation li'); | |
12 var iframes = document.querySelectorAll('.iframe-container'); | |
13 | |
14 for (var i = 0; i < navigationItems.length; ++i) { | |
15 var navItem = navigationItems[i]; | |
16 navItem.associatedIframe = iframes[i]; | |
17 iframes[i].associatedNavItem = navItem; | |
18 navItem.addEventListener('click', onNavItemClicked); | |
19 } | |
20 | |
21 // Update the URL if need be. | 11 // Update the URL if need be. |
22 if (window.location.pathname === '/') { | 12 if (window.location.pathname === '/') { |
23 var selectedNavItem = document.querySelector('#navigation li.selected'); | 13 var pageId = getSelectedIframe().id; |
24 var pageId = selectedNavItem.associatedIframe.id; | |
25 window.history.replaceState({pageId: pageId}, '', '/' + pageId); | 14 window.history.replaceState({pageId: pageId}, '', '/' + pageId); |
26 } | 15 } |
27 | 16 |
28 window.addEventListener('message', handleWindowMessage); | 17 window.addEventListener('message', handleWindowMessage); |
29 } | 18 } |
30 | 19 |
31 /** | 20 /** |
32 * Handles clicks on the navigation controls (switches the page and updates | |
33 * the URL). | |
34 * @param {Event} e The click event. | |
35 */ | |
36 function onNavItemClicked(e) { | |
37 if (selectPageForNavItem(e.currentTarget)) { | |
38 var pageId = e.currentTarget.associatedIframe.id; | |
39 window.history.pushState({pageId: pageId}, '', '/' + pageId); | |
40 } | |
41 } | |
42 | |
43 /** | |
44 * Selects the page for |navItem|, or does nothing if that item has already | 21 * Selects the page for |navItem|, or does nothing if that item has already |
45 * been selected. | 22 * been selected. |
46 * @param {Object} navItem The navigation control li. | 23 * @param {Object} navItem The navigation control li. |
47 * @return {boolean} True if the item became selected, false if it had already | 24 * @return {boolean} True if the item became selected, false if it had already |
48 * been selected. | 25 * been selected. |
49 */ | 26 */ |
50 function selectPageForNavItem(navItem) { | 27 function selectPageForNavItem(navItem) { |
51 // Note that |iframe| is the containing element of the underlying iframe, as | 28 // Note that |iframe| is the containing element of the underlying iframe, as |
52 // opposed to the iframe element itself. | 29 // opposed to the iframe element itself. |
53 var iframe = navItem.associatedIframe; | 30 var iframe = navItem.associatedIframe; |
54 var currentIframe = getSelectedIframe_(); | 31 var currentIframe = getSelectedIframe(); |
55 if (currentIframe == iframe) | 32 if (currentIframe == iframe) |
56 return false; | 33 return false; |
57 | 34 |
58 // Restore the cached title. | 35 // Restore the cached title. |
59 if (iframe.title) | 36 if (iframe.title) |
60 document.title = iframe.title; | 37 document.title = iframe.title; |
61 | 38 |
62 currentIframe.classList.remove('selected'); | 39 currentIframe.classList.remove('selected'); |
63 iframe.classList.add('selected'); | 40 iframe.classList.add('selected'); |
64 | 41 |
65 var currentNavItem = document.querySelector('li.selected'); | 42 var currentNavItem = document.querySelector('li.selected'); |
66 currentNavItem.classList.remove('selected'); | 43 currentNavItem.classList.remove('selected'); |
67 navItem.classList.add('selected'); | 44 navItem.classList.add('selected'); |
68 | 45 |
69 return true; | 46 return true; |
70 } | 47 } |
71 | 48 |
72 /** | 49 /** |
73 * Handler for window.onpopstate. | 50 * Handler for window.onpopstate. |
74 * @param {Event} e The history event. | 51 * @param {Event} e The history event. |
75 */ | 52 */ |
76 function onPopHistoryState(e) { | 53 function onPopHistoryState(e) { |
77 if (e.state && e.state.pageId) | 54 if (e.state && e.state.pageId) |
78 selectPageForNavItem($(e.state.pageId).associatedNavItem); | 55 showPage(e.state.pageId); |
79 } | 56 } |
80 | 57 |
81 /** | 58 /** |
82 * @return {Object} The currently selected iframe container. | 59 * @return {Object} The currently selected iframe container. |
83 * @private | |
84 */ | 60 */ |
85 function getSelectedIframe_() { | 61 function getSelectedIframe() { |
86 return document.querySelector('.iframe-container.selected'); | 62 return document.querySelector('.iframe-container.selected'); |
87 } | 63 } |
88 | 64 |
89 /** | 65 /** |
90 * Handles postMessage calls from the iframes of the contained pages. | 66 * Handles postMessage calls from the iframes of the contained pages. |
91 * | 67 * |
92 * The pages request functionality from this object by passing an object of | 68 * The pages request functionality from this object by passing an object of |
93 * the following form: | 69 * the following form: |
94 * | 70 * |
95 * { method : "methodToInvoke", | 71 * { method : "methodToInvoke", |
96 * params : {...} | 72 * params : {...} |
97 * } | 73 * } |
98 * | 74 * |
99 * |method| is required, while |params| is optional. Extra parameters required | 75 * |method| is required, while |params| is optional. Extra parameters required |
100 * by a method must be specified by that method's documentation. | 76 * by a method must be specified by that method's documentation. |
101 * | 77 * |
102 * @param {Event} e The posted object. | 78 * @param {Event} e The posted object. |
103 */ | 79 */ |
104 function handleWindowMessage(e) { | 80 function handleWindowMessage(e) { |
105 if (e.data.method === 'showOverlay') | 81 if (e.data.method === 'beginInterceptingEvents') |
106 showOverlay_(); | 82 backgroundNavigation(); |
107 else if (e.data.method === 'hideOverlay') | 83 else if (e.data.method === 'stopInterceptingEvents') |
108 hideOverlay_(); | 84 foregroundNavigation(); |
109 else if (e.data.method === 'setTitle') | 85 else if (e.data.method === 'setTitle') |
110 setTitle_(e.origin, e.data.params); | 86 setTitle_(e.origin, e.data.params); |
| 87 else if (e.data.method === 'showPage') |
| 88 showPage(e.data.params.pageId); |
| 89 else if (e.data.method === 'navigationControlsLoaded') |
| 90 onNavigationControlsLoaded(); |
111 else | 91 else |
112 console.error('Received unexpected message: ' + e.data); | 92 console.error('Received unexpected message: ' + e.data); |
113 } | 93 } |
114 | 94 |
115 /** | 95 /** |
116 * @private | 96 * Sends the navigation iframe to the background. |
117 */ | 97 */ |
118 function showOverlay_() { | 98 function backgroundNavigation() { |
119 document.querySelector('.overlay').classList.add('showing'); | 99 $('navigation').classList.add('background'); |
120 } | 100 } |
121 | 101 |
122 /** | 102 /** |
123 * @private | 103 * Retrieves the navigation iframe from the background. |
124 */ | 104 */ |
125 function hideOverlay_() { | 105 function foregroundNavigation() { |
126 document.querySelector('.overlay').classList.remove('showing'); | 106 $('navigation').classList.remove('background'); |
127 } | 107 } |
128 | 108 |
129 /** | 109 /** |
130 * Sets the title of the page. | 110 * Sets the title of the page. |
131 * @param {Object} origin The origin of the source iframe. | 111 * @param {Object} origin The origin of the source iframe. |
132 * @param {Object} params Must contain a |title| property. | 112 * @param {Object} params Must contain a |title| property. |
133 * @private | |
134 */ | 113 */ |
135 function setTitle_(origin, params) { | 114 function setTitle_(origin, params) { |
136 // |iframe.src| always contains a trailing backslash while |origin| does not | 115 // |iframe.src| always contains a trailing backslash while |origin| does not |
137 // so add the trailing source for normalization. | 116 // so add the trailing source for normalization. |
138 var query = '.iframe-container > iframe[src="' + origin + '/"]'; | 117 var query = '.iframe-container > iframe[src="' + origin + '/"]'; |
139 | 118 |
140 // Cache the title for the client iframe, i.e., the iframe setting the | 119 // Cache the title for the client iframe, i.e., the iframe setting the |
141 // title. querySelector returns the actual iframe element, so use parentNode | 120 // title. querySelector returns the actual iframe element, so use parentNode |
142 // to get back to the container. | 121 // to get back to the container. |
143 var container = document.querySelector(query).parentNode; | 122 var container = document.querySelector(query).parentNode; |
144 container.title = params.title; | 123 container.title = params.title; |
145 | 124 |
146 // Only update the currently displayed title if this is the visible frame. | 125 // Only update the currently displayed title if this is the visible frame. |
147 if (container == getSelectedIframe_()) | 126 if (container == getSelectedIframe()) |
148 document.title = params.title; | 127 document.title = params.title; |
149 } | 128 } |
150 | 129 |
| 130 /** |
| 131 * Selects a subpage. This is called from uber-frame. |
| 132 * @param {String} pageId Should matche an id of one of the iframe containers. |
| 133 */ |
| 134 function showPage(pageId) { |
| 135 var container = $(pageId); |
| 136 var lastSelected = document.querySelector('.iframe-container.selected'); |
| 137 if (lastSelected === container) |
| 138 return; |
| 139 |
| 140 lastSelected.classList.remove('selected'); |
| 141 container.classList.add('selected'); |
| 142 document.title = container.title; |
| 143 |
| 144 window.history.pushState({pageId: pageId}, '', '/' + pageId); |
| 145 updateNavigationControls(); |
| 146 } |
| 147 |
| 148 function onNavigationControlsLoaded() { |
| 149 updateNavigationControls(); |
| 150 } |
| 151 |
| 152 /** |
| 153 * Sends a message to uber-frame to update the appearance of the nav controls. |
| 154 * It should be called whenever the selected iframe changes. |
| 155 */ |
| 156 function updateNavigationControls() { |
| 157 var iframe = getSelectedIframe(); |
| 158 uber.invokeMethodOnWindow($('navigation').firstChild.contentWindow, |
| 159 'changeSelection', {pageId: iframe.id}); |
| 160 } |
| 161 |
151 return { | 162 return { |
152 onLoad: onLoad, | 163 onLoad: onLoad, |
153 onPopHistoryState: onPopHistoryState | 164 onPopHistoryState: onPopHistoryState |
154 }; | 165 }; |
155 | 166 |
156 }); | 167 }); |
157 | 168 |
158 window.addEventListener('popstate', uber.onPopHistoryState); | 169 window.addEventListener('popstate', uber.onPopHistoryState); |
159 document.addEventListener('DOMContentLoaded', uber.onLoad); | 170 document.addEventListener('DOMContentLoaded', uber.onLoad); |
OLD | NEW |