Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Unified Diff: chrome/browser/resources/local_ntp/local_ntp.js

Issue 12840003: Implement local NTP for fallback. (Closed) Base URL: https://git.chromium.org/chromium/src.git@master
Patch Set: Fix compile. Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/resources/local_ntp/local_ntp.html ('k') | chrome/browser/search/instant_service.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
new file mode 100644
index 0000000000000000000000000000000000000000..550e5e1af19cac1f173ca2df13b4f96202e08a15
--- /dev/null
+++ b/chrome/browser/resources/local_ntp/local_ntp.js
@@ -0,0 +1,519 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+/**
+ * The element used to vertically position the most visited section on
+ * window resize.
+ * @type {Element}
+ */
+var topMarginElement;
+
+/**
+ * The container for the tile elements.
+ * @type {Element}
+ */
+var tilesContainer;
+
+/**
+ * The notification displayed when a page is blacklisted.
+ * @type {Element}
+ */
+var notification;
+
+/**
+ * The array of rendered tiles, ordered by appearance.
+ * @type {Array.<Tile>}
+ */
+var tiles = [];
+
+/**
+ * The last blacklisted tile if any, which by definition should not be filler.
+ * @type {?Tile}
+ */
+var lastBlacklistedTile = null;
+
+/**
+ * The index of the last blacklisted tile, if any. Used to determine where to
+ * re-insert a tile on undo.
+ * @type {number}
+ */
+var lastBlacklistedIndex = -1;
+
+/**
+ * True if a page has been blacklisted and we're waiting on the
+ * onmostvisitedchange callback. See onMostVisitedChange() for how this
+ * is used.
+ * @type {boolean}
+ */
+var isBlacklisting = false;
+
+/**
+ * True if a blacklist has been undone and we're waiting on the
+ * onmostvisitedchange callback. See onMostVisitedChange() for how this
+ * is used.
+ * @type {boolean}
+ */
+var isUndoing = false;
+
+/**
+ * Current number of tiles shown based on the window width, including filler.
+ * @type {number}
+ */
+var numTilesShown = 0;
+
+/**
+ * The browser embeddedSearch.newTabPage object.
+ * @type {Object}
+ */
+var apiHandle;
+
+/**
+ * Possible background-colors of a non-custom theme. Used to determine whether
+ * the homepage should be updated to support custom or non-custom themes.
+ * @type {!Array.<string>}
+ * @const
+ */
+var WHITE = ['rgba(255,255,255,1)', 'rgba(0,0,0,0)'];
+
+/**
+ * Should be equal to mv-tile's -webkit-margin-start + width.
+ * @type {number}
+ * @const
+ */
+var TILE_WIDTH = 160;
+
+/**
+ * The height of the most visited section.
+ * @type {number}
+ * @const
+ */
+var MOST_VISITED_HEIGHT = 156;
+
+/** @type {number} @const */
+var MAX_NUM_TILES_TO_SHOW = 4;
+
+/** @type {number} @const */
+var MIN_NUM_TILES_TO_SHOW = 2;
+
+/**
+ * Minimum total padding to give to the left and right of the most visited
+ * section. Used to determine how many tiles to show.
+ * @type {number}
+ * @const
+ */
+var MIN_TOTAL_HORIZONTAL_PADDING = 188;
+
+/**
+ * Enum for classnames.
+ * @enum {string}
+ * @const
+ */
+var CLASSES = {
+ TILE: 'mv-tile',
+ PAGE: 'mv-page', // page tiles
+ TITLE: 'mv-title',
+ THUMBNAIL: 'mv-thumb',
+ DOMAIN: 'mv-domain',
+ BLACKLIST_BUTTON: 'mv-x',
+ FAVICON: 'mv-favicon',
+ FILLER: 'mv-filler', // filler tiles
+ BLACKLIST: 'mv-blacklist', // triggers tile blacklist animation
+ HIDE_TILE: 'mv-tile-hide', // hides tiles on small browser width
+ HIDE_BLACKLIST_BUTTON: 'mv-x-hide', // hides blacklist button during animation
+ DELAYED_HIDE_NOTIFICATION: 'mv-notice-delayed-hide',
+ HIDE_NOTIFICATION: 'mv-notice-hide'
+};
+
+/**
+ * Enum for ids.
+ * @enum {string}
+ * @const
+ */
+var IDS = {
+ TOP_MARGIN: 'mv-top-margin',
+ TILES: 'mv-tiles',
+ NOTIFICATION: 'mv-notice',
+ NOTIFICATION_MESSAGE: 'mv-msg',
+ UNDO_LINK: 'mv-undo',
+ RESTORE_ALL_LINK: 'mv-restore',
+ NOTIFICATION_CLOSE_BUTTON: 'mv-notice-x'
+};
+
+/**
+ * A Tile is either a rendering of a Most Visited page or "filler" used to
+ * pad out the section when not enough pages exist.
+ *
+ * @param {Element} elem The element for rendering the tile.
+ * @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_rid) {
+ /** @type {Element} */
+ this.elem = elem;
+
+ /** @type {number|undefined} */
+ this.rid = opt_rid;
+}
+
+/**
+ * Updates the NTP based on the current theme.
+ * @private
+ */
+function onThemeChange() {
+ var info = apiHandle.themeBackgroundInfo;
+ if (!info)
+ return;
+ var background = [info.colorRgba,
+ info.imageUrl,
+ info.imageTiling,
+ info.imageHorizontalAlignment,
+ info.imageVerticalAlignment].join(' ').trim();
+ document.body.style.background = background;
+ var isCustom = !!background && WHITE.indexOf(background) == -1;
+ document.body.classList.toggle('custom-theme', isCustom);
+}
+
+/**
+ * Handles a new set of Most Visited page data.
+ */
+function onMostVisitedChange() {
+ var pages = apiHandle.mostVisited;
+
+ if (isBlacklisting) {
+ // If this was called as a result of a blacklist, add a new replacement
+ // (possibly filler) tile at the end and trigger the blacklist animation.
+ var replacementTile = createTile(pages[MAX_NUM_TILES_TO_SHOW - 1]);
+
+ tiles.push(replacementTile);
+ tilesContainer.appendChild(replacementTile.elem);
+
+ var lastBlacklistedTileElement = lastBlacklistedTile.elem;
+ lastBlacklistedTileElement.addEventListener(
+ 'webkitTransitionEnd', blacklistAnimationDone);
+ lastBlacklistedTileElement.classList.add(CLASSES.BLACKLIST);
+ // In order to animate the replacement tile sliding into place, it must
+ // be made visible.
+ updateTileVisibility(numTilesShown + 1);
+
+ } else if (isUndoing) {
+ // If this was called as a result of an undo, re-insert the last blacklisted
+ // tile in its old location and trigger the undo animation.
+ tiles.splice(
+ lastBlacklistedIndex, 0, lastBlacklistedTile);
+ var lastBlacklistedTileElement = lastBlacklistedTile.elem;
+ tilesContainer.insertBefore(
+ lastBlacklistedTileElement,
+ tilesContainer.childNodes[lastBlacklistedIndex]);
+ lastBlacklistedTileElement.addEventListener(
+ 'webkitTransitionEnd', undoAnimationDone);
+ // Force the removal to happen synchronously.
+ lastBlacklistedTileElement.scrollTop;
+ lastBlacklistedTileElement.classList.remove(CLASSES.BLACKLIST);
+ } else {
+ // Otherwise render the tiles using the new data without animation.
+ tiles = [];
+ for (var i = 0; i < MAX_NUM_TILES_TO_SHOW; ++i) {
+ tiles.push(createTile(pages[i]));
+ }
+ renderTiles();
+ }
+}
+
+/**
+ * Renders the current set of tiles without animation.
+ */
+function renderTiles() {
+ removeChildren(tilesContainer);
+ for (var i = 0, length = tiles.length; i < length; ++i) {
+ tilesContainer.appendChild(tiles[i].elem);
+ }
+}
+
+/**
+ * Creates a Tile with the specified page data. If no data is provided, a
+ * filler Tile is created.
+ * @param {Object} page The page data.
+ * @return {Tile} The new Tile.
+ */
+function createTile(page) {
+ var tileElement = document.createElement('div');
+ tileElement.classList.add(CLASSES.TILE);
+
+ if (page) {
+ var rid = page.rid;
+ tileElement.classList.add(CLASSES.PAGE);
+
+ // The click handler for navigating to the page identified by the RID.
+ tileElement.addEventListener('click', function() {
+ apiHandle.navigateContentWindow(rid);
+ });
+
+ // The shadow DOM which renders the page title.
+ var titleElement = page.titleElement;
+ if (titleElement) {
+ titleElement.classList.add(CLASSES.TITLE);
+ tileElement.appendChild(titleElement);
+ }
+
+ // Render the thumbnail if present. Otherwise, fall back to a shadow DOM
+ // which renders the domain.
+ var thumbnailUrl = page.thumbnailUrl;
+
+ var showDomainElement = function() {
+ var domainElement = page.domainElement;
+ if (domainElement) {
+ domainElement.classList.add(CLASSES.DOMAIN);
+ tileElement.appendChild(domainElement);
+ }
+ };
+ if (thumbnailUrl) {
+ var image = new Image();
+ image.onload = function() {
+ var thumbnailElement = createAndAppendElement(
+ tileElement, 'div', CLASSES.THUMBNAIL);
+ thumbnailElement.style.backgroundImage = 'url(' + thumbnailUrl + ')';
+ };
+
+ image.onerror = showDomainElement;
+ image.src = thumbnailUrl;
+ } else {
+ showDomainElement();
+ }
+
+ // The button used to blacklist this page.
+ var blacklistButton = createAndAppendElement(
+ tileElement, 'div', CLASSES.BLACKLIST_BUTTON);
+ blacklistButton.addEventListener('click', generateBlacklistFunction(rid));
+ // TODO(jeremycho): i18n. See http://crbug.com/190223.
+ blacklistButton.title = "Don't show on this page";
+
+ // The page favicon, if any.
+ var faviconUrl = page.faviconUrl;
+ if (faviconUrl) {
+ var favicon = createAndAppendElement(
+ tileElement, 'div', CLASSES.FAVICON);
+ favicon.style.backgroundImage = 'url(' + faviconUrl + ')';
+ }
+ return new Tile(tileElement, rid);
+ } else {
+ tileElement.classList.add(CLASSES.FILLER);
+ return new Tile(tileElement);
+ }
+}
+
+/**
+ * Generates a function to be called when the page with the corresponding RID
+ * is blacklisted.
+ * @param {number} rid The RID of the page being blacklisted.
+ * @return {function(Event)} A function which handles the blacklisting of the
+ * page by displaying the notification, updating state variables, and
+ * notifying Chrome.
+ */
+function generateBlacklistFunction(rid) {
+ return function(e) {
+ // Prevent navigation when the page is being blacklisted.
+ e.stopPropagation();
+
+ showNotification();
+ isBlacklisting = true;
+ tilesContainer.classList.add(CLASSES.HIDE_BLACKLIST_BUTTON);
+ lastBlacklistedTile = getTileByRid(rid);
+ lastBlacklistedIndex = tiles.indexOf(lastBlacklistedTile);
+ apiHandle.deleteMostVisitedItem(rid);
+ };
+}
+
+/**
+ * Shows the blacklist notification and triggers a delay to hide it.
+ */
+function showNotification() {
+ notification.classList.remove(CLASSES.HIDE_NOTIFICATION);
+ notification.classList.remove(CLASSES.DELAYED_HIDE_NOTIFICATION);
+ notification.scrollTop;
+ notification.classList.add(CLASSES.DELAYED_HIDE_NOTIFICATION);
+}
+
+/**
+ * Hides the blacklist notification.
+ */
+function hideNotification() {
+ notification.classList.add(CLASSES.HIDE_NOTIFICATION);
+}
+
+/**
+ * Handles the end of the blacklist animation by removing the blacklisted tile.
+ */
+function blacklistAnimationDone() {
+ tiles.splice(lastBlacklistedIndex, 1);
+ removeNode(lastBlacklistedTile.elem);
+ updateTileVisibility(numTilesShown);
+ isBlacklisting = false;
+ tilesContainer.classList.remove(CLASSES.HIDE_BLACKLIST_BUTTON);
+ lastBlacklistedTile.elem.removeEventListener(
+ 'webkitTransitionEnd', blacklistAnimationDone);
+}
+
+/**
+ * Handles a click on the notification undo link by hiding the notification and
+ * informing Chrome.
+ */
+function onUndo() {
+ hideNotification();
+ var lastBlacklistedRID = lastBlacklistedTile.rid;
+ if (typeof lastBlacklistedRID != 'undefined') {
+ isUndoing = true;
+ apiHandle.undoMostVisitedDeletion(lastBlacklistedRID);
+ }
+}
+
+/**
+ * Handles the end of the undo animation by removing the extraneous end tile.
+ */
+function undoAnimationDone() {
+ isUndoing = false;
+ tiles.splice(tiles.length - 1, 1);
+ removeNode(tilesContainer.lastElementChild);
+ updateTileVisibility(numTilesShown);
+ lastBlacklistedTile.elem.removeEventListener(
+ 'webkitTransitionEnd', undoAnimationDone);
+}
+
+/**
+ * Handles a click on the restore all notification link by hiding the
+ * notification and informing Chrome.
+ */
+function onRestoreAll() {
+ hideNotification();
+ apiHandle.undoAllMostVisitedDeletions();
+}
+
+/**
+ * Handles a resize by vertically centering the most visited section
+ * and triggering the tile show/hide animation if necessary.
+ */
+function onResize() {
+ var clientHeight = document.documentElement.clientHeight;
+ topMarginElement.style.marginTop =
+ Math.max(0, (clientHeight - MOST_VISITED_HEIGHT) / 2) + 'px';
+
+ var clientWidth = document.documentElement.clientWidth;
+ var numTilesToShow = Math.floor(
+ (clientWidth - MIN_TOTAL_HORIZONTAL_PADDING) / TILE_WIDTH);
+ numTilesToShow = Math.max(MIN_NUM_TILES_TO_SHOW, numTilesToShow);
+ if (numTilesToShow != numTilesShown) {
+ updateTileVisibility(numTilesToShow);
+ numTilesShown = numTilesToShow;
+ }
+}
+
+/**
+ * Triggers an animation to show the first numTilesToShow tiles and hide the
+ * remaining.
+ * @param {number} numTilesToShow The number of tiles to show.
+ */
+function updateTileVisibility(numTilesToShow) {
+ for (var i = 0, length = tiles.length; i < length; ++i) {
+ tiles[i].elem.classList.toggle(CLASSES.HIDE_TILE, i >= numTilesToShow);
+ }
+}
+
+/**
+ * Returns the tile corresponding to the specified page RID.
+ * @param {number} rid The page RID being looked up.
+ * @return {Tile} The corresponding tile.
+ */
+function getTileByRid(rid) {
+ for (var i = 0, length = tiles.length; i < length; ++i) {
+ var tile = tiles[i];
+ if (tile.rid == rid)
+ return tile;
+ }
+ return null;
+}
+
+/**
+ * Utility function which creates an element with an optional classname and
+ * appends it to the specified parent.
+ * @param {Element} parent The parent to append the new element.
+ * @param {string} name The name of the new element.
+ * @param {string=} opt_class The optional classname of the new element.
+ * @return {Element} The new element.
+ */
+function createAndAppendElement(parent, name, opt_class) {
+ var child = document.createElement(name);
+ if (opt_class)
+ child.classList.add(opt_class);
+ parent.appendChild(child);
+ return child;
+}
+
+/**
+ * Removes a node from its parent.
+ * @param {Node} node The node to remove.
+ */
+function removeNode(node) {
+ node.parentNode.removeChild(node);
+}
+
+/**
+ * Removes all the child nodes on a DOM node.
+ * @param {Node} node Node to remove children from.
+ */
+function removeChildren(node) {
+ node.innerHTML = '';
+}
+
+/**
+ * @return {Object} the handle to the embeddedSearch API.
+ */
+function getEmbeddedSearchApiHandle() {
+ if (window.cideb)
+ return window.cideb;
+ if (window.chrome && window.chrome.embeddedSearch)
+ return window.chrome.embeddedSearch;
+ return null;
+}
+
+/**
+ * Prepares the New Tab Page by adding listeners, rendering the current
+ * theme, and the most visited pages section.
+ */
+function init() {
+ topMarginElement = document.getElementById(IDS.TOP_MARGIN);
+ tilesContainer = document.getElementById(IDS.TILES);
+ notification = document.getElementById(IDS.NOTIFICATION);
+
+ // TODO(jeremycho): i18n.
+ var notificationMessage = document.getElementById(IDS.NOTIFICATION_MESSAGE);
+ notificationMessage.innerText = 'Thumbnail removed.';
+ var undoLink = document.getElementById(IDS.UNDO_LINK);
+ undoLink.addEventListener('click', onUndo);
+ undoLink.innerText = 'Undo';
+ var restoreAllLink = document.getElementById(IDS.RESTORE_ALL_LINK);
+ restoreAllLink.addEventListener('click', onRestoreAll);
+ restoreAllLink.innerText = 'Restore all';
+ var notificationCloseButton =
+ document.getElementById(IDS.NOTIFICATION_CLOSE_BUTTON);
+ notificationCloseButton.addEventListener('click', hideNotification);
+
+ window.addEventListener('resize', onResize);
+ onResize();
+
+ var topLevelHandle = getEmbeddedSearchApiHandle();
+ // This is to inform Chrome that the NTP is instant-extended capable i.e.
+ // it should fire events like onmostvisitedchange.
+ topLevelHandle.searchBox.onsubmit = function() {};
+
+ apiHandle = topLevelHandle.newTabPage;
+ apiHandle.onthemechange = onThemeChange;
+ apiHandle.onmostvisitedchange = onMostVisitedChange;
+
+ onThemeChange();
+ onMostVisitedChange();
+}
+
+document.addEventListener('DOMContentLoaded', init);
+})();
« no previous file with comments | « chrome/browser/resources/local_ntp/local_ntp.html ('k') | chrome/browser/search/instant_service.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698