Chromium Code Reviews| Index: chrome/browser/resources/local_ntp/local_ntp.js |
| diff --git a/chrome/browser/resources/local_ntp/local_ntp.js b/chrome/browser/resources/local_ntp/local_ntp.js |
| index 9aaa9a814bb1b7410f2e92e39f7db5a0e03bb55f..c8f499c13bb09c73924df4e8fdb0a2c8a58ec819 100644 |
| --- a/chrome/browser/resources/local_ntp/local_ntp.js |
| +++ b/chrome/browser/resources/local_ntp/local_ntp.js |
| @@ -28,7 +28,9 @@ var CLASSES = { |
| ALTERNATE_LOGO: 'alternate-logo', // Shows white logo if required by theme |
| BLACKLIST: 'mv-blacklist', // triggers tile blacklist animation |
| BLACKLIST_BUTTON: 'mv-x', |
| + DARK: 'dark', |
| DELAYED_HIDE_NOTIFICATION: 'mv-notice-delayed-hide', |
| + DOT: 'dot', |
| FAKEBOX_DISABLE: 'fakebox-disable', // Makes fakebox non-interactive |
| FAKEBOX_FOCUS: 'fakebox-focused', // Applies focus styles to the fakebox |
| // Applies drag focus style to the fakebox |
| @@ -43,8 +45,10 @@ var CLASSES = { |
| PAGE_READY: 'mv-page-ready', // page tile when ready |
| RTL: 'rtl', // Right-to-left language text. |
| THUMBNAIL: 'mv-thumb', |
| + THUMBNAIL_FALLBACK: 'mv-thumb-fallback', |
| THUMBNAIL_MASK: 'mv-mask', |
| TILE: 'mv-tile', |
| + TILE_INNER: 'mv-tile-inner', |
| TITLE: 'mv-title' |
| }; |
| @@ -169,6 +173,13 @@ var isBlacklisting = false; |
| /** |
| + * Stores whether the current theme has a dark background. |
| + * @type {boolean} |
| + */ |
| +var isBackgroundDark = false; |
| + |
| + |
| +/** |
| * Current number of tiles columns shown based on the window width, including |
| * those that just contain filler. |
| * @type {number} |
| @@ -266,17 +277,21 @@ var MOST_VISITED_PAINT_TIMEOUT_MSEC = 500; |
| * pad out the section when not enough pages exist. |
| * |
| * @param {Element} elem The element for rendering the tile. |
| + * @param {Element=} opt_innerElem The element for contents of tile. |
| * @param {Element=} opt_titleElem The element for rendering the title. |
| * @param {Element=} opt_thumbnailElem The element for rendering the thumbnail. |
| * @param {number=} opt_rid The RID for the corresponding Most Visited page. |
| * Should only be left unspecified when creating a filler tile. |
| * @constructor |
| */ |
| -function Tile(elem, opt_titleElem, opt_thumbnailElem, opt_rid) { |
| +function Tile(elem, opt_innerElem, opt_titleElem, opt_thumbnailElem, opt_rid) { |
| /** @type {Element} */ |
| this.elem = elem; |
| /** @type {Element|undefined} */ |
| + this.innerElem = opt_innerElem; |
| + |
| + /** @type {Element|undefined} */ |
| this.titleElem = opt_titleElem; |
| /** @type {Element|undefined} */ |
| @@ -288,13 +303,33 @@ function Tile(elem, opt_titleElem, opt_thumbnailElem, opt_rid) { |
| /** |
| + * Determines whether a theme should be considered to have dark background. |
| + * @param {ThemeBackgroundInfo} info Theme background information. |
| + * @return {boolean} Whether the theme has dark background. |
| + * @private |
| + */ |
| +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
|
| + if (info.imageUrl) |
| + return true; |
| + var rgba = info.backgroundColorRgba; |
| + var luminance = 0.3 * rgba[0] + 0.59 * rgba[1] + 0.11 * rgba[2]; |
| + return luminance < 128; |
| +} |
| + |
| + |
| +/** |
| * Updates the NTP based on the current theme. |
| * @private |
| */ |
| -function onThemeChange() { |
| +function renderTheme() { |
| var info = ntpApiHandle.themeBackgroundInfo; |
| - if (!info) |
| + if (!info) { |
| + isBackgroundDark = false; |
| return; |
| + } |
| + |
| + isBackgroundDark = getIsBackgroundDark(info); |
| + ntpContents.classList.toggle(CLASSES.DARK, isBackgroundDark); |
| var background = [convertToRGBAColor(info.backgroundColorRgba), |
| info.imageUrl, |
| @@ -305,7 +340,15 @@ function onThemeChange() { |
| document.body.classList.toggle(CLASSES.ALTERNATE_LOGO, info.alternateLogo); |
| updateThemeAttribution(info.attributionUrl); |
| setCustomThemeStyle(info); |
| +} |
| + |
| +/** |
| + * Updates the NTP based on the current theme, then rerenders all tiles. |
| + * @private |
| + */ |
| +function onThemeChange() { |
| + renderTheme(); |
| tilesContainer.innerHTML = ''; |
| renderAndShowTiles(); |
| } |
| @@ -412,10 +455,10 @@ function convertToRGBAColor(color) { |
| function onMostVisitedChange() { |
| if (isBlacklisting) { |
| // Trigger the blacklist animation, which then triggers reloadAllTiles(). |
| - var lastBlacklistedTileElement = lastBlacklistedTile.elem; |
| - lastBlacklistedTileElement.addEventListener( |
| + var lastBlacklistedTileElem = lastBlacklistedTile.elem; |
| + lastBlacklistedTileElem.addEventListener( |
| 'webkitTransitionEnd', blacklistAnimationDone); |
| - lastBlacklistedTileElement.classList.add(CLASSES.BLACKLIST); |
| + lastBlacklistedTileElem.classList.add(CLASSES.BLACKLIST); |
| } else { |
| reloadAllTiles(); |
| } |
| @@ -457,22 +500,19 @@ function reloadAllTiles() { |
| /** |
| * Binds onload events for a tile's internal <iframe> elements. |
| * @param {Tile} tile The main tile to bind events to. |
| - * @param {Barrier} tileVisibilityBarrier A barrier to make tile visible the |
| - * moment all tiles are loaded. |
| + * @param {Barrier} tileVisibilityBarrier A barrier to make all tiles visible |
| + * the moment all tiles are loaded. |
| */ |
| function bindTileOnloadEvents(tile, tileVisibilityBarrier) { |
| if (tile.titleElem) { |
| tileVisibilityBarrier.add(); |
| tile.titleElem.onload = function() { |
| - tile.titleElem.hidden = false; |
| tileVisibilityBarrier.remove(); |
| }; |
| } |
| - |
| if (tile.thumbnailElem) { |
| tileVisibilityBarrier.add(); |
| tile.thumbnailElem.onload = function() { |
| - tile.thumbnailElem.hidden = false; |
| tile.elem.classList.add(CLASSES.PAGE_READY); |
| tileVisibilityBarrier.remove(); |
| }; |
| @@ -494,18 +534,21 @@ function renderAndShowTiles() { |
| // If we need to render new tiles, manage the visibility to hide intermediate |
| // load states of the <iframe>s. |
| if (numExisting < numDesired) { |
| - var tileVisibilityBarrier = new Barrier(function() { |
| - tilesContainer.style.visibility = 'visible'; |
| - }); |
| + var showAll = function() { |
| + for (var i = 0; i < numDesired; ++i) { |
| + if (tiles[i].titleElem || tiles[i].thumbnailElem) |
| + tiles[i].elem.classList.add(CLASSES.PAGE_READY); |
| + } |
| + }; |
| + var tileVisibilityBarrier = new Barrier(showAll); |
| if (!userInitiatedMostVisitedChange) { |
| // Make titleContainer invisible, but still taking up space. |
| // titleContainer becomes visible again (1) on timeout, or (2) when all |
| // tiles finish loading (using tileVisibilityBarrier). |
| - tilesContainer.style.visibility = 'hidden'; |
| window.setTimeout(function() { |
| tileVisibilityBarrier.cancel(); |
| - tilesContainer.style.visibility = 'visible'; |
| + showAll(); |
| }, MOST_VISITED_PAINT_TIMEOUT_MSEC); |
| } |
| userInitiatedMostVisitedChange = false; |
| @@ -516,8 +559,8 @@ function renderAndShowTiles() { |
| } |
| } |
| - // Show only the desired tiles. Not using .hidden because it does not work for |
| - // inline-block elements. |
| + // Show only the desired tiles. Note that .hidden does not work for |
| + // inline-block elements like tiles[i].elem. |
| for (var i = 0; i < numDesired; ++i) |
| tiles[i].elem.style.display = 'inline-block'; |
| // If |numDesired| < |numExisting| then hide extra tiles (e.g., this occurs |
| @@ -536,11 +579,13 @@ function renderAndShowTiles() { |
| function getMostVisitedTitleIframeUrl(rid, position) { |
| var url = 'chrome-search://most-visited/' + |
| encodeURIComponent(MOST_VISITED_TITLE_IFRAME); |
| + var titleColor = isBackgroundDark ? NTP_DESIGN.titleColorAgainstDark : |
| + NTP_DESIGN.titleColor; |
| var params = [ |
| 'rid=' + encodeURIComponent(rid), |
| 'f=' + encodeURIComponent(NTP_DESIGN.fontFamily), |
| 'fs=' + encodeURIComponent(NTP_DESIGN.fontSize), |
| - 'c=' + encodeURIComponent(NTP_DESIGN.titleColor), |
| + 'c=' + encodeURIComponent(titleColor), |
| 'pos=' + encodeURIComponent(position)]; |
| if (NTP_DESIGN.titleTextAlign) |
| params.push('ta=' + encodeURIComponent(NTP_DESIGN.titleTextAlign)); |
| @@ -565,6 +610,8 @@ function getMostVisitedThumbnailIframeUrl(rid, position) { |
| 'fs=' + encodeURIComponent(NTP_DESIGN.fontSize), |
| 'c=' + encodeURIComponent(NTP_DESIGN.thumbnailTextColor), |
| 'pos=' + encodeURIComponent(position)]; |
| + if (NTP_DESIGN.thumbnailFallback) |
| + params.push('tfb=1'); |
| return url + '?' + params.join('&'); |
| } |
| @@ -577,12 +624,13 @@ function getMostVisitedThumbnailIframeUrl(rid, position) { |
| * @return {Tile} The new Tile. |
| */ |
| function createTile(page, position) { |
| - var tileElement = document.createElement('div'); |
| - tileElement.classList.add(CLASSES.TILE); |
| + var tileElem = document.createElement('div'); |
| + tileElem.classList.add(CLASSES.TILE); |
| + var innerElem = createAndAppendElement(tileElem, 'div', CLASSES.TILE_INNER); |
| if (page) { |
| var rid = page.rid; |
| - tileElement.classList.add(CLASSES.PAGE); |
| + tileElem.classList.add(CLASSES.PAGE); |
| var navigateFunction = function(e) { |
| e.preventDefault(); |
| @@ -590,15 +638,15 @@ function createTile(page, position) { |
| }; |
| // The click handler for navigating to the page identified by the RID. |
| - tileElement.addEventListener('click', navigateFunction); |
| + tileElem.addEventListener('click', navigateFunction); |
| // Make thumbnails tab-accessible. |
| - tileElement.setAttribute('tabindex', '1'); |
| - registerKeyHandler(tileElement, KEYCODE.ENTER, navigateFunction); |
| + tileElem.setAttribute('tabindex', '1'); |
| + registerKeyHandler(tileElem, KEYCODE.ENTER, navigateFunction); |
| // The iframe which renders the page title. |
| - var titleElement = document.createElement('iframe'); |
| - titleElement.tabIndex = '-1'; |
| + var titleElem = document.createElement('iframe'); |
| + titleElem.tabIndex = '-1'; |
| // Why iframes have IDs: |
| // |
| @@ -615,45 +663,52 @@ function createTile(page, position) { |
| // TODO(jered): Find and fix the root (probably Blink) bug. |
| // Keep this ID here. See comment above. |
| - titleElement.id = 'title-' + rid; |
| - titleElement.className = CLASSES.TITLE; |
| - titleElement.hidden = true; |
| - titleElement.src = getMostVisitedTitleIframeUrl(rid, position); |
| - tileElement.appendChild(titleElement); |
| + titleElem.id = 'title-' + rid; |
| + titleElem.className = CLASSES.TITLE; |
| + titleElem.src = getMostVisitedTitleIframeUrl(rid, position); |
| + innerElem.appendChild(titleElem); |
| + |
| + // A fallback element for missing thumbnails. |
| + if (NTP_DESIGN.thumbnailFallback) { |
| + var fallbackElem = createAndAppendElement( |
| + innerElem, 'div', CLASSES.THUMBNAIL_FALLBACK); |
| + if (NTP_DESIGN.thumbnailFallback === THUMBNAIL_FALLBACK.DOT) |
| + createAndAppendElement(fallbackElem, 'div', CLASSES.DOT); |
| + } |
| // The iframe which renders either a thumbnail or domain element. |
| - var thumbnailElement = document.createElement('iframe'); |
| - thumbnailElement.tabIndex = '-1'; |
| + var thumbnailElem = document.createElement('iframe'); |
| + thumbnailElem.tabIndex = '-1'; |
| // Keep this ID here. See comment above. |
| - thumbnailElement.id = 'thumb-' + rid; |
| - thumbnailElement.className = CLASSES.THUMBNAIL; |
| - thumbnailElement.hidden = true; |
| - thumbnailElement.src = getMostVisitedThumbnailIframeUrl(rid, position); |
| - tileElement.appendChild(thumbnailElement); |
| - |
| - // A mask to darken the thumbnail on focus. |
| - var maskElement = createAndAppendElement( |
| - tileElement, 'div', CLASSES.THUMBNAIL_MASK); |
| + thumbnailElem.id = 'thumb-' + rid; |
| + thumbnailElem.className = CLASSES.THUMBNAIL; |
| + thumbnailElem.src = getMostVisitedThumbnailIframeUrl(rid, position); |
| + innerElem.appendChild(thumbnailElem); |
| // The button used to blacklist this page. |
| var blacklistButton = createAndAppendElement( |
| - tileElement, 'div', CLASSES.BLACKLIST_BUTTON); |
| + innerElem, 'div', CLASSES.BLACKLIST_BUTTON); |
| var blacklistFunction = generateBlacklistFunction(rid); |
| blacklistButton.addEventListener('click', blacklistFunction); |
| blacklistButton.title = configData.translatedStrings.removeThumbnailTooltip; |
| + // A helper mask on top of the tile that is used to create hover border |
| + // and/or to darken the thumbnail on focus. |
| + var maskElement = createAndAppendElement( |
| + innerElem, 'div', CLASSES.THUMBNAIL_MASK); |
| + |
| // When a tile is focused, have delete also blacklist the page. |
| - registerKeyHandler(tileElement, KEYCODE.DELETE, blacklistFunction); |
| + registerKeyHandler(tileElem, KEYCODE.DELETE, blacklistFunction); |
| // The page favicon, if any. |
| var faviconUrl = page.faviconUrl; |
| if (faviconUrl) { |
| - var favicon = createAndAppendElement(tileElement, 'div', CLASSES.FAVICON); |
| + var favicon = createAndAppendElement(innerElem, 'div', CLASSES.FAVICON); |
| favicon.style.backgroundImage = 'url(' + faviconUrl + ')'; |
| } |
| - return new Tile(tileElement, titleElement, thumbnailElement, rid); |
| + return new Tile(tileElem, innerElem, titleElem, thumbnailElem, rid); |
| } else { |
| - return new Tile(tileElement); |
| + return new Tile(tileElem); |
| } |
| } |
| @@ -747,9 +802,14 @@ function onResize() { |
| var tilesContainerWidth = numColumnsShown * tileRequiredWidth; |
| tilesContainer.style.width = tilesContainerWidth + 'px'; |
| if (fakebox) { |
| - // -2 to account for border. |
| - fakebox.style.width = |
| - (tilesContainerWidth - NTP_DESIGN.tileMargin - 2) + 'px'; |
| + if (NTP_DESIGN.name === 'smallScreen1') { |
| + // -2 to account for border. |
| + fakebox.style.width = (tilesContainerWidth - tileRequiredWidth - |
| + NTP_DESIGN.tileMargin - 2) + 'px'; |
| + } else { |
| + fakebox.style.width = |
| + (tilesContainerWidth - NTP_DESIGN.tileMargin - 2) + 'px'; |
| + } |
| } |
| // Render without clearing tiles. |
| renderAndShowTiles(); |
| @@ -930,16 +990,25 @@ function init() { |
| attribution = $(IDS.ATTRIBUTION); |
| ntpContents = $(IDS.NTP_CONTENTS); |
| + 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
|
| + ntpContents.classList.add(v); |
| + }); |
| + |
| if (configData.isGooglePage) { |
| var logo = document.createElement('div'); |
| logo.id = IDS.LOGO; |
| fakebox = document.createElement('div'); |
| fakebox.id = IDS.FAKEBOX; |
| - fakebox.innerHTML = |
| - '<input id="' + IDS.FAKEBOX_INPUT + |
| - '" autocomplete="off" tabindex="-1" aria-hidden="true">' + |
| - '<div id="cursor"></div>'; |
| + var fakeboxHtml = []; |
| + fakeboxHtml.push('<input id="' + IDS.FAKEBOX_INPUT + |
| + '" autocomplete="off" tabindex="-1" aria-hidden="true">'); |
| + if (NTP_DESIGN.showFakeboxHint && configData.searchboxPlaceholder) { |
| + fakeboxHtml.push('<div id="fakebox-text">' + |
| + configData.searchboxPlaceholder + '</div>'); |
| + } |
| + fakeboxHtml.push('<div id="cursor"></div>'); |
| + fakebox.innerHTML = fakeboxHtml.join(''); |
| ntpContents.insertBefore(fakebox, ntpContents.firstChild); |
| ntpContents.insertBefore(logo, ntpContents.firstChild); |
| @@ -983,7 +1052,7 @@ function init() { |
| if (ntpApiHandle.isInputInProgress) |
| onInputStart(); |
| - onThemeChange(); |
| + renderTheme(); |
| onMostVisitedChange(); |
| searchboxApiHandle = topLevelHandle.searchBox; |