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 /** | 5 /** |
6 * @fileoverview New tab page | 6 * @fileoverview New tab page |
7 * This is the main code for the new tab page used by touch-enabled Chrome | 7 * This is the main code for the new tab page used by touch-enabled Chrome |
8 * browsers. For now this is still a prototype. | 8 * browsers. For now this is still a prototype. |
9 */ | 9 */ |
10 | 10 |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
130 loginBubble.setArrowLocation(cr.ui.ArrowLocation.TOP_END); | 130 loginBubble.setArrowLocation(cr.ui.ArrowLocation.TOP_END); |
131 loginBubble.bubbleAlignment = | 131 loginBubble.bubbleAlignment = |
132 cr.ui.BubbleAlignment.BUBBLE_EDGE_TO_ANCHOR_EDGE; | 132 cr.ui.BubbleAlignment.BUBBLE_EDGE_TO_ANCHOR_EDGE; |
133 loginBubble.deactivateToDismissDelay = 2000; | 133 loginBubble.deactivateToDismissDelay = 2000; |
134 loginBubble.setCloseButtonVisible(false); | 134 loginBubble.setCloseButtonVisible(false); |
135 | 135 |
136 $('login-status-learn-more').href = | 136 $('login-status-learn-more').href = |
137 localStrings.getString('login_status_url'); | 137 localStrings.getString('login_status_url'); |
138 $('login-status-advanced').onclick = function() { | 138 $('login-status-advanced').onclick = function() { |
139 chrome.send('showAdvancedLoginUI'); | 139 chrome.send('showAdvancedLoginUI'); |
140 } | 140 }; |
141 $('login-status-dismiss').onclick = loginBubble.hide.bind(loginBubble); | 141 $('login-status-dismiss').onclick = loginBubble.hide.bind(loginBubble); |
142 | 142 |
143 var bubbleContent = $('login-status-bubble-contents'); | 143 var bubbleContent = $('login-status-bubble-contents'); |
144 loginBubble.content = bubbleContent; | 144 loginBubble.content = bubbleContent; |
145 | 145 |
146 // The anchor node won't be updated until updateLogin is called so don't | 146 // The anchor node won't be updated until updateLogin is called so don't |
147 // show the bubble yet. | 147 // show the bubble yet. |
148 shouldShowLoginBubble = true; | 148 shouldShowLoginBubble = true; |
149 } else if (localStrings.getString('ntp4_intro_message')) { | 149 } else if (localStrings.getString('ntp4_intro_message')) { |
150 infoBubble = new cr.ui.Bubble; | 150 infoBubble = new cr.ui.Bubble; |
151 infoBubble.anchorNode = newTabView.mostVisitedPage.navigationDot; | 151 infoBubble.anchorNode = newTabView.mostVisitedPage.navigationDot; |
152 infoBubble.setArrowLocation(cr.ui.ArrowLocation.BOTTOM_START); | 152 infoBubble.setArrowLocation(cr.ui.ArrowLocation.BOTTOM_START); |
153 infoBubble.handleCloseEvent = function() { | 153 infoBubble.handleCloseEvent = function() { |
154 this.hide(); | 154 this.hide(); |
155 chrome.send('introMessageDismissed'); | 155 chrome.send('introMessageDismissed'); |
156 } | 156 }; |
157 | 157 |
158 var bubbleContent = $('ntp4-intro-bubble-contents'); | 158 var bubbleContent = $('ntp4-intro-bubble-contents'); |
159 infoBubble.content = bubbleContent; | 159 infoBubble.content = bubbleContent; |
160 | 160 |
161 var learnMoreLink = infoBubble.querySelector('a'); | 161 var learnMoreLink = infoBubble.querySelector('a'); |
162 learnMoreLink.href = localStrings.getString('ntp4_intro_url'); | 162 learnMoreLink.href = localStrings.getString('ntp4_intro_url'); |
163 learnMoreLink.onclick = infoBubble.hide.bind(infoBubble); | 163 learnMoreLink.onclick = infoBubble.hide.bind(infoBubble); |
164 | 164 |
165 infoBubble.show(); | 165 infoBubble.show(); |
166 chrome.send('introMessageSeen'); | 166 chrome.send('introMessageSeen'); |
167 } | 167 } |
168 | 168 |
169 var serverpromo = localStrings.getString('serverpromo'); | 169 var promo = localStrings.getString('serverpromo'); |
170 if (serverpromo) { | 170 if (promo) { |
171 showNotification(parseHtmlSubset(serverpromo), [], function() { | 171 var tags = ['IMG']; |
172 var attrs = { | |
173 src: function(node, value) { | |
174 return node.tagName == 'IMG' && | |
175 /^data\:image\/(?:png|gif|jpe?g)/.test(value); | |
176 }, | |
177 }; | |
178 showNotification(parseHtmlSubset(promo, tags, attrs), [], function() { | |
172 chrome.send('closeNotificationPromo'); | 179 chrome.send('closeNotificationPromo'); |
173 }, 60000); | 180 }, 60000); |
174 chrome.send('notificationPromoViewed'); | 181 chrome.send('notificationPromoViewed'); |
175 } | 182 } |
176 | 183 |
177 var loginContainer = getRequiredElement('login-container'); | 184 var loginContainer = getRequiredElement('login-container'); |
178 loginContainer.addEventListener('click', function() { | 185 loginContainer.addEventListener('click', function() { |
179 var rect = loginContainer.getBoundingClientRect(); | 186 var rect = loginContainer.getBoundingClientRect(); |
180 chrome.send('showSyncLoginUI', | 187 chrome.send('showSyncLoginUI', |
181 [rect.left, rect.top, rect.width, rect.height]); | 188 [rect.left, rect.top, rect.width, rect.height]); |
182 }); | 189 }); |
183 chrome.send('initializeSyncLogin'); | 190 chrome.send('initializeSyncLogin'); |
184 } | 191 } |
185 | 192 |
186 /** | 193 /** |
187 * Launches the chrome web store app with the chrome-ntp-launcher | 194 * Launches the chrome web store app with the chrome-ntp-launcher |
188 * source. | 195 * source. |
189 * @param {Event} e The click event. | 196 * @param {Event} e The click event. |
190 */ | 197 */ |
191 function onChromeWebStoreButtonClick(e) { | 198 function onChromeWebStoreButtonClick(e) { |
192 chrome.send('recordAppLaunchByURL', | 199 chrome.send('recordAppLaunchByURL', |
193 [encodeURIComponent(this.href), | 200 [encodeURIComponent(this.href), |
194 ntp4.APP_LAUNCH.NTP_WEBSTORE_FOOTER]); | 201 ntp4.APP_LAUNCH.NTP_WEBSTORE_FOOTER]); |
195 } | 202 } |
196 | 203 |
204 /* | |
205 * The number of sections to wait on. | |
206 * @type {number} | |
207 */ | |
208 var sectionsToWaitFor = 2; | |
209 | |
210 /** | |
211 * Queued callbacks which lie in wait for all sections to be ready. | |
212 * @type {array} | |
213 */ | |
214 var readyCallbacks = []; | |
215 | |
216 /** | |
217 * Fired as each section of pages becomes ready. | |
218 * @param {Event} e Each page's synthetic DOM event. | |
219 */ | |
220 document.addEventListener('sectionready', function(e) { | |
221 if (--sectionsToWaitFor <= 0) { | |
222 while (readyCallbacks.length) | |
Evan Stade
2012/02/07 01:07:21
I think curlies here
Dan Beam
2012/02/07 01:42:27
Done. (why are for/while treated differently, thou
| |
223 readyCallbacks.shift()(); | |
224 } | |
225 }); | |
226 | |
227 /** | |
228 * This is used to simulate a fire-once event (i.e. $(document).ready() in | |
229 * jQuery or Y.on('domready') in YUI. If all sections are ready, the callback | |
230 * is fired right away. If all pages are not ready yet, the function is queued | |
231 * for later execution. | |
232 * @param {function} callback The work to be done when ready. | |
233 */ | |
234 function doWhenAllSectionsReady(callback) { | |
235 assert(typeof callback == 'function'); | |
236 if (sectionsToWaitFor > 0) | |
237 readyCallbacks.push(callback); | |
238 else | |
239 window.setTimeout(callback, 0); // Do soon after, but asynchronously. | |
240 } | |
241 | |
197 /** | 242 /** |
198 * Fills in an invisible div with the 'Most Visited' string so that | 243 * Fills in an invisible div with the 'Most Visited' string so that |
199 * its length may be measured and the nav dots sized accordingly. | 244 * its length may be measured and the nav dots sized accordingly. |
200 */ | 245 */ |
201 function measureNavDots() { | 246 function measureNavDots() { |
202 var measuringDiv = $('fontMeasuringDiv'); | 247 var measuringDiv = $('fontMeasuringDiv'); |
203 measuringDiv.textContent = localStrings.getString('mostvisited'); | 248 measuringDiv.textContent = localStrings.getString('mostvisited'); |
204 var pxWidth = Math.max(measuringDiv.clientWidth * 1.15, 80); | 249 var pxWidth = Math.max(measuringDiv.clientWidth * 1.15, 80); |
205 | 250 |
206 var styleElement = document.createElement('style'); | 251 var styleElement = document.createElement('style'); |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
242 attribution.hidden = false; | 287 attribution.hidden = false; |
243 } else { | 288 } else { |
244 attribution.hidden = true; | 289 attribution.hidden = true; |
245 } | 290 } |
246 } | 291 } |
247 | 292 |
248 /** | 293 /** |
249 * Timeout ID. | 294 * Timeout ID. |
250 * @type {number} | 295 * @type {number} |
251 */ | 296 */ |
252 var notificationTimeout_ = 0; | 297 var notificationTimeout = 0; |
253 | 298 |
254 /** | 299 /** |
255 * Shows the notification bubble. | 300 * Shows the notification bubble. |
256 * @param {string|Node} message The notification message or node to use as | 301 * @param {string|Node} message The notification message or node to use as |
257 * message. | 302 * message. |
258 * @param {Array.<{text: string, action: function()}>} links An array of | 303 * @param {Array.<{text: string, action: function()}>} links An array of |
259 * records describing the links in the notification. Each record should | 304 * records describing the links in the notification. Each record should |
260 * have a 'text' attribute (the display string) and an 'action' attribute | 305 * have a 'text' attribute (the display string) and an 'action' attribute |
261 * (a function to run when the link is activated). | 306 * (a function to run when the link is activated). |
262 * @param {Function} opt_closeHandler The callback invoked if the user | 307 * @param {Function} opt_closeHandler The callback invoked if the user |
263 * manually dismisses the notification. | 308 * manually dismisses the notification. |
264 */ | 309 */ |
265 function showNotification(message, links, opt_closeHandler, opt_timeout) { | 310 function showNotification(message, links, opt_closeHandler, opt_timeout) { |
266 window.clearTimeout(notificationTimeout_); | 311 window.clearTimeout(notificationTimeout); |
267 | 312 |
268 var span = document.querySelector('#notification > span'); | 313 var span = document.querySelector('#notification > span'); |
269 if (typeof message == 'string') { | 314 if (typeof message == 'string') { |
270 span.textContent = message; | 315 span.textContent = message; |
271 } else { | 316 } else { |
272 span.textContent = ''; // Remove all children. | 317 span.textContent = ''; // Remove all children. |
273 span.appendChild(message); | 318 span.appendChild(message); |
274 } | 319 } |
275 | 320 |
276 var linksBin = $('notificationLinks'); | 321 var linksBin = $('notificationLinks'); |
277 linksBin.textContent = ''; | 322 linksBin.textContent = ''; |
278 for (var i = 0; i < links.length; i++) { | 323 for (var i = 0; i < links.length; i++) { |
279 var link = linksBin.ownerDocument.createElement('div'); | 324 var link = linksBin.ownerDocument.createElement('div'); |
280 link.textContent = links[i].text; | 325 link.textContent = links[i].text; |
281 link.action = links[i].action; | 326 link.action = links[i].action; |
282 link.onclick = function() { | 327 link.onclick = function() { |
283 this.action(); | 328 this.action(); |
284 hideNotification(); | 329 hideNotification(); |
285 } | 330 }; |
286 link.setAttribute('role', 'button'); | 331 link.setAttribute('role', 'button'); |
287 link.setAttribute('tabindex', 0); | 332 link.setAttribute('tabindex', 0); |
288 link.className = 'link-button'; | 333 link.className = 'link-button'; |
289 linksBin.appendChild(link); | 334 linksBin.appendChild(link); |
290 } | 335 } |
291 | 336 |
292 document.querySelector('#notification button').onclick = function(e) { | 337 function closeFunc(e) { |
293 if (opt_closeHandler) | 338 if (opt_closeHandler) |
294 opt_closeHandler(); | 339 opt_closeHandler(); |
295 hideNotification(); | 340 hideNotification(); |
296 }; | 341 } |
342 | |
343 document.querySelector('#notification button').onclick = closeFunc; | |
344 document.addEventListener('dragstart', closeFunc); | |
345 | |
346 notificationContainer.hidden = false; | |
347 showNotificationOnCurrentPage(); | |
348 | |
349 newTabView.cardSlider.frame.addEventListener( | |
350 'cardSlider:card_change_ended', onCardChangeEnded); | |
297 | 351 |
298 var timeout = opt_timeout || 10000; | 352 var timeout = opt_timeout || 10000; |
299 notificationContainer.hidden = false; | 353 notificationTimeout = window.setTimeout(hideNotification, timeout); |
300 notificationContainer.classList.remove('inactive'); | |
301 notificationTimeout_ = window.setTimeout(hideNotification, timeout); | |
302 } | 354 } |
303 | 355 |
304 /** | 356 /** |
305 * Hide the notification bubble. | 357 * Hide the notification bubble. |
306 */ | 358 */ |
307 function hideNotification() { | 359 function hideNotification() { |
308 notificationContainer.classList.add('inactive'); | 360 notificationContainer.classList.add('inactive'); |
361 | |
362 newTabView.cardSlider.frame.removeEventListener( | |
363 'cardSlider:card_change_ended', onCardChangeEnded); | |
364 } | |
365 | |
366 /** | |
367 * Happens when 1 or more consecutive card changes end. | |
368 * @param {Event} e The cardSlider:card_change_ended event. | |
369 */ | |
370 function onCardChangeEnded(e) { | |
371 // If we ended on the same page as we started, ignore. | |
372 if (newTabView.cardSlider.currentCardValue.notification) | |
373 return; | |
374 | |
375 // Hide the notification the old page. | |
376 notificationContainer.classList.add('card-changed'); | |
377 | |
378 showNotificationOnCurrentPage(); | |
379 } | |
380 | |
381 /** | |
382 * Move and show the notification on the current page. | |
383 */ | |
384 function showNotificationOnCurrentPage() { | |
385 var page = newTabView.cardSlider.currentCardValue; | |
386 doWhenAllSectionsReady(function() { | |
387 if (page != newTabView.cardSlider.currentCardValue) | |
388 return; | |
389 | |
390 // NOTE: This moves the notification to inside of the current page. | |
391 page.notification = notificationContainer; | |
392 | |
393 // Reveal the notification and instruct it to hide itself if ignored. | |
394 notificationContainer.classList.remove('inactive'); | |
395 | |
396 // Gives the browser time to apply this rule before we remove it (causing | |
397 // a transition). | |
398 window.setTimeout(function() { | |
399 notificationContainer.classList.remove('card-changed'); | |
400 }, 0); | |
401 }); | |
309 } | 402 } |
310 | 403 |
311 /** | 404 /** |
312 * When done fading out, set hidden to true so the notification can't be | 405 * When done fading out, set hidden to true so the notification can't be |
313 * tabbed to or clicked. | 406 * tabbed to or clicked. |
407 * @param {Event} e The webkitTransitionEnd event. | |
314 */ | 408 */ |
315 function onNotificationTransitionEnd(e) { | 409 function onNotificationTransitionEnd(e) { |
316 if (notificationContainer.classList.contains('inactive')); | 410 if (notificationContainer.classList.contains('inactive')) |
317 notificationContainer.hidden = true; | 411 notificationContainer.hidden = true; |
318 } | 412 } |
319 | 413 |
320 function setRecentlyClosedTabs(dataItems) { | 414 function setRecentlyClosedTabs(dataItems) { |
321 $('recently-closed-menu-button').dataItems = dataItems; | 415 $('recently-closed-menu-button').dataItems = dataItems; |
322 } | 416 } |
323 | 417 |
324 function setMostVisitedPages(data, hasBlacklistedUrls) { | 418 function setMostVisitedPages(data, hasBlacklistedUrls) { |
325 newTabView.mostVisitedPage.data = data; | 419 newTabView.mostVisitedPage.data = data; |
420 cr.dispatchSimpleEvent(document, 'sectionready', true, true); | |
326 } | 421 } |
327 | 422 |
328 /** | 423 /** |
329 * Set the dominant color for a node. This will be called in response to | 424 * Set the dominant color for a node. This will be called in response to |
330 * getFaviconDominantColor. The node represented by |id| better have a setter | 425 * getFaviconDominantColor. The node represented by |id| better have a setter |
331 * for stripeColor. | 426 * for stripeColor. |
332 * @param {string} id The ID of a node. | 427 * @param {string} id The ID of a node. |
333 * @param {string} color The color represented as a CSS string. | 428 * @param {string} color The color represented as a CSS string. |
334 */ | 429 */ |
335 function setStripeColor(id, color) { | 430 function setStripeColor(id, color) { |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
447 // TODO(estade): update the content handlers to use ntp namespace instead of | 542 // TODO(estade): update the content handlers to use ntp namespace instead of |
448 // making these global. | 543 // making these global. |
449 var getAppsCallback = ntp4.getAppsCallback; | 544 var getAppsCallback = ntp4.getAppsCallback; |
450 var appsPrefChangeCallback = ntp4.appsPrefChangeCallback; | 545 var appsPrefChangeCallback = ntp4.appsPrefChangeCallback; |
451 var themeChanged = ntp4.themeChanged; | 546 var themeChanged = ntp4.themeChanged; |
452 var recentlyClosedTabs = ntp4.setRecentlyClosedTabs; | 547 var recentlyClosedTabs = ntp4.setRecentlyClosedTabs; |
453 var setMostVisitedPages = ntp4.setMostVisitedPages; | 548 var setMostVisitedPages = ntp4.setMostVisitedPages; |
454 var updateLogin = ntp4.updateLogin; | 549 var updateLogin = ntp4.updateLogin; |
455 | 550 |
456 document.addEventListener('DOMContentLoaded', ntp4.onLoad); | 551 document.addEventListener('DOMContentLoaded', ntp4.onLoad); |
OLD | NEW |