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) { |
| 223 readyCallbacks.shift()(); |
| 224 } |
| 225 } |
| 226 }); |
| 227 |
| 228 /** |
| 229 * This is used to simulate a fire-once event (i.e. $(document).ready() in |
| 230 * jQuery or Y.on('domready') in YUI. If all sections are ready, the callback |
| 231 * is fired right away. If all pages are not ready yet, the function is queued |
| 232 * for later execution. |
| 233 * @param {function} callback The work to be done when ready. |
| 234 */ |
| 235 function doWhenAllSectionsReady(callback) { |
| 236 assert(typeof callback == 'function'); |
| 237 if (sectionsToWaitFor > 0) |
| 238 readyCallbacks.push(callback); |
| 239 else |
| 240 window.setTimeout(callback, 0); // Do soon after, but asynchronously. |
| 241 } |
| 242 |
197 /** | 243 /** |
198 * Fills in an invisible div with the 'Most Visited' string so that | 244 * Fills in an invisible div with the 'Most Visited' string so that |
199 * its length may be measured and the nav dots sized accordingly. | 245 * its length may be measured and the nav dots sized accordingly. |
200 */ | 246 */ |
201 function measureNavDots() { | 247 function measureNavDots() { |
202 var measuringDiv = $('fontMeasuringDiv'); | 248 var measuringDiv = $('fontMeasuringDiv'); |
203 measuringDiv.textContent = localStrings.getString('mostvisited'); | 249 measuringDiv.textContent = localStrings.getString('mostvisited'); |
204 var pxWidth = Math.max(measuringDiv.clientWidth * 1.15, 80); | 250 var pxWidth = Math.max(measuringDiv.clientWidth * 1.15, 80); |
205 | 251 |
206 var styleElement = document.createElement('style'); | 252 var styleElement = document.createElement('style'); |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
242 attribution.hidden = false; | 288 attribution.hidden = false; |
243 } else { | 289 } else { |
244 attribution.hidden = true; | 290 attribution.hidden = true; |
245 } | 291 } |
246 } | 292 } |
247 | 293 |
248 /** | 294 /** |
249 * Timeout ID. | 295 * Timeout ID. |
250 * @type {number} | 296 * @type {number} |
251 */ | 297 */ |
252 var notificationTimeout_ = 0; | 298 var notificationTimeout = 0; |
253 | 299 |
254 /** | 300 /** |
255 * Shows the notification bubble. | 301 * Shows the notification bubble. |
256 * @param {string|Node} message The notification message or node to use as | 302 * @param {string|Node} message The notification message or node to use as |
257 * message. | 303 * message. |
258 * @param {Array.<{text: string, action: function()}>} links An array of | 304 * @param {Array.<{text: string, action: function()}>} links An array of |
259 * records describing the links in the notification. Each record should | 305 * records describing the links in the notification. Each record should |
260 * have a 'text' attribute (the display string) and an 'action' attribute | 306 * have a 'text' attribute (the display string) and an 'action' attribute |
261 * (a function to run when the link is activated). | 307 * (a function to run when the link is activated). |
262 * @param {Function} opt_closeHandler The callback invoked if the user | 308 * @param {Function} opt_closeHandler The callback invoked if the user |
263 * manually dismisses the notification. | 309 * manually dismisses the notification. |
264 */ | 310 */ |
265 function showNotification(message, links, opt_closeHandler, opt_timeout) { | 311 function showNotification(message, links, opt_closeHandler, opt_timeout) { |
266 window.clearTimeout(notificationTimeout_); | 312 window.clearTimeout(notificationTimeout); |
267 | 313 |
268 var span = document.querySelector('#notification > span'); | 314 var span = document.querySelector('#notification > span'); |
269 if (typeof message == 'string') { | 315 if (typeof message == 'string') { |
270 span.textContent = message; | 316 span.textContent = message; |
271 } else { | 317 } else { |
272 span.textContent = ''; // Remove all children. | 318 span.textContent = ''; // Remove all children. |
273 span.appendChild(message); | 319 span.appendChild(message); |
274 } | 320 } |
275 | 321 |
276 var linksBin = $('notificationLinks'); | 322 var linksBin = $('notificationLinks'); |
277 linksBin.textContent = ''; | 323 linksBin.textContent = ''; |
278 for (var i = 0; i < links.length; i++) { | 324 for (var i = 0; i < links.length; i++) { |
279 var link = linksBin.ownerDocument.createElement('div'); | 325 var link = linksBin.ownerDocument.createElement('div'); |
280 link.textContent = links[i].text; | 326 link.textContent = links[i].text; |
281 link.action = links[i].action; | 327 link.action = links[i].action; |
282 link.onclick = function() { | 328 link.onclick = function() { |
283 this.action(); | 329 this.action(); |
284 hideNotification(); | 330 hideNotification(); |
285 } | 331 }; |
286 link.setAttribute('role', 'button'); | 332 link.setAttribute('role', 'button'); |
287 link.setAttribute('tabindex', 0); | 333 link.setAttribute('tabindex', 0); |
288 link.className = 'link-button'; | 334 link.className = 'link-button'; |
289 linksBin.appendChild(link); | 335 linksBin.appendChild(link); |
290 } | 336 } |
291 | 337 |
292 document.querySelector('#notification button').onclick = function(e) { | 338 function closeFunc(e) { |
293 if (opt_closeHandler) | 339 if (opt_closeHandler) |
294 opt_closeHandler(); | 340 opt_closeHandler(); |
295 hideNotification(); | 341 hideNotification(); |
296 }; | 342 } |
| 343 |
| 344 document.querySelector('#notification button').onclick = closeFunc; |
| 345 document.addEventListener('dragstart', closeFunc); |
| 346 |
| 347 notificationContainer.hidden = false; |
| 348 showNotificationOnCurrentPage(); |
| 349 |
| 350 newTabView.cardSlider.frame.addEventListener( |
| 351 'cardSlider:card_change_ended', onCardChangeEnded); |
297 | 352 |
298 var timeout = opt_timeout || 10000; | 353 var timeout = opt_timeout || 10000; |
299 notificationContainer.hidden = false; | 354 notificationTimeout = window.setTimeout(hideNotification, timeout); |
300 notificationContainer.classList.remove('inactive'); | |
301 notificationTimeout_ = window.setTimeout(hideNotification, timeout); | |
302 } | 355 } |
303 | 356 |
304 /** | 357 /** |
305 * Hide the notification bubble. | 358 * Hide the notification bubble. |
306 */ | 359 */ |
307 function hideNotification() { | 360 function hideNotification() { |
308 notificationContainer.classList.add('inactive'); | 361 notificationContainer.classList.add('inactive'); |
| 362 |
| 363 newTabView.cardSlider.frame.removeEventListener( |
| 364 'cardSlider:card_change_ended', onCardChangeEnded); |
| 365 } |
| 366 |
| 367 /** |
| 368 * Happens when 1 or more consecutive card changes end. |
| 369 * @param {Event} e The cardSlider:card_change_ended event. |
| 370 */ |
| 371 function onCardChangeEnded(e) { |
| 372 // If we ended on the same page as we started, ignore. |
| 373 if (newTabView.cardSlider.currentCardValue.notification) |
| 374 return; |
| 375 |
| 376 // Hide the notification the old page. |
| 377 notificationContainer.classList.add('card-changed'); |
| 378 |
| 379 showNotificationOnCurrentPage(); |
| 380 } |
| 381 |
| 382 /** |
| 383 * Move and show the notification on the current page. |
| 384 */ |
| 385 function showNotificationOnCurrentPage() { |
| 386 var page = newTabView.cardSlider.currentCardValue; |
| 387 doWhenAllSectionsReady(function() { |
| 388 if (page != newTabView.cardSlider.currentCardValue) |
| 389 return; |
| 390 |
| 391 // NOTE: This moves the notification to inside of the current page. |
| 392 page.notification = notificationContainer; |
| 393 |
| 394 // Reveal the notification and instruct it to hide itself if ignored. |
| 395 notificationContainer.classList.remove('inactive'); |
| 396 |
| 397 // Gives the browser time to apply this rule before we remove it (causing |
| 398 // a transition). |
| 399 window.setTimeout(function() { |
| 400 notificationContainer.classList.remove('card-changed'); |
| 401 }, 0); |
| 402 }); |
309 } | 403 } |
310 | 404 |
311 /** | 405 /** |
312 * When done fading out, set hidden to true so the notification can't be | 406 * When done fading out, set hidden to true so the notification can't be |
313 * tabbed to or clicked. | 407 * tabbed to or clicked. |
| 408 * @param {Event} e The webkitTransitionEnd event. |
314 */ | 409 */ |
315 function onNotificationTransitionEnd(e) { | 410 function onNotificationTransitionEnd(e) { |
316 if (notificationContainer.classList.contains('inactive')); | 411 if (notificationContainer.classList.contains('inactive')) |
317 notificationContainer.hidden = true; | 412 notificationContainer.hidden = true; |
318 } | 413 } |
319 | 414 |
320 function setRecentlyClosedTabs(dataItems) { | 415 function setRecentlyClosedTabs(dataItems) { |
321 $('recently-closed-menu-button').dataItems = dataItems; | 416 $('recently-closed-menu-button').dataItems = dataItems; |
322 } | 417 } |
323 | 418 |
324 function setMostVisitedPages(data, hasBlacklistedUrls) { | 419 function setMostVisitedPages(data, hasBlacklistedUrls) { |
325 newTabView.mostVisitedPage.data = data; | 420 newTabView.mostVisitedPage.data = data; |
| 421 cr.dispatchSimpleEvent(document, 'sectionready', true, true); |
326 } | 422 } |
327 | 423 |
328 /** | 424 /** |
329 * Set the dominant color for a node. This will be called in response to | 425 * 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 | 426 * getFaviconDominantColor. The node represented by |id| better have a setter |
331 * for stripeColor. | 427 * for stripeColor. |
332 * @param {string} id The ID of a node. | 428 * @param {string} id The ID of a node. |
333 * @param {string} color The color represented as a CSS string. | 429 * @param {string} color The color represented as a CSS string. |
334 */ | 430 */ |
335 function setStripeColor(id, color) { | 431 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 | 543 // TODO(estade): update the content handlers to use ntp namespace instead of |
448 // making these global. | 544 // making these global. |
449 var getAppsCallback = ntp4.getAppsCallback; | 545 var getAppsCallback = ntp4.getAppsCallback; |
450 var appsPrefChangeCallback = ntp4.appsPrefChangeCallback; | 546 var appsPrefChangeCallback = ntp4.appsPrefChangeCallback; |
451 var themeChanged = ntp4.themeChanged; | 547 var themeChanged = ntp4.themeChanged; |
452 var recentlyClosedTabs = ntp4.setRecentlyClosedTabs; | 548 var recentlyClosedTabs = ntp4.setRecentlyClosedTabs; |
453 var setMostVisitedPages = ntp4.setMostVisitedPages; | 549 var setMostVisitedPages = ntp4.setMostVisitedPages; |
454 var updateLogin = ntp4.updateLogin; | 550 var updateLogin = ntp4.updateLogin; |
455 | 551 |
456 document.addEventListener('DOMContentLoaded', ntp4.onLoad); | 552 document.addEventListener('DOMContentLoaded', ntp4.onLoad); |
OLD | NEW |