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