OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 /** | 6 /** |
7 * @fileoverview The local InstantExtended NTP. | 7 * @fileoverview The local InstantExtended NTP. |
8 */ | 8 */ |
9 | 9 |
10 | 10 |
(...skipping 10 matching lines...) Expand all Loading... |
21 | 21 |
22 /** | 22 /** |
23 * Enum for classnames. | 23 * Enum for classnames. |
24 * @enum {string} | 24 * @enum {string} |
25 * @const | 25 * @const |
26 */ | 26 */ |
27 var CLASSES = { | 27 var CLASSES = { |
28 ALTERNATE_LOGO: 'alternate-logo', // Shows white logo if required by theme | 28 ALTERNATE_LOGO: 'alternate-logo', // Shows white logo if required by theme |
29 BLACKLIST: 'mv-blacklist', // triggers tile blacklist animation | 29 BLACKLIST: 'mv-blacklist', // triggers tile blacklist animation |
30 BLACKLIST_BUTTON: 'mv-x', | 30 BLACKLIST_BUTTON: 'mv-x', |
| 31 DARK: 'dark', |
| 32 DEFAULT_THEME: 'default-theme', |
31 DELAYED_HIDE_NOTIFICATION: 'mv-notice-delayed-hide', | 33 DELAYED_HIDE_NOTIFICATION: 'mv-notice-delayed-hide', |
| 34 DOT: 'dot', |
32 FAKEBOX_DISABLE: 'fakebox-disable', // Makes fakebox non-interactive | 35 FAKEBOX_DISABLE: 'fakebox-disable', // Makes fakebox non-interactive |
33 FAKEBOX_FOCUS: 'fakebox-focused', // Applies focus styles to the fakebox | 36 FAKEBOX_FOCUS: 'fakebox-focused', // Applies focus styles to the fakebox |
34 // Applies drag focus style to the fakebox | 37 // Applies drag focus style to the fakebox |
35 FAKEBOX_DRAG_FOCUS: 'fakebox-drag-focused', | 38 FAKEBOX_DRAG_FOCUS: 'fakebox-drag-focused', |
36 FAVICON: 'mv-favicon', | 39 FAVICON: 'mv-favicon', |
| 40 FAVICON_FALLBACK: 'mv-favicon-fallback', |
37 HIDE_BLACKLIST_BUTTON: 'mv-x-hide', // hides blacklist button during animation | 41 HIDE_BLACKLIST_BUTTON: 'mv-x-hide', // hides blacklist button during animation |
38 HIDE_FAKEBOX_AND_LOGO: 'hide-fakebox-logo', | 42 HIDE_FAKEBOX_AND_LOGO: 'hide-fakebox-logo', |
39 HIDE_NOTIFICATION: 'mv-notice-hide', | 43 HIDE_NOTIFICATION: 'mv-notice-hide', |
40 // Vertically centers the most visited section for a non-Google provided page. | 44 // Vertically centers the most visited section for a non-Google provided page. |
41 NON_GOOGLE_PAGE: 'non-google-page', | 45 NON_GOOGLE_PAGE: 'non-google-page', |
42 PAGE: 'mv-page', // page tiles | 46 PAGE: 'mv-page', // page tiles |
43 PAGE_READY: 'mv-page-ready', // page tile when ready | 47 PAGE_READY: 'mv-page-ready', // page tile when ready |
44 RTL: 'rtl', // Right-to-left language text. | 48 RTL: 'rtl', // Right-to-left language text. |
45 THUMBNAIL: 'mv-thumb', | 49 THUMBNAIL: 'mv-thumb', |
| 50 THUMBNAIL_FALLBACK: 'mv-thumb-fallback', |
46 THUMBNAIL_MASK: 'mv-mask', | 51 THUMBNAIL_MASK: 'mv-mask', |
47 TILE: 'mv-tile', | 52 TILE: 'mv-tile', |
| 53 TILE_INNER: 'mv-tile-inner', |
48 TITLE: 'mv-title' | 54 TITLE: 'mv-title' |
49 }; | 55 }; |
50 | 56 |
51 | 57 |
52 /** | 58 /** |
53 * Enum for HTML element ids. | 59 * Enum for HTML element ids. |
54 * @enum {string} | 60 * @enum {string} |
55 * @const | 61 * @const |
56 */ | 62 */ |
57 var IDS = { | 63 var IDS = { |
58 ATTRIBUTION: 'attribution', | 64 ATTRIBUTION: 'attribution', |
59 ATTRIBUTION_TEXT: 'attribution-text', | 65 ATTRIBUTION_TEXT: 'attribution-text', |
60 CUSTOM_THEME_STYLE: 'ct-style', | 66 CUSTOM_THEME_STYLE: 'ct-style', |
61 FAKEBOX: 'fakebox', | 67 FAKEBOX: 'fakebox', |
62 FAKEBOX_INPUT: 'fakebox-input', | 68 FAKEBOX_INPUT: 'fakebox-input', |
| 69 FAKEBOX_TEXT: 'fakebox-text', |
63 LOGO: 'logo', | 70 LOGO: 'logo', |
64 NOTIFICATION: 'mv-notice', | 71 NOTIFICATION: 'mv-notice', |
65 NOTIFICATION_CLOSE_BUTTON: 'mv-notice-x', | 72 NOTIFICATION_CLOSE_BUTTON: 'mv-notice-x', |
66 NOTIFICATION_MESSAGE: 'mv-msg', | 73 NOTIFICATION_MESSAGE: 'mv-msg', |
67 NTP_CONTENTS: 'ntp-contents', | 74 NTP_CONTENTS: 'ntp-contents', |
68 RESTORE_ALL_LINK: 'mv-restore', | 75 RESTORE_ALL_LINK: 'mv-restore', |
69 TILES: 'mv-tiles', | 76 TILES: 'mv-tiles', |
70 UNDO_LINK: 'mv-undo' | 77 UNDO_LINK: 'mv-undo' |
71 }; | 78 }; |
72 | 79 |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
162 /** | 169 /** |
163 * True if a page has been blacklisted and we're waiting on the | 170 * True if a page has been blacklisted and we're waiting on the |
164 * onmostvisitedchange callback. See onMostVisitedChange() for how this | 171 * onmostvisitedchange callback. See onMostVisitedChange() for how this |
165 * is used. | 172 * is used. |
166 * @type {boolean} | 173 * @type {boolean} |
167 */ | 174 */ |
168 var isBlacklisting = false; | 175 var isBlacklisting = false; |
169 | 176 |
170 | 177 |
171 /** | 178 /** |
| 179 * Stores whether the current theme has a dark background. |
| 180 * @type {boolean} |
| 181 */ |
| 182 var isBackgroundDark = false; |
| 183 |
| 184 |
| 185 /** |
172 * Current number of tiles columns shown based on the window width, including | 186 * Current number of tiles columns shown based on the window width, including |
173 * those that just contain filler. | 187 * those that just contain filler. |
174 * @type {number} | 188 * @type {number} |
175 */ | 189 */ |
176 var numColumnsShown = 0; | 190 var numColumnsShown = 0; |
177 | 191 |
178 | 192 |
179 /** | 193 /** |
180 * A flag to indicate Most Visited changed caused by user action. If true, then | 194 * A flag to indicate Most Visited changed caused by user action. If true, then |
181 * in onMostVisitedChange() tiles remain visible so no flickering occurs. | 195 * in onMostVisitedChange() tiles remain visible so no flickering occurs. |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
259 * @const | 273 * @const |
260 */ | 274 */ |
261 var MOST_VISITED_PAINT_TIMEOUT_MSEC = 500; | 275 var MOST_VISITED_PAINT_TIMEOUT_MSEC = 500; |
262 | 276 |
263 | 277 |
264 /** | 278 /** |
265 * A Tile is either a rendering of a Most Visited page or "filler" used to | 279 * A Tile is either a rendering of a Most Visited page or "filler" used to |
266 * pad out the section when not enough pages exist. | 280 * pad out the section when not enough pages exist. |
267 * | 281 * |
268 * @param {Element} elem The element for rendering the tile. | 282 * @param {Element} elem The element for rendering the tile. |
| 283 * @param {Element=} opt_innerElem The element for contents of tile. |
269 * @param {Element=} opt_titleElem The element for rendering the title. | 284 * @param {Element=} opt_titleElem The element for rendering the title. |
270 * @param {Element=} opt_thumbnailElem The element for rendering the thumbnail. | 285 * @param {Element=} opt_thumbnailElem The element for rendering the thumbnail. |
271 * @param {number=} opt_rid The RID for the corresponding Most Visited page. | 286 * @param {number=} opt_rid The RID for the corresponding Most Visited page. |
272 * Should only be left unspecified when creating a filler tile. | 287 * Should only be left unspecified when creating a filler tile. |
273 * @constructor | 288 * @constructor |
274 */ | 289 */ |
275 function Tile(elem, opt_titleElem, opt_thumbnailElem, opt_rid) { | 290 function Tile(elem, opt_innerElem, opt_titleElem, opt_thumbnailElem, opt_rid) { |
276 /** @type {Element} */ | 291 /** @type {Element} */ |
277 this.elem = elem; | 292 this.elem = elem; |
278 | 293 |
279 /** @type {Element|undefined} */ | 294 /** @type {Element|undefined} */ |
| 295 this.innerElem = opt_innerElem; |
| 296 |
| 297 /** @type {Element|undefined} */ |
280 this.titleElem = opt_titleElem; | 298 this.titleElem = opt_titleElem; |
281 | 299 |
282 /** @type {Element|undefined} */ | 300 /** @type {Element|undefined} */ |
283 this.thumbnailElem = opt_thumbnailElem; | 301 this.thumbnailElem = opt_thumbnailElem; |
284 | 302 |
285 /** @type {number|undefined} */ | 303 /** @type {number|undefined} */ |
286 this.rid = opt_rid; | 304 this.rid = opt_rid; |
287 } | 305 } |
288 | 306 |
289 | 307 |
290 /** | 308 /** |
| 309 * Determines whether a theme should be considered to have dark background. |
| 310 * @param {ThemeBackgroundInfo} info Theme background information. |
| 311 * @return {boolean} Whether the theme has dark background. |
| 312 * @private |
| 313 */ |
| 314 function getIsBackgroundDark(info) { |
| 315 if (info.imageUrl) |
| 316 return true; |
| 317 var rgba = info.backgroundColorRgba; |
| 318 var luminance = 0.3 * rgba[0] + 0.59 * rgba[1] + 0.11 * rgba[2]; |
| 319 return luminance < 128; |
| 320 } |
| 321 |
| 322 |
| 323 /** |
291 * Updates the NTP based on the current theme. | 324 * Updates the NTP based on the current theme. |
292 * @private | 325 * @private |
293 */ | 326 */ |
294 function onThemeChange() { | 327 function renderTheme() { |
295 var info = ntpApiHandle.themeBackgroundInfo; | 328 var info = ntpApiHandle.themeBackgroundInfo; |
296 if (!info) | 329 if (!info) { |
| 330 isBackgroundDark = false; |
297 return; | 331 return; |
| 332 } |
| 333 |
| 334 isBackgroundDark = getIsBackgroundDark(info); |
| 335 ntpContents.classList.toggle(CLASSES.DARK, isBackgroundDark); |
298 | 336 |
299 var background = [convertToRGBAColor(info.backgroundColorRgba), | 337 var background = [convertToRGBAColor(info.backgroundColorRgba), |
300 info.imageUrl, | 338 info.imageUrl, |
301 info.imageTiling, | 339 info.imageTiling, |
302 info.imageHorizontalAlignment, | 340 info.imageHorizontalAlignment, |
303 info.imageVerticalAlignment].join(' ').trim(); | 341 info.imageVerticalAlignment].join(' ').trim(); |
304 document.body.style.background = background; | 342 document.body.style.background = background; |
305 document.body.classList.toggle(CLASSES.ALTERNATE_LOGO, info.alternateLogo); | 343 document.body.classList.toggle(CLASSES.ALTERNATE_LOGO, info.alternateLogo); |
306 updateThemeAttribution(info.attributionUrl); | 344 updateThemeAttribution(info.attributionUrl); |
307 setCustomThemeStyle(info); | 345 setCustomThemeStyle(info); |
| 346 } |
308 | 347 |
| 348 |
| 349 /** |
| 350 * Updates the NTP based on the current theme, then rerenders all tiles. |
| 351 * @private |
| 352 */ |
| 353 function onThemeChange() { |
| 354 renderTheme(); |
309 tilesContainer.innerHTML = ''; | 355 tilesContainer.innerHTML = ''; |
310 renderAndShowTiles(); | 356 renderAndShowTiles(); |
311 } | 357 } |
312 | 358 |
313 | 359 |
314 /** | 360 /** |
315 * Updates the NTP style according to theme. | 361 * Updates the NTP style according to theme. |
316 * @param {Object=} opt_themeInfo The information about the theme. If it is | 362 * @param {Object=} opt_themeInfo The information about the theme. If it is |
317 * omitted the style will be reverted to the default. | 363 * omitted the style will be reverted to the default. |
318 * @private | 364 * @private |
319 */ | 365 */ |
320 function setCustomThemeStyle(opt_themeInfo) { | 366 function setCustomThemeStyle(opt_themeInfo) { |
321 var customStyleElement = $(IDS.CUSTOM_THEME_STYLE); | 367 var customStyleElement = $(IDS.CUSTOM_THEME_STYLE); |
322 var head = document.head; | 368 var head = document.head; |
323 | 369 |
324 if (opt_themeInfo && !opt_themeInfo.usingDefaultTheme) { | 370 if (opt_themeInfo && !opt_themeInfo.usingDefaultTheme) { |
| 371 ntpContents.classList.remove(CLASSES.DEFAULT_THEME); |
325 var themeStyle = | 372 var themeStyle = |
326 '#attribution {' + | 373 '#attribution {' + |
327 ' color: ' + convertToRGBAColor(opt_themeInfo.textColorLightRgba) + ';' + | 374 ' color: ' + convertToRGBAColor(opt_themeInfo.textColorLightRgba) + ';' + |
328 '}' + | 375 '}' + |
329 '#mv-msg {' + | 376 '#mv-msg {' + |
330 ' color: ' + convertToRGBAColor(opt_themeInfo.textColorRgba) + ';' + | 377 ' color: ' + convertToRGBAColor(opt_themeInfo.textColorRgba) + ';' + |
331 '}' + | 378 '}' + |
332 '#mv-notice-links span {' + | 379 '#mv-notice-links span {' + |
333 ' color: ' + convertToRGBAColor(opt_themeInfo.textColorLightRgba) + ';' + | 380 ' color: ' + convertToRGBAColor(opt_themeInfo.textColorLightRgba) + ';' + |
334 '}' + | 381 '}' + |
335 '#mv-notice-x {' + | 382 '#mv-notice-x {' + |
336 ' -webkit-filter: drop-shadow(0 0 0 ' + | 383 ' -webkit-filter: drop-shadow(0 0 0 ' + |
337 convertToRGBAColor(opt_themeInfo.textColorRgba) + ');' + | 384 convertToRGBAColor(opt_themeInfo.textColorRgba) + ');' + |
338 '}' + | 385 '}' + |
339 '.mv-page-ready {' + | 386 '.mv-page-ready .mv-mask {' + |
340 ' border: 1px solid ' + | 387 ' border: 1px solid ' + |
341 convertToRGBAColor(opt_themeInfo.sectionBorderColorRgba) + ';' + | 388 convertToRGBAColor(opt_themeInfo.sectionBorderColorRgba) + ';' + |
342 '}' + | 389 '}' + |
343 '.mv-page-ready:hover, .mv-page-ready:focus {' + | 390 '.mv-page-ready:hover .mv-mask, .mv-page-ready:focus .mv-mask {' + |
344 ' border-color: ' + | 391 ' border-color: ' + |
345 convertToRGBAColor(opt_themeInfo.headerColorRgba) + ';' + | 392 convertToRGBAColor(opt_themeInfo.headerColorRgba) + ';' + |
346 '}'; | 393 '}'; |
347 | 394 |
348 if (customStyleElement) { | 395 if (customStyleElement) { |
349 customStyleElement.textContent = themeStyle; | 396 customStyleElement.textContent = themeStyle; |
350 } else { | 397 } else { |
351 customStyleElement = document.createElement('style'); | 398 customStyleElement = document.createElement('style'); |
352 customStyleElement.type = 'text/css'; | 399 customStyleElement.type = 'text/css'; |
353 customStyleElement.id = IDS.CUSTOM_THEME_STYLE; | 400 customStyleElement.id = IDS.CUSTOM_THEME_STYLE; |
354 customStyleElement.textContent = themeStyle; | 401 customStyleElement.textContent = themeStyle; |
355 head.appendChild(customStyleElement); | 402 head.appendChild(customStyleElement); |
356 } | 403 } |
357 | 404 |
358 } else if (customStyleElement) { | 405 } else { |
359 head.removeChild(customStyleElement); | 406 ntpContents.classList.add(CLASSES.DEFAULT_THEME); |
| 407 if (customStyleElement) |
| 408 head.removeChild(customStyleElement); |
360 } | 409 } |
361 } | 410 } |
362 | 411 |
363 | 412 |
364 /** | 413 /** |
365 * Renders the attribution if the URL is present, otherwise hides it. | 414 * Renders the attribution if the URL is present, otherwise hides it. |
366 * @param {string} url The URL of the attribution image, if any. | 415 * @param {string} url The URL of the attribution image, if any. |
367 * @private | 416 * @private |
368 */ | 417 */ |
369 function updateThemeAttribution(url) { | 418 function updateThemeAttribution(url) { |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
405 color[3] / 255 + ')'; | 454 color[3] / 255 + ')'; |
406 } | 455 } |
407 | 456 |
408 | 457 |
409 /** | 458 /** |
410 * Handles a new set of Most Visited page data. | 459 * Handles a new set of Most Visited page data. |
411 */ | 460 */ |
412 function onMostVisitedChange() { | 461 function onMostVisitedChange() { |
413 if (isBlacklisting) { | 462 if (isBlacklisting) { |
414 // Trigger the blacklist animation, which then triggers reloadAllTiles(). | 463 // Trigger the blacklist animation, which then triggers reloadAllTiles(). |
415 var lastBlacklistedTileElement = lastBlacklistedTile.elem; | 464 var lastBlacklistedTileElem = lastBlacklistedTile.elem; |
416 lastBlacklistedTileElement.addEventListener( | 465 lastBlacklistedTileElem.addEventListener( |
417 'webkitTransitionEnd', blacklistAnimationDone); | 466 'webkitTransitionEnd', blacklistAnimationDone); |
418 lastBlacklistedTileElement.classList.add(CLASSES.BLACKLIST); | 467 lastBlacklistedTileElem.classList.add(CLASSES.BLACKLIST); |
419 } else { | 468 } else { |
420 reloadAllTiles(); | 469 reloadAllTiles(); |
421 } | 470 } |
422 } | 471 } |
423 | 472 |
424 | 473 |
425 /** | 474 /** |
426 * Handles the end of the blacklist animation by showing the notification and | 475 * Handles the end of the blacklist animation by showing the notification and |
427 * re-rendering the new set of tiles. | 476 * re-rendering the new set of tiles. |
428 */ | 477 */ |
(...skipping 21 matching lines...) Expand all Loading... |
450 tiles.push(createTile(pages[i], i)); | 499 tiles.push(createTile(pages[i], i)); |
451 | 500 |
452 tilesContainer.innerHTML = ''; | 501 tilesContainer.innerHTML = ''; |
453 renderAndShowTiles(); | 502 renderAndShowTiles(); |
454 } | 503 } |
455 | 504 |
456 | 505 |
457 /** | 506 /** |
458 * Binds onload events for a tile's internal <iframe> elements. | 507 * Binds onload events for a tile's internal <iframe> elements. |
459 * @param {Tile} tile The main tile to bind events to. | 508 * @param {Tile} tile The main tile to bind events to. |
460 * @param {Barrier} tileVisibilityBarrier A barrier to make tile visible the | 509 * @param {Barrier} tileVisibilityBarrier A barrier to make all tiles visible |
461 * moment all tiles are loaded. | 510 * the moment all tiles are loaded. |
462 */ | 511 */ |
463 function bindTileOnloadEvents(tile, tileVisibilityBarrier) { | 512 function bindTileOnloadEvents(tile, tileVisibilityBarrier) { |
464 if (tile.titleElem) { | 513 if (tile.titleElem) { |
465 tileVisibilityBarrier.add(); | 514 tileVisibilityBarrier.add(); |
466 tile.titleElem.onload = function() { | 515 tile.titleElem.onload = function() { |
467 tile.titleElem.hidden = false; | |
468 tileVisibilityBarrier.remove(); | 516 tileVisibilityBarrier.remove(); |
469 }; | 517 }; |
470 } | 518 } |
471 | |
472 if (tile.thumbnailElem) { | 519 if (tile.thumbnailElem) { |
473 tileVisibilityBarrier.add(); | 520 tileVisibilityBarrier.add(); |
474 tile.thumbnailElem.onload = function() { | 521 tile.thumbnailElem.onload = function() { |
475 tile.thumbnailElem.hidden = false; | |
476 tile.elem.classList.add(CLASSES.PAGE_READY); | 522 tile.elem.classList.add(CLASSES.PAGE_READY); |
477 tileVisibilityBarrier.remove(); | 523 tileVisibilityBarrier.remove(); |
478 }; | 524 }; |
479 } | 525 } |
480 } | 526 } |
481 | 527 |
482 | 528 |
483 /** | 529 /** |
484 * Renders the current list of visible tiles to DOM, and hides tiles that are | 530 * Renders the current list of visible tiles to DOM, and hides tiles that are |
485 * already in the DOM but should not be seen. | 531 * already in the DOM but should not be seen. |
486 */ | 532 */ |
487 function renderAndShowTiles() { | 533 function renderAndShowTiles() { |
488 var numExisting = tilesContainer.querySelectorAll('.' + CLASSES.TILE).length; | 534 var numExisting = tilesContainer.querySelectorAll('.' + CLASSES.TILE).length; |
489 // Only add visible tiles to the DOM, to avoid creating invisible tiles that | 535 // Only add visible tiles to the DOM, to avoid creating invisible tiles that |
490 // produce meaningless impression metrics. However, if a tile becomes | 536 // produce meaningless impression metrics. However, if a tile becomes |
491 // invisible then we leave it in DOM to prevent reload if it's shown again. | 537 // invisible then we leave it in DOM to prevent reload if it's shown again. |
492 var numDesired = Math.min(tiles.length, numColumnsShown * NUM_ROWS); | 538 var numDesired = Math.min(tiles.length, numColumnsShown * NUM_ROWS); |
493 | 539 |
494 // If we need to render new tiles, manage the visibility to hide intermediate | 540 // If we need to render new tiles, manage the visibility to hide intermediate |
495 // load states of the <iframe>s. | 541 // load states of the <iframe>s. |
496 if (numExisting < numDesired) { | 542 if (numExisting < numDesired) { |
497 var tileVisibilityBarrier = new Barrier(function() { | 543 var showAll = function() { |
498 tilesContainer.style.visibility = 'visible'; | 544 for (var i = 0; i < numDesired; ++i) { |
499 }); | 545 if (tiles[i].titleElem || tiles[i].thumbnailElem) |
| 546 tiles[i].elem.classList.add(CLASSES.PAGE_READY); |
| 547 } |
| 548 }; |
| 549 var tileVisibilityBarrier = new Barrier(showAll); |
500 | 550 |
501 if (!userInitiatedMostVisitedChange) { | 551 if (!userInitiatedMostVisitedChange) { |
502 // Make titleContainer invisible, but still taking up space. | 552 // Make titleContainer invisible, but still taking up space. |
503 // titleContainer becomes visible again (1) on timeout, or (2) when all | 553 // titleContainer becomes visible again (1) on timeout, or (2) when all |
504 // tiles finish loading (using tileVisibilityBarrier). | 554 // tiles finish loading (using tileVisibilityBarrier). |
505 tilesContainer.style.visibility = 'hidden'; | |
506 window.setTimeout(function() { | 555 window.setTimeout(function() { |
507 tileVisibilityBarrier.cancel(); | 556 tileVisibilityBarrier.cancel(); |
508 tilesContainer.style.visibility = 'visible'; | 557 showAll(); |
509 }, MOST_VISITED_PAINT_TIMEOUT_MSEC); | 558 }, MOST_VISITED_PAINT_TIMEOUT_MSEC); |
510 } | 559 } |
511 userInitiatedMostVisitedChange = false; | 560 userInitiatedMostVisitedChange = false; |
512 | 561 |
513 for (var i = numExisting; i < numDesired; ++i) { | 562 for (var i = numExisting; i < numDesired; ++i) { |
514 bindTileOnloadEvents(tiles[i], tileVisibilityBarrier); | 563 bindTileOnloadEvents(tiles[i], tileVisibilityBarrier); |
515 tilesContainer.appendChild(tiles[i].elem); | 564 tilesContainer.appendChild(tiles[i].elem); |
516 } | 565 } |
517 } | 566 } |
518 | 567 |
519 // Show only the desired tiles. Not using .hidden because it does not work for | 568 // Show only the desired tiles. Note that .hidden does not work for |
520 // inline-block elements. | 569 // inline-block elements like tiles[i].elem. |
521 for (var i = 0; i < numDesired; ++i) | 570 for (var i = 0; i < numDesired; ++i) |
522 tiles[i].elem.style.display = 'inline-block'; | 571 tiles[i].elem.style.display = 'inline-block'; |
523 // If |numDesired| < |numExisting| then hide extra tiles (e.g., this occurs | 572 // If |numDesired| < |numExisting| then hide extra tiles (e.g., this occurs |
524 // when window is downsized). | 573 // when window is downsized). |
525 for (; i < numExisting; ++i) | 574 for (; i < numExisting; ++i) |
526 tiles[i].elem.style.display = 'none'; | 575 tiles[i].elem.style.display = 'none'; |
527 } | 576 } |
528 | 577 |
529 | 578 |
530 /** | 579 /** |
531 * Builds a URL to display a most visited tile title in an iframe. | 580 * Builds a URL to display a most visited tile title in an iframe. |
532 * @param {number} rid The restricted ID. | 581 * @param {number} rid The restricted ID. |
533 * @param {number} position The position of the iframe in the UI. | 582 * @param {number} position The position of the iframe in the UI. |
534 * @return {string} An URL to display the most visited title in an iframe. | 583 * @return {string} An URL to display the most visited title in an iframe. |
535 */ | 584 */ |
536 function getMostVisitedTitleIframeUrl(rid, position) { | 585 function getMostVisitedTitleIframeUrl(rid, position) { |
537 var url = 'chrome-search://most-visited/' + | 586 var url = 'chrome-search://most-visited/' + |
538 encodeURIComponent(MOST_VISITED_TITLE_IFRAME); | 587 encodeURIComponent(MOST_VISITED_TITLE_IFRAME); |
| 588 var titleColor = isBackgroundDark ? NTP_DESIGN.titleColorAgainstDark : |
| 589 NTP_DESIGN.titleColor; |
539 var params = [ | 590 var params = [ |
540 'rid=' + encodeURIComponent(rid), | 591 'rid=' + encodeURIComponent(rid), |
541 'f=' + encodeURIComponent(NTP_DESIGN.fontFamily), | 592 'f=' + encodeURIComponent(NTP_DESIGN.fontFamily), |
542 'fs=' + encodeURIComponent(NTP_DESIGN.fontSize), | 593 'fs=' + encodeURIComponent(NTP_DESIGN.fontSize), |
543 'c=' + encodeURIComponent(NTP_DESIGN.titleColor), | 594 'c=' + encodeURIComponent(titleColor), |
544 'pos=' + encodeURIComponent(position)]; | 595 'pos=' + encodeURIComponent(position)]; |
545 if (NTP_DESIGN.titleTextAlign) | 596 if (NTP_DESIGN.titleTextAlign) |
546 params.push('ta=' + encodeURIComponent(NTP_DESIGN.titleTextAlign)); | 597 params.push('ta=' + encodeURIComponent(NTP_DESIGN.titleTextAlign)); |
547 if (NTP_DESIGN.titleTextFade) | 598 if (NTP_DESIGN.titleTextFade) |
548 params.push('tf=' + encodeURIComponent(NTP_DESIGN.titleTextFade)); | 599 params.push('tf=' + encodeURIComponent(NTP_DESIGN.titleTextFade)); |
549 return url + '?' + params.join('&'); | 600 return url + '?' + params.join('&'); |
550 } | 601 } |
551 | 602 |
552 | 603 |
553 /** | 604 /** |
554 * Builds a URL to display a most visited tile thumbnail in an iframe. | 605 * Builds a URL to display a most visited tile thumbnail in an iframe. |
555 * @param {number} rid The restricted ID. | 606 * @param {number} rid The restricted ID. |
556 * @param {number} position The position of the iframe in the UI. | 607 * @param {number} position The position of the iframe in the UI. |
557 * @return {string} An URL to display the most visited thumbnail in an iframe. | 608 * @return {string} An URL to display the most visited thumbnail in an iframe. |
558 */ | 609 */ |
559 function getMostVisitedThumbnailIframeUrl(rid, position) { | 610 function getMostVisitedThumbnailIframeUrl(rid, position) { |
560 var url = 'chrome-search://most-visited/' + | 611 var url = 'chrome-search://most-visited/' + |
561 encodeURIComponent(MOST_VISITED_THUMBNAIL_IFRAME); | 612 encodeURIComponent(MOST_VISITED_THUMBNAIL_IFRAME); |
562 var params = [ | 613 var params = [ |
563 'rid=' + encodeURIComponent(rid), | 614 'rid=' + encodeURIComponent(rid), |
564 'f=' + encodeURIComponent(NTP_DESIGN.fontFamily), | 615 'f=' + encodeURIComponent(NTP_DESIGN.fontFamily), |
565 'fs=' + encodeURIComponent(NTP_DESIGN.fontSize), | 616 'fs=' + encodeURIComponent(NTP_DESIGN.fontSize), |
566 'c=' + encodeURIComponent(NTP_DESIGN.thumbnailTextColor), | 617 'c=' + encodeURIComponent(NTP_DESIGN.thumbnailTextColor), |
567 'pos=' + encodeURIComponent(position)]; | 618 'pos=' + encodeURIComponent(position)]; |
| 619 if (NTP_DESIGN.thumbnailFallback) |
| 620 params.push('etfb=1'); |
568 return url + '?' + params.join('&'); | 621 return url + '?' + params.join('&'); |
569 } | 622 } |
570 | 623 |
571 | 624 |
572 /** | 625 /** |
573 * Creates a Tile with the specified page data. If no data is provided, a | 626 * Creates a Tile with the specified page data. If no data is provided, a |
574 * filler Tile is created. | 627 * filler Tile is created. |
575 * @param {Object} page The page data. | 628 * @param {Object} page The page data. |
576 * @param {number} position The position of the tile. | 629 * @param {number} position The position of the tile. |
577 * @return {Tile} The new Tile. | 630 * @return {Tile} The new Tile. |
578 */ | 631 */ |
579 function createTile(page, position) { | 632 function createTile(page, position) { |
580 var tileElement = document.createElement('div'); | 633 var tileElem = document.createElement('div'); |
581 tileElement.classList.add(CLASSES.TILE); | 634 tileElem.classList.add(CLASSES.TILE); |
| 635 var innerElem = createAndAppendElement(tileElem, 'div', CLASSES.TILE_INNER); |
582 | 636 |
583 if (page) { | 637 if (page) { |
584 var rid = page.rid; | 638 var rid = page.rid; |
585 tileElement.classList.add(CLASSES.PAGE); | 639 tileElem.classList.add(CLASSES.PAGE); |
586 | 640 |
587 var navigateFunction = function(e) { | 641 var navigateFunction = function(e) { |
588 e.preventDefault(); | 642 e.preventDefault(); |
589 ntpApiHandle.navigateContentWindow(rid, getDispositionFromEvent(e)); | 643 ntpApiHandle.navigateContentWindow(rid, getDispositionFromEvent(e)); |
590 }; | 644 }; |
591 | 645 |
592 // The click handler for navigating to the page identified by the RID. | 646 // The click handler for navigating to the page identified by the RID. |
593 tileElement.addEventListener('click', navigateFunction); | 647 tileElem.addEventListener('click', navigateFunction); |
594 | 648 |
595 // Make thumbnails tab-accessible. | 649 // Make thumbnails tab-accessible. |
596 tileElement.setAttribute('tabindex', '1'); | 650 tileElem.setAttribute('tabindex', '1'); |
597 registerKeyHandler(tileElement, KEYCODE.ENTER, navigateFunction); | 651 registerKeyHandler(tileElem, KEYCODE.ENTER, navigateFunction); |
598 | 652 |
599 // The iframe which renders the page title. | 653 // The iframe which renders the page title. |
600 var titleElement = document.createElement('iframe'); | 654 var titleElem = document.createElement('iframe'); |
601 titleElement.tabIndex = '-1'; | 655 titleElem.tabIndex = '-1'; |
602 | 656 |
603 // Why iframes have IDs: | 657 // Why iframes have IDs: |
604 // | 658 // |
605 // On navigating back to the NTP we see several onmostvisitedchange() events | 659 // On navigating back to the NTP we see several onmostvisitedchange() events |
606 // in series with incrementing RIDs. After the first event, a set of iframes | 660 // in series with incrementing RIDs. After the first event, a set of iframes |
607 // begins loading RIDs n, n+1, ..., n+k-1; after the second event, these get | 661 // begins loading RIDs n, n+1, ..., n+k-1; after the second event, these get |
608 // destroyed and a new set begins loading RIDs n+k, n+k+1, ..., n+2k-1. | 662 // destroyed and a new set begins loading RIDs n+k, n+k+1, ..., n+2k-1. |
609 // Now due to crbug.com/68841, Chrome incorrectly loads the content for the | 663 // Now due to crbug.com/68841, Chrome incorrectly loads the content for the |
610 // first set of iframes into the most recent set of iframes. | 664 // first set of iframes into the most recent set of iframes. |
611 // | 665 // |
612 // Giving iframes distinct ids seems to cause some invalidation and prevent | 666 // Giving iframes distinct ids seems to cause some invalidation and prevent |
613 // associating the incorrect data. | 667 // associating the incorrect data. |
614 // | 668 // |
615 // TODO(jered): Find and fix the root (probably Blink) bug. | 669 // TODO(jered): Find and fix the root (probably Blink) bug. |
616 | 670 |
617 // Keep this ID here. See comment above. | 671 // Keep this ID here. See comment above. |
618 titleElement.id = 'title-' + rid; | 672 titleElem.id = 'title-' + rid; |
619 titleElement.className = CLASSES.TITLE; | 673 titleElem.className = CLASSES.TITLE; |
620 titleElement.hidden = true; | 674 titleElem.src = getMostVisitedTitleIframeUrl(rid, position); |
621 titleElement.src = getMostVisitedTitleIframeUrl(rid, position); | 675 innerElem.appendChild(titleElem); |
622 tileElement.appendChild(titleElement); | 676 |
| 677 // A fallback element for missing thumbnails. |
| 678 if (NTP_DESIGN.thumbnailFallback) { |
| 679 var fallbackElem = createAndAppendElement( |
| 680 innerElem, 'div', CLASSES.THUMBNAIL_FALLBACK); |
| 681 if (NTP_DESIGN.thumbnailFallback === THUMBNAIL_FALLBACK.DOT) |
| 682 createAndAppendElement(fallbackElem, 'div', CLASSES.DOT); |
| 683 } |
623 | 684 |
624 // The iframe which renders either a thumbnail or domain element. | 685 // The iframe which renders either a thumbnail or domain element. |
625 var thumbnailElement = document.createElement('iframe'); | 686 var thumbnailElem = document.createElement('iframe'); |
626 thumbnailElement.tabIndex = '-1'; | 687 thumbnailElem.tabIndex = '-1'; |
627 // Keep this ID here. See comment above. | 688 // Keep this ID here. See comment above. |
628 thumbnailElement.id = 'thumb-' + rid; | 689 thumbnailElem.id = 'thumb-' + rid; |
629 thumbnailElement.className = CLASSES.THUMBNAIL; | 690 thumbnailElem.className = CLASSES.THUMBNAIL; |
630 thumbnailElement.hidden = true; | 691 thumbnailElem.src = getMostVisitedThumbnailIframeUrl(rid, position); |
631 thumbnailElement.src = getMostVisitedThumbnailIframeUrl(rid, position); | 692 innerElem.appendChild(thumbnailElem); |
632 tileElement.appendChild(thumbnailElement); | |
633 | |
634 // A mask to darken the thumbnail on focus. | |
635 var maskElement = createAndAppendElement( | |
636 tileElement, 'div', CLASSES.THUMBNAIL_MASK); | |
637 | 693 |
638 // The button used to blacklist this page. | 694 // The button used to blacklist this page. |
639 var blacklistButton = createAndAppendElement( | 695 var blacklistButton = createAndAppendElement( |
640 tileElement, 'div', CLASSES.BLACKLIST_BUTTON); | 696 innerElem, 'div', CLASSES.BLACKLIST_BUTTON); |
641 var blacklistFunction = generateBlacklistFunction(rid); | 697 var blacklistFunction = generateBlacklistFunction(rid); |
642 blacklistButton.addEventListener('click', blacklistFunction); | 698 blacklistButton.addEventListener('click', blacklistFunction); |
643 blacklistButton.title = configData.translatedStrings.removeThumbnailTooltip; | 699 blacklistButton.title = configData.translatedStrings.removeThumbnailTooltip; |
644 | 700 |
| 701 // A helper mask on top of the tile that is used to create hover border |
| 702 // and/or to darken the thumbnail on focus. |
| 703 var maskElement = createAndAppendElement( |
| 704 innerElem, 'div', CLASSES.THUMBNAIL_MASK); |
| 705 |
645 // When a tile is focused, have delete also blacklist the page. | 706 // When a tile is focused, have delete also blacklist the page. |
646 registerKeyHandler(tileElement, KEYCODE.DELETE, blacklistFunction); | 707 registerKeyHandler(tileElem, KEYCODE.DELETE, blacklistFunction); |
647 | 708 |
648 // The page favicon, if any. | 709 // The page favicon, or a fallback. |
649 var faviconUrl = page.faviconUrl; | 710 var favicon = createAndAppendElement(innerElem, 'div', CLASSES.FAVICON); |
650 if (faviconUrl) { | 711 if (page.faviconUrl) { |
651 var favicon = createAndAppendElement(tileElement, 'div', CLASSES.FAVICON); | 712 favicon.style.backgroundImage = 'url(' + page.faviconUrl + ')'; |
652 favicon.style.backgroundImage = 'url(' + faviconUrl + ')'; | 713 } else { |
| 714 favicon.classList.add(CLASSES.FAVICON_FALLBACK); |
653 } | 715 } |
654 return new Tile(tileElement, titleElement, thumbnailElement, rid); | 716 return new Tile(tileElem, innerElem, titleElem, thumbnailElem, rid); |
655 } else { | 717 } else { |
656 return new Tile(tileElement); | 718 return new Tile(tileElem); |
657 } | 719 } |
658 } | 720 } |
659 | 721 |
660 | 722 |
661 /** | 723 /** |
662 * Generates a function to be called when the page with the corresponding RID | 724 * Generates a function to be called when the page with the corresponding RID |
663 * is blacklisted. | 725 * is blacklisted. |
664 * @param {number} rid The RID of the page being blacklisted. | 726 * @param {number} rid The RID of the page being blacklisted. |
665 * @return {function(Event)} A function which handles the blacklisting of the | 727 * @return {function(Event)} A function which handles the blacklisting of the |
666 * page by updating state variables and notifying Chrome. | 728 * page by updating state variables and notifying Chrome. |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
740 if (newNumColumns < MIN_NUM_COLUMNS) | 802 if (newNumColumns < MIN_NUM_COLUMNS) |
741 newNumColumns = MIN_NUM_COLUMNS; | 803 newNumColumns = MIN_NUM_COLUMNS; |
742 else if (newNumColumns > MAX_NUM_COLUMNS) | 804 else if (newNumColumns > MAX_NUM_COLUMNS) |
743 newNumColumns = MAX_NUM_COLUMNS; | 805 newNumColumns = MAX_NUM_COLUMNS; |
744 | 806 |
745 if (numColumnsShown != newNumColumns) { | 807 if (numColumnsShown != newNumColumns) { |
746 numColumnsShown = newNumColumns; | 808 numColumnsShown = newNumColumns; |
747 var tilesContainerWidth = numColumnsShown * tileRequiredWidth; | 809 var tilesContainerWidth = numColumnsShown * tileRequiredWidth; |
748 tilesContainer.style.width = tilesContainerWidth + 'px'; | 810 tilesContainer.style.width = tilesContainerWidth + 'px'; |
749 if (fakebox) { | 811 if (fakebox) { |
750 // -2 to account for border. | 812 fakebox.style.width = // -2 to account for border. |
751 fakebox.style.width = | |
752 (tilesContainerWidth - NTP_DESIGN.tileMargin - 2) + 'px'; | 813 (tilesContainerWidth - NTP_DESIGN.tileMargin - 2) + 'px'; |
753 } | 814 } |
754 // Render without clearing tiles. | 815 // Render without clearing tiles. |
755 renderAndShowTiles(); | 816 renderAndShowTiles(); |
756 } | 817 } |
757 } | 818 } |
758 | 819 |
759 | 820 |
760 /** | 821 /** |
761 * Returns the tile corresponding to the specified page RID. | 822 * Returns the tile corresponding to the specified page RID. |
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
929 notification = $(IDS.NOTIFICATION); | 990 notification = $(IDS.NOTIFICATION); |
930 attribution = $(IDS.ATTRIBUTION); | 991 attribution = $(IDS.ATTRIBUTION); |
931 ntpContents = $(IDS.NTP_CONTENTS); | 992 ntpContents = $(IDS.NTP_CONTENTS); |
932 | 993 |
933 if (configData.isGooglePage) { | 994 if (configData.isGooglePage) { |
934 var logo = document.createElement('div'); | 995 var logo = document.createElement('div'); |
935 logo.id = IDS.LOGO; | 996 logo.id = IDS.LOGO; |
936 | 997 |
937 fakebox = document.createElement('div'); | 998 fakebox = document.createElement('div'); |
938 fakebox.id = IDS.FAKEBOX; | 999 fakebox.id = IDS.FAKEBOX; |
939 fakebox.innerHTML = | 1000 var fakeboxHtml = []; |
940 '<input id="' + IDS.FAKEBOX_INPUT + | 1001 fakeboxHtml.push('<input id="' + IDS.FAKEBOX_INPUT + |
941 '" autocomplete="off" tabindex="-1" aria-hidden="true">' + | 1002 '" autocomplete="off" tabindex="-1" aria-hidden="true">'); |
942 '<div id="cursor"></div>'; | 1003 if (NTP_DESIGN.showFakeboxHint && configData.searchboxPlaceholder) { |
| 1004 fakeboxHtml.push('<div id="' + IDS.FAKEBOX_TEXT + '">' + |
| 1005 configData.searchboxPlaceholder + '</div>'); |
| 1006 } |
| 1007 fakeboxHtml.push('<div id="cursor"></div>'); |
| 1008 fakebox.innerHTML = fakeboxHtml.join(''); |
943 | 1009 |
944 ntpContents.insertBefore(fakebox, ntpContents.firstChild); | 1010 ntpContents.insertBefore(fakebox, ntpContents.firstChild); |
945 ntpContents.insertBefore(logo, ntpContents.firstChild); | 1011 ntpContents.insertBefore(logo, ntpContents.firstChild); |
946 } else { | 1012 } else { |
947 document.body.classList.add(CLASSES.NON_GOOGLE_PAGE); | 1013 document.body.classList.add(CLASSES.NON_GOOGLE_PAGE); |
948 } | 1014 } |
949 | 1015 |
950 var notificationMessage = $(IDS.NOTIFICATION_MESSAGE); | 1016 var notificationMessage = $(IDS.NOTIFICATION_MESSAGE); |
951 notificationMessage.textContent = | 1017 notificationMessage.textContent = |
952 configData.translatedStrings.thumbnailRemovedNotification; | 1018 configData.translatedStrings.thumbnailRemovedNotification; |
(...skipping 23 matching lines...) Expand all Loading... |
976 ntpApiHandle = topLevelHandle.newTabPage; | 1042 ntpApiHandle = topLevelHandle.newTabPage; |
977 ntpApiHandle.onthemechange = onThemeChange; | 1043 ntpApiHandle.onthemechange = onThemeChange; |
978 ntpApiHandle.onmostvisitedchange = onMostVisitedChange; | 1044 ntpApiHandle.onmostvisitedchange = onMostVisitedChange; |
979 | 1045 |
980 ntpApiHandle.oninputstart = onInputStart; | 1046 ntpApiHandle.oninputstart = onInputStart; |
981 ntpApiHandle.oninputcancel = restoreNtp; | 1047 ntpApiHandle.oninputcancel = restoreNtp; |
982 | 1048 |
983 if (ntpApiHandle.isInputInProgress) | 1049 if (ntpApiHandle.isInputInProgress) |
984 onInputStart(); | 1050 onInputStart(); |
985 | 1051 |
986 onThemeChange(); | 1052 renderTheme(); |
987 onMostVisitedChange(); | 1053 onMostVisitedChange(); |
988 | 1054 |
989 searchboxApiHandle = topLevelHandle.searchBox; | 1055 searchboxApiHandle = topLevelHandle.searchBox; |
990 | 1056 |
991 if (fakebox) { | 1057 if (fakebox) { |
992 // Listener for updating the key capture state. | 1058 // Listener for updating the key capture state. |
993 document.body.onmousedown = function(event) { | 1059 document.body.onmousedown = function(event) { |
994 if (isFakeboxClick(event)) | 1060 if (isFakeboxClick(event)) |
995 searchboxApiHandle.startCapturingKeyStrokes(); | 1061 searchboxApiHandle.startCapturingKeyStrokes(); |
996 else if (isFakeboxFocused()) | 1062 else if (isFakeboxFocused()) |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1043 | 1109 |
1044 return { | 1110 return { |
1045 init: init, | 1111 init: init, |
1046 listen: listen | 1112 listen: listen |
1047 }; | 1113 }; |
1048 } | 1114 } |
1049 | 1115 |
1050 if (!window.localNTPUnitTest) { | 1116 if (!window.localNTPUnitTest) { |
1051 LocalNTP().listen(); | 1117 LocalNTP().listen(); |
1052 } | 1118 } |
OLD | NEW |