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

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

Issue 2600683002: Run tools/clang-format-js on some of chrome/browser/resources/ (Closed)
Patch Set: hackhackhack Created 3 years, 11 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
Index: chrome/browser/resources/local_ntp/most_visited_single.js
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.js b/chrome/browser/resources/local_ntp/most_visited_single.js
index aba6f846b8be4c88f90cd88fb7985937fe93cd42..8298871290c383a5af6e568fa367d9cd286d7bb9 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.js
+++ b/chrome/browser/resources/local_ntp/most_visited_single.js
@@ -2,597 +2,612 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */
- // Single iframe for NTP tiles.
+// Single iframe for NTP tiles.
(function() {
-'use strict';
-
-
-/**
- * The different types of events that are logged from the NTP. This enum is
- * used to transfer information from the NTP JavaScript to the renderer and is
- * not used as a UMA enum histogram's logged value.
- * Note: Keep in sync with common/ntp_logging_events.h
- * @enum {number}
- * @const
- */
-var LOG_TYPE = {
- // All NTP Tiles have finished loading (successfully or failing).
- NTP_ALL_TILES_LOADED: 11,
-};
-
-
-/**
- * The different sources that an NTP tile can have.
- * Note: Keep in sync with components/ntp_tiles/ntp_tile_source.h
- * @enum {number}
- * @const
- */
-var NTPTileSource = {
- TOP_SITES: 0,
- SUGGESTIONS_SERVICE: 1,
- POPULAR: 3,
- WHITELIST: 4,
-};
-
-
-/**
- * Total number of tiles to show at any time. If the host page doesn't send
- * enough tiles, we fill them blank.
- * @const {number}
- */
-var NUMBER_OF_TILES = 8;
-
-
-/**
- * Whether to use icons instead of thumbnails.
- * @type {boolean}
- */
-var USE_ICONS = false;
-
-
-/**
- * Number of lines to display in titles.
- * @type {number}
- */
-var NUM_TITLE_LINES = 1;
-
-
-/**
- * The origin of this request.
- * @const {string}
- */
-var DOMAIN_ORIGIN = '{{ORIGIN}}';
-
-
-/**
- * Counter for DOM elements that we are waiting to finish loading.
- * @type {number}
- */
-var loadedCounter = 1;
-
-
-/**
- * DOM element containing the tiles we are going to present next.
- * Works as a double-buffer that is shown when we receive a "show" postMessage.
- * @type {Element}
- */
-var tiles = null;
-
-
-/**
- * List of parameters passed by query args.
- * @type {Object}
- */
-var queryArgs = {};
-
-
-/**
- * Log an event on the NTP.
- * @param {number} eventType Event from LOG_TYPE.
- */
-var logEvent = function(eventType) {
- chrome.embeddedSearch.newTabPage.logEvent(eventType);
-};
-
-/**
- * Log impression of an NTP tile.
- * @param {number} tileIndex Position of the tile, >= 0 and < NUMBER_OF_TILES.
- * @param {number} tileSource The source from NTPTileSource.
- */
-function logMostVisitedImpression(tileIndex, tileSource) {
- chrome.embeddedSearch.newTabPage.logMostVisitedImpression(tileIndex,
- tileSource);
-}
-
-/**
- * Log click on an NTP tile.
- * @param {number} tileIndex Position of the tile, >= 0 and < NUMBER_OF_TILES.
- * @param {number} tileSource The source from NTPTileSource.
- */
-function logMostVisitedNavigation(tileIndex, tileSource) {
- chrome.embeddedSearch.newTabPage.logMostVisitedNavigation(tileIndex,
- tileSource);
-}
-
-/**
- * Down counts the DOM elements that we are waiting for the page to load.
- * When we get to 0, we send a message to the parent window.
- * This is usually used as an EventListener of onload/onerror.
- */
-var countLoad = function() {
- loadedCounter -= 1;
- if (loadedCounter <= 0) {
- showTiles();
- logEvent(LOG_TYPE.NTP_ALL_TILES_LOADED);
- window.parent.postMessage({cmd: 'loaded'}, DOMAIN_ORIGIN);
- loadedCounter = 1;
+ 'use strict';
+
+
+ /**
+ * The different types of events that are logged from the NTP. This enum is
+ * used to transfer information from the NTP JavaScript to the renderer and is
+ * not used as a UMA enum histogram's logged value.
+ * Note: Keep in sync with common/ntp_logging_events.h
+ * @enum {number}
+ * @const
+ */
+ var LOG_TYPE = {
+ // All NTP Tiles have finished loading (successfully or failing).
+ NTP_ALL_TILES_LOADED: 11,
+ };
+
+
+ /**
+ * The different sources that an NTP tile can have.
+ * Note: Keep in sync with components/ntp_tiles/ntp_tile_source.h
+ * @enum {number}
+ * @const
+ */
+ var NTPTileSource = {
+ TOP_SITES: 0,
+ SUGGESTIONS_SERVICE: 1,
+ POPULAR: 3,
+ WHITELIST: 4,
+ };
+
+
+ /**
+ * Total number of tiles to show at any time. If the host page doesn't send
+ * enough tiles, we fill them blank.
+ * @const {number}
+ */
+ var NUMBER_OF_TILES = 8;
+
+
+ /**
+ * Whether to use icons instead of thumbnails.
+ * @type {boolean}
+ */
+ var USE_ICONS = false;
+
+
+ /**
+ * Number of lines to display in titles.
+ * @type {number}
+ */
+ var NUM_TITLE_LINES = 1;
+
+
+ /**
+ * The origin of this request.
+ * @const {string}
+ */
+ var DOMAIN_ORIGIN = '{{ORIGIN}}';
+
+
+ /**
+ * Counter for DOM elements that we are waiting to finish loading.
+ * @type {number}
+ */
+ var loadedCounter = 1;
+
+
+ /**
+ * DOM element containing the tiles we are going to present next.
+ * Works as a double-buffer that is shown when we receive a "show"
+ * postMessage.
+ * @type {Element}
+ */
+ var tiles = null;
+
+
+ /**
+ * List of parameters passed by query args.
+ * @type {Object}
+ */
+ var queryArgs = {};
+
+
+ /**
+ * Log an event on the NTP.
+ * @param {number} eventType Event from LOG_TYPE.
+ */
+ var logEvent = function(eventType) {
+ chrome.embeddedSearch.newTabPage.logEvent(eventType);
+ };
+
+ /**
+ * Log impression of an NTP tile.
+ * @param {number} tileIndex Position of the tile, >= 0 and < NUMBER_OF_TILES.
+ * @param {number} tileSource The source from NTPTileSource.
+ */
+ function logMostVisitedImpression(tileIndex, tileSource) {
+ chrome.embeddedSearch.newTabPage.logMostVisitedImpression(
+ tileIndex, tileSource);
}
-};
+ /**
+ * Log click on an NTP tile.
+ * @param {number} tileIndex Position of the tile, >= 0 and < NUMBER_OF_TILES.
+ * @param {number} tileSource The source from NTPTileSource.
+ */
+ function logMostVisitedNavigation(tileIndex, tileSource) {
+ chrome.embeddedSearch.newTabPage.logMostVisitedNavigation(
+ tileIndex, tileSource);
+ }
-/**
- * Handles postMessages coming from the host page to the iframe.
- * Mostly, it dispatches every command to handleCommand.
- */
-var handlePostMessage = function(event) {
- if (event.data instanceof Array) {
- for (var i = 0; i < event.data.length; ++i) {
- handleCommand(event.data[i]);
+ /**
+ * Down counts the DOM elements that we are waiting for the page to load.
+ * When we get to 0, we send a message to the parent window.
+ * This is usually used as an EventListener of onload/onerror.
+ */
+ var countLoad = function() {
+ loadedCounter -= 1;
+ if (loadedCounter <= 0) {
+ showTiles();
+ logEvent(LOG_TYPE.NTP_ALL_TILES_LOADED);
+ window.parent.postMessage({cmd: 'loaded'}, DOMAIN_ORIGIN);
+ loadedCounter = 1;
}
- } else {
- handleCommand(event.data);
- }
-};
-
-
-/**
- * Handles a single command coming from the host page to the iframe.
- * We try to keep the logic here to a minimum and just dispatch to the relevant
- * functions.
- */
-var handleCommand = function(data) {
- var cmd = data.cmd;
-
- if (cmd == 'tile') {
- addTile(data);
- } else if (cmd == 'show') {
- countLoad();
- hideOverflowTiles(data);
- } else if (cmd == 'updateTheme') {
- updateTheme(data);
- } else if (cmd == 'tilesVisible') {
- hideOverflowTiles(data);
- } else {
- console.error('Unknown command: ' + JSON.stringify(data));
- }
-};
+ };
-var updateTheme = function(info) {
- var themeStyle = [];
+ /**
+ * Handles postMessages coming from the host page to the iframe.
+ * Mostly, it dispatches every command to handleCommand.
+ */
+ var handlePostMessage = function(event) {
+ if (event.data instanceof Array) {
+ for (var i = 0; i < event.data.length; ++i) {
+ handleCommand(event.data[i]);
+ }
+ } else {
+ handleCommand(event.data);
+ }
+ };
- if (info.tileBorderColor) {
- themeStyle.push('.thumb-ntp .mv-tile {' +
- 'border: 1px solid ' + info.tileBorderColor + '; }');
- }
- if (info.tileHoverBorderColor) {
- themeStyle.push('.thumb-ntp .mv-tile:hover {' +
- 'border-color: ' + info.tileHoverBorderColor + '; }');
- }
- if (info.isThemeDark) {
- themeStyle.push('.thumb-ntp .mv-tile, .thumb-ntp .mv-empty-tile { ' +
- 'background: rgb(51,51,51); }');
- themeStyle.push('.thumb-ntp .mv-thumb.failed-img { ' +
- 'background-color: #555; }');
- themeStyle.push('.thumb-ntp .mv-thumb.failed-img::after { ' +
- 'border-color: #333; }');
- themeStyle.push('.thumb-ntp .mv-x { ' +
- 'background: linear-gradient(to left, ' +
- 'rgb(51,51,51) 60%, transparent); }');
- themeStyle.push('html[dir=rtl] .thumb-ntp .mv-x { ' +
- 'background: linear-gradient(to right, ' +
- 'rgb(51,51,51) 60%, transparent); }');
- themeStyle.push('.thumb-ntp .mv-x::after { ' +
- 'background-color: rgba(255,255,255,0.7); }');
- themeStyle.push('.thumb-ntp .mv-x:hover::after { ' +
- 'background-color: #fff; }');
- themeStyle.push('.thumb-ntp .mv-x:active::after { ' +
- 'background-color: rgba(255,255,255,0.5); }');
- themeStyle.push('.icon-ntp .mv-tile:focus { ' +
- 'background: rgba(255,255,255,0.2); }');
- }
- if (info.tileTitleColor) {
- themeStyle.push('body { color: ' + info.tileTitleColor + '; }');
- }
- document.querySelector('#custom-theme').textContent = themeStyle.join('\n');
-};
+ /**
+ * Handles a single command coming from the host page to the iframe.
+ * We try to keep the logic here to a minimum and just dispatch to the
+ * relevant
+ * functions.
+ */
+ var handleCommand = function(data) {
+ var cmd = data.cmd;
+ if (cmd == 'tile') {
+ addTile(data);
+ } else if (cmd == 'show') {
+ countLoad();
+ hideOverflowTiles(data);
+ } else if (cmd == 'updateTheme') {
+ updateTheme(data);
+ } else if (cmd == 'tilesVisible') {
+ hideOverflowTiles(data);
+ } else {
+ console.error('Unknown command: ' + JSON.stringify(data));
+ }
+ };
-/**
- * Hides extra tiles that don't fit on screen.
- */
-var hideOverflowTiles = function(data) {
- var tileAndEmptyTileList = document.querySelectorAll(
- '#mv-tiles .mv-tile,#mv-tiles .mv-empty-tile');
- for (var i = 0; i < tileAndEmptyTileList.length; ++i) {
- tileAndEmptyTileList[i].classList.toggle('hidden', i >= data.maxVisible);
- }
-};
+ var updateTheme = function(info) {
+ var themeStyle = [];
-/**
- * Removes all old instances of #mv-tiles that are pending for deletion.
- */
-var removeAllOldTiles = function() {
- var parent = document.querySelector('#most-visited');
- var oldList = parent.querySelectorAll('.mv-tiles-old');
- for (var i = 0; i < oldList.length; ++i) {
- parent.removeChild(oldList[i]);
- }
-};
+ if (info.tileBorderColor) {
+ themeStyle.push(
+ '.thumb-ntp .mv-tile {' +
+ 'border: 1px solid ' + info.tileBorderColor + '; }');
+ }
+ if (info.tileHoverBorderColor) {
+ themeStyle.push(
+ '.thumb-ntp .mv-tile:hover {' +
+ 'border-color: ' + info.tileHoverBorderColor + '; }');
+ }
+ if (info.isThemeDark) {
+ themeStyle.push(
+ '.thumb-ntp .mv-tile, .thumb-ntp .mv-empty-tile { ' +
+ 'background: rgb(51,51,51); }');
+ themeStyle.push(
+ '.thumb-ntp .mv-thumb.failed-img { ' +
+ 'background-color: #555; }');
+ themeStyle.push(
+ '.thumb-ntp .mv-thumb.failed-img::after { ' +
+ 'border-color: #333; }');
+ themeStyle.push(
+ '.thumb-ntp .mv-x { ' +
+ 'background: linear-gradient(to left, ' +
+ 'rgb(51,51,51) 60%, transparent); }');
+ themeStyle.push(
+ 'html[dir=rtl] .thumb-ntp .mv-x { ' +
+ 'background: linear-gradient(to right, ' +
+ 'rgb(51,51,51) 60%, transparent); }');
+ themeStyle.push(
+ '.thumb-ntp .mv-x::after { ' +
+ 'background-color: rgba(255,255,255,0.7); }');
+ themeStyle.push(
+ '.thumb-ntp .mv-x:hover::after { ' +
+ 'background-color: #fff; }');
+ themeStyle.push(
+ '.thumb-ntp .mv-x:active::after { ' +
+ 'background-color: rgba(255,255,255,0.5); }');
+ themeStyle.push(
+ '.icon-ntp .mv-tile:focus { ' +
+ 'background: rgba(255,255,255,0.2); }');
+ }
+ if (info.tileTitleColor) {
+ themeStyle.push('body { color: ' + info.tileTitleColor + '; }');
+ }
+ document.querySelector('#custom-theme').textContent = themeStyle.join('\n');
+ };
-/**
- * Called when the host page has finished sending us tile information and
- * we are ready to show the new tiles and drop the old ones.
- */
-var showTiles = function() {
- // Store the tiles on the current closure.
- var cur = tiles;
- // Create empty tiles until we have NUMBER_OF_TILES.
- while (cur.childNodes.length < NUMBER_OF_TILES) {
- addTile({});
- }
+ /**
+ * Hides extra tiles that don't fit on screen.
+ */
+ var hideOverflowTiles = function(data) {
+ var tileAndEmptyTileList = document.querySelectorAll(
+ '#mv-tiles .mv-tile,#mv-tiles .mv-empty-tile');
+ for (var i = 0; i < tileAndEmptyTileList.length; ++i) {
+ tileAndEmptyTileList[i].classList.toggle('hidden', i >= data.maxVisible);
+ }
+ };
- var parent = document.querySelector('#most-visited');
-
- // Only fade in the new tiles if there were tiles before.
- var fadeIn = false;
- var old = parent.querySelector('#mv-tiles');
- if (old) {
- fadeIn = true;
- // Mark old tile DIV for removal after the transition animation is done.
- old.removeAttribute('id');
- old.classList.add('mv-tiles-old');
- old.style.opacity = 0.0;
- cur.addEventListener('webkitTransitionEnd', function(ev) {
- if (ev.target === cur) {
- removeAllOldTiles();
- }
- });
- }
- // Add new tileset.
- cur.id = 'mv-tiles';
- parent.appendChild(cur);
- // getComputedStyle causes the initial style (opacity 0) to be applied, so
- // that when we then set it to 1, that triggers the CSS transition.
- if (fadeIn) {
- window.getComputedStyle(cur).opacity;
- }
- cur.style.opacity = 1.0;
-
- // Make sure the tiles variable contain the next tileset we may use.
- tiles = document.createElement('div');
-};
-
-
-/**
- * Called when the host page wants to add a suggestion tile.
- * For Most Visited, it grabs the data from Chrome and pass on.
- * For host page generated it just passes the data.
- * @param {object} args Data for the tile to be rendered.
- */
-var addTile = function(args) {
- if (isFinite(args.rid)) {
- // If a valid number passed in |args.rid|: a local chrome suggestion.
- var data =
- chrome.embeddedSearch.newTabPage.getMostVisitedItemData(args.rid);
- if (!data)
- return;
-
- data.tid = data.rid;
- if (!data.faviconUrl) {
- data.faviconUrl = 'chrome-search://favicon/size/16@' +
- window.devicePixelRatio + 'x/' + data.renderViewId + '/' + data.tid;
+ /**
+ * Removes all old instances of #mv-tiles that are pending for deletion.
+ */
+ var removeAllOldTiles = function() {
+ var parent = document.querySelector('#most-visited');
+ var oldList = parent.querySelectorAll('.mv-tiles-old');
+ for (var i = 0; i < oldList.length; ++i) {
+ parent.removeChild(oldList[i]);
}
- tiles.appendChild(renderTile(data));
- } else if (args.url) {
- // If a URL is passed: a server-side suggestion.
- args.tileSource = NTPTileSource.SUGGESTIONS_SERVICE;
- // check sanity of the arguments
- if (/^javascript:/i.test(args.url) ||
- /^javascript:/i.test(args.thumbnailUrl))
- return;
- tiles.appendChild(renderTile(args));
- } else { // an empty tile
- tiles.appendChild(renderTile(null));
- }
-};
-
-/**
- * Called when the user decided to add a tile to the blacklist.
- * It sets of the animation for the blacklist and sends the blacklisted id
- * to the host page.
- * @param {Element} tile DOM node of the tile we want to remove.
- */
-var blacklistTile = function(tile) {
- tile.classList.add('blacklisted');
- tile.addEventListener('webkitTransitionEnd', function(ev) {
- if (ev.propertyName != 'width') return;
-
- window.parent.postMessage({cmd: 'tileBlacklisted',
- tid: Number(tile.getAttribute('data-tid'))},
- DOMAIN_ORIGIN);
- });
-};
-
-
-/**
- * Returns whether the given URL has a known, safe scheme.
- * @param {string} url URL to check.
- */
-var isSchemeAllowed = function(url) {
- return url.startsWith('http://') || url.startsWith('https://') ||
- url.startsWith('ftp://') || url.startsWith('file://') ||
- url.startsWith('chrome-extension://');
-};
-
-
-/**
- * Renders a MostVisited tile to the DOM.
- * @param {object} data Object containing rid, url, title, favicon, thumbnail.
- * data is null if you want to construct an empty tile.
- */
-var renderTile = function(data) {
- var tile = document.createElement('a');
-
- if (data == null) {
- tile.className = 'mv-empty-tile';
- return tile;
- }
+ };
- // The tile will be appended to tiles.
- var position = tiles.children.length;
- logMostVisitedImpression(position, data.tileSource);
- tile.className = 'mv-tile';
- tile.setAttribute('data-tid', data.tid);
- var html = [];
- if (!USE_ICONS) {
- html.push('<div class="mv-favicon"></div>');
- }
- html.push('<div class="mv-title"></div><div class="mv-thumb"></div>');
- html.push('<div class="mv-x" role="button"></div>');
- tile.innerHTML = html.join('');
- tile.lastElementChild.title = queryArgs['removeTooltip'] || '';
+ /**
+ * Called when the host page has finished sending us tile information and
+ * we are ready to show the new tiles and drop the old ones.
+ */
+ var showTiles = function() {
+ // Store the tiles on the current closure.
+ var cur = tiles;
- if (isSchemeAllowed(data.url)) {
- tile.href = data.url;
- }
- tile.setAttribute('aria-label', data.title);
- tile.title = data.title;
-
- tile.addEventListener('click', function(ev) {
- logMostVisitedNavigation(position, data.tileSource);
- });
-
- tile.addEventListener('keydown', function(event) {
- if (event.keyCode == 46 /* DELETE */ ||
- event.keyCode == 8 /* BACKSPACE */) {
- event.preventDefault();
- event.stopPropagation();
- blacklistTile(this);
- } else if (event.keyCode == 13 /* ENTER */ ||
- event.keyCode == 32 /* SPACE */) {
- event.preventDefault();
- this.click();
- } else if (event.keyCode >= 37 && event.keyCode <= 40 /* ARROWS */) {
- // specify the direction of movement
- var inArrowDirection = function(origin, target) {
- return (event.keyCode == 37 /* LEFT */ &&
- origin.offsetTop == target.offsetTop &&
- origin.offsetLeft > target.offsetLeft) ||
- (event.keyCode == 38 /* UP */ &&
- origin.offsetTop > target.offsetTop &&
- origin.offsetLeft == target.offsetLeft) ||
- (event.keyCode == 39 /* RIGHT */ &&
- origin.offsetTop == target.offsetTop &&
- origin.offsetLeft < target.offsetLeft) ||
- (event.keyCode == 40 /* DOWN */ &&
- origin.offsetTop < target.offsetTop &&
- origin.offsetLeft == target.offsetLeft);
- };
+ // Create empty tiles until we have NUMBER_OF_TILES.
+ while (cur.childNodes.length < NUMBER_OF_TILES) {
+ addTile({});
+ }
- var nonEmptyTiles = document.querySelectorAll('#mv-tiles .mv-tile');
- var nextTile = null;
- // Find the closest tile in the appropriate direction.
- for (var i = 0; i < nonEmptyTiles.length; i++) {
- if (inArrowDirection(this, nonEmptyTiles[i]) &&
- (!nextTile || inArrowDirection(nonEmptyTiles[i], nextTile))) {
- nextTile = nonEmptyTiles[i];
+ var parent = document.querySelector('#most-visited');
+
+ // Only fade in the new tiles if there were tiles before.
+ var fadeIn = false;
+ var old = parent.querySelector('#mv-tiles');
+ if (old) {
+ fadeIn = true;
+ // Mark old tile DIV for removal after the transition animation is done.
+ old.removeAttribute('id');
+ old.classList.add('mv-tiles-old');
+ old.style.opacity = 0.0;
+ cur.addEventListener('webkitTransitionEnd', function(ev) {
+ if (ev.target === cur) {
+ removeAllOldTiles();
}
+ });
+ }
+
+ // Add new tileset.
+ cur.id = 'mv-tiles';
+ parent.appendChild(cur);
+ // getComputedStyle causes the initial style (opacity 0) to be applied, so
+ // that when we then set it to 1, that triggers the CSS transition.
+ if (fadeIn) {
+ window.getComputedStyle(cur).opacity;
+ }
+ cur.style.opacity = 1.0;
+
+ // Make sure the tiles variable contain the next tileset we may use.
+ tiles = document.createElement('div');
+ };
+
+
+ /**
+ * Called when the host page wants to add a suggestion tile.
+ * For Most Visited, it grabs the data from Chrome and pass on.
+ * For host page generated it just passes the data.
+ * @param {object} args Data for the tile to be rendered.
+ */
+ var addTile = function(args) {
+ if (isFinite(args.rid)) {
+ // If a valid number passed in |args.rid|: a local chrome suggestion.
+ var data =
+ chrome.embeddedSearch.newTabPage.getMostVisitedItemData(args.rid);
+ if (!data)
+ return;
+
+ data.tid = data.rid;
+ if (!data.faviconUrl) {
+ data.faviconUrl = 'chrome-search://favicon/size/16@' +
+ window.devicePixelRatio + 'x/' + data.renderViewId + '/' + data.tid;
}
- if (nextTile) {
- nextTile.focus();
- }
+ tiles.appendChild(renderTile(data));
+ } else if (args.url) {
+ // If a URL is passed: a server-side suggestion.
+ args.tileSource = NTPTileSource.SUGGESTIONS_SERVICE;
+ // check sanity of the arguments
+ if (/^javascript:/i.test(args.url) ||
+ /^javascript:/i.test(args.thumbnailUrl))
+ return;
+ tiles.appendChild(renderTile(args));
+ } else { // an empty tile
+ tiles.appendChild(renderTile(null));
}
- });
+ };
+
+ /**
+ * Called when the user decided to add a tile to the blacklist.
+ * It sets of the animation for the blacklist and sends the blacklisted id
+ * to the host page.
+ * @param {Element} tile DOM node of the tile we want to remove.
+ */
+ var blacklistTile = function(tile) {
+ tile.classList.add('blacklisted');
+ tile.addEventListener('webkitTransitionEnd', function(ev) {
+ if (ev.propertyName != 'width')
+ return;
- var title = tile.querySelector('.mv-title');
- title.innerText = data.title;
- title.style.direction = data.direction || 'ltr';
- if (NUM_TITLE_LINES > 1) {
- title.classList.add('multiline');
- }
+ window.parent.postMessage(
+ {cmd: 'tileBlacklisted', tid: Number(tile.getAttribute('data-tid'))},
+ DOMAIN_ORIGIN);
+ });
+ };
+
+
+ /**
+ * Returns whether the given URL has a known, safe scheme.
+ * @param {string} url URL to check.
+ */
+ var isSchemeAllowed = function(url) {
+ return url.startsWith('http://') || url.startsWith('https://') ||
+ url.startsWith('ftp://') || url.startsWith('file://') ||
+ url.startsWith('chrome-extension://');
+ };
+
+
+ /**
+ * Renders a MostVisited tile to the DOM.
+ * @param {object} data Object containing rid, url, title, favicon, thumbnail.
+ * data is null if you want to construct an empty tile.
+ */
+ var renderTile = function(data) {
+ var tile = document.createElement('a');
+
+ if (data == null) {
+ tile.className = 'mv-empty-tile';
+ return tile;
+ }
+
+ // The tile will be appended to tiles.
+ var position = tiles.children.length;
+ logMostVisitedImpression(position, data.tileSource);
+
+ tile.className = 'mv-tile';
+ tile.setAttribute('data-tid', data.tid);
+ var html = [];
+ if (!USE_ICONS) {
+ html.push('<div class="mv-favicon"></div>');
+ }
+ html.push('<div class="mv-title"></div><div class="mv-thumb"></div>');
+ html.push('<div class="mv-x" role="button"></div>');
+ tile.innerHTML = html.join('');
+ tile.lastElementChild.title = queryArgs['removeTooltip'] || '';
+
+ if (isSchemeAllowed(data.url)) {
+ tile.href = data.url;
+ }
+ tile.setAttribute('aria-label', data.title);
+ tile.title = data.title;
+
+ tile.addEventListener('click', function(ev) {
+ logMostVisitedNavigation(position, data.tileSource);
+ });
- if (USE_ICONS) {
- var thumb = tile.querySelector('.mv-thumb');
- if (data.largeIconUrl) {
+ tile.addEventListener('keydown', function(event) {
+ if (event.keyCode == 46 /* DELETE */ ||
+ event.keyCode == 8 /* BACKSPACE */) {
+ event.preventDefault();
+ event.stopPropagation();
+ blacklistTile(this);
+ } else if (
+ event.keyCode == 13 /* ENTER */ || event.keyCode == 32 /* SPACE */) {
+ event.preventDefault();
+ this.click();
+ } else if (event.keyCode >= 37 && event.keyCode <= 40 /* ARROWS */) {
+ // specify the direction of movement
+ var inArrowDirection = function(origin, target) {
+ return (event.keyCode == 37 /* LEFT */ &&
+ origin.offsetTop == target.offsetTop &&
+ origin.offsetLeft > target.offsetLeft) ||
+ (event.keyCode == 38 /* UP */ &&
+ origin.offsetTop > target.offsetTop &&
+ origin.offsetLeft == target.offsetLeft) ||
+ (event.keyCode == 39 /* RIGHT */ &&
+ origin.offsetTop == target.offsetTop &&
+ origin.offsetLeft < target.offsetLeft) ||
+ (event.keyCode == 40 /* DOWN */ &&
+ origin.offsetTop < target.offsetTop &&
+ origin.offsetLeft == target.offsetLeft);
+ };
+
+ var nonEmptyTiles = document.querySelectorAll('#mv-tiles .mv-tile');
+ var nextTile = null;
+ // Find the closest tile in the appropriate direction.
+ for (var i = 0; i < nonEmptyTiles.length; i++) {
+ if (inArrowDirection(this, nonEmptyTiles[i]) &&
+ (!nextTile || inArrowDirection(nonEmptyTiles[i], nextTile))) {
+ nextTile = nonEmptyTiles[i];
+ }
+ }
+ if (nextTile) {
+ nextTile.focus();
+ }
+ }
+ });
+
+ var title = tile.querySelector('.mv-title');
+ title.innerText = data.title;
+ title.style.direction = data.direction || 'ltr';
+ if (NUM_TITLE_LINES > 1) {
+ title.classList.add('multiline');
+ }
+
+ if (USE_ICONS) {
+ var thumb = tile.querySelector('.mv-thumb');
+ if (data.largeIconUrl) {
+ var img = document.createElement('img');
+ img.title = data.title;
+ img.src = data.largeIconUrl;
+ img.classList.add('large-icon');
+ loadedCounter += 1;
+ img.addEventListener('load', countLoad);
+ img.addEventListener('load', function(ev) {
+ thumb.classList.add('large-icon-outer');
+ });
+ img.addEventListener('error', countLoad);
+ img.addEventListener('error', function(ev) {
+ thumb.classList.add('failed-img');
+ thumb.removeChild(img);
+ });
+ thumb.appendChild(img);
+ } else {
+ thumb.classList.add('failed-img');
+ }
+ } else { // THUMBNAILS
+ // We keep track of the outcome of loading possible thumbnails for this
+ // tile. Possible values:
+ // - null: waiting for load/error
+ // - false: error
+ // - a string: URL that loaded correctly.
+ // This is populated by acceptImage/rejectImage and loadBestImage
+ // decides the best one to load.
+ var results = [];
+ var thumb = tile.querySelector('.mv-thumb');
var img = document.createElement('img');
+ var loaded = false;
+
+ var loadBestImage = function() {
+ if (loaded) {
+ return;
+ }
+ for (var i = 0; i < results.length; ++i) {
+ if (results[i] === null) {
+ return;
+ }
+ if (results[i] != false) {
+ img.src = results[i];
+ loaded = true;
+ return;
+ }
+ }
+ thumb.classList.add('failed-img');
+ thumb.removeChild(img);
+ countLoad();
+ };
+
+ var acceptImage = function(idx, url) {
+ return function(ev) {
+ results[idx] = url;
+ loadBestImage();
+ };
+ };
+
+ var rejectImage = function(idx) {
+ return function(ev) {
+ results[idx] = false;
+ loadBestImage();
+ };
+ };
+
img.title = data.title;
- img.src = data.largeIconUrl;
- img.classList.add('large-icon');
+ img.classList.add('thumbnail');
loadedCounter += 1;
img.addEventListener('load', countLoad);
- img.addEventListener('load', function(ev) {
- thumb.classList.add('large-icon-outer');
- });
img.addEventListener('error', countLoad);
img.addEventListener('error', function(ev) {
thumb.classList.add('failed-img');
thumb.removeChild(img);
});
thumb.appendChild(img);
- } else {
- thumb.classList.add('failed-img');
- }
- } else { // THUMBNAILS
- // We keep track of the outcome of loading possible thumbnails for this
- // tile. Possible values:
- // - null: waiting for load/error
- // - false: error
- // - a string: URL that loaded correctly.
- // This is populated by acceptImage/rejectImage and loadBestImage
- // decides the best one to load.
- var results = [];
- var thumb = tile.querySelector('.mv-thumb');
- var img = document.createElement('img');
- var loaded = false;
-
- var loadBestImage = function() {
- if (loaded) {
- return;
- }
- for (var i = 0; i < results.length; ++i) {
- if (results[i] === null) {
- return;
+
+ if (data.thumbnailUrl) {
+ img.src = data.thumbnailUrl;
+ } else {
+ // Get all thumbnailUrls for the tile.
+ // They are ordered from best one to be used to worst.
+ for (var i = 0; i < data.thumbnailUrls.length; ++i) {
+ results.push(null);
}
- if (results[i] != false) {
- img.src = results[i];
- loaded = true;
- return;
+ for (var i = 0; i < data.thumbnailUrls.length; ++i) {
+ if (data.thumbnailUrls[i]) {
+ var image = new Image();
+ image.src = data.thumbnailUrls[i];
+ image.onload = acceptImage(i, data.thumbnailUrls[i]);
+ image.onerror = rejectImage(i);
+ } else {
+ rejectImage(i)(null);
+ }
}
}
- thumb.classList.add('failed-img');
- thumb.removeChild(img);
- countLoad();
- };
- var acceptImage = function(idx, url) {
- return function(ev) {
- results[idx] = url;
- loadBestImage();
- };
- };
+ var favicon = tile.querySelector('.mv-favicon');
+ if (data.faviconUrl) {
+ var fi = document.createElement('img');
+ fi.src = data.faviconUrl;
+ // Set the title to empty so screen readers won't say the image name.
+ fi.title = '';
+ loadedCounter += 1;
+ fi.addEventListener('load', countLoad);
+ fi.addEventListener('error', countLoad);
+ fi.addEventListener('error', function(ev) {
+ favicon.classList.add('failed-favicon');
+ });
+ favicon.appendChild(fi);
+ } else {
+ favicon.classList.add('failed-favicon');
+ }
+ }
- var rejectImage = function(idx) {
- return function(ev) {
- results[idx] = false;
- loadBestImage();
- };
- };
-
- img.title = data.title;
- img.classList.add('thumbnail');
- loadedCounter += 1;
- img.addEventListener('load', countLoad);
- img.addEventListener('error', countLoad);
- img.addEventListener('error', function(ev) {
- thumb.classList.add('failed-img');
- thumb.removeChild(img);
+ var mvx = tile.querySelector('.mv-x');
+ mvx.addEventListener('click', function(ev) {
+ removeAllOldTiles();
+ blacklistTile(tile);
+ ev.preventDefault();
+ ev.stopPropagation();
});
- thumb.appendChild(img);
- if (data.thumbnailUrl) {
- img.src = data.thumbnailUrl;
- } else {
- // Get all thumbnailUrls for the tile.
- // They are ordered from best one to be used to worst.
- for (var i = 0; i < data.thumbnailUrls.length; ++i) {
- results.push(null);
- }
- for (var i = 0; i < data.thumbnailUrls.length; ++i) {
- if (data.thumbnailUrls[i]) {
- var image = new Image();
- image.src = data.thumbnailUrls[i];
- image.onload = acceptImage(i, data.thumbnailUrls[i]);
- image.onerror = rejectImage(i);
- } else {
- rejectImage(i)(null);
- }
- }
+ return tile;
+ };
+
+
+ /**
+ * Do some initialization and parses the query arguments passed to the iframe.
+ */
+ var init = function() {
+ // Creates a new DOM element to hold the tiles.
+ tiles = document.createElement('div');
+
+ // Parse query arguments.
+ var query = window.location.search.substring(1).split('&');
+ queryArgs = {};
+ for (var i = 0; i < query.length; ++i) {
+ var val = query[i].split('=');
+ if (val[0] == '')
+ continue;
+ queryArgs[decodeURIComponent(val[0])] = decodeURIComponent(val[1]);
}
- var favicon = tile.querySelector('.mv-favicon');
- if (data.faviconUrl) {
- var fi = document.createElement('img');
- fi.src = data.faviconUrl;
- // Set the title to empty so screen readers won't say the image name.
- fi.title = '';
- loadedCounter += 1;
- fi.addEventListener('load', countLoad);
- fi.addEventListener('error', countLoad);
- fi.addEventListener('error', function(ev) {
- favicon.classList.add('failed-favicon');
- });
- favicon.appendChild(fi);
- } else {
- favicon.classList.add('failed-favicon');
+ // Apply class for icon NTP, if specified.
+ USE_ICONS = queryArgs['icons'] == '1';
+ if ('ntl' in queryArgs) {
+ var ntl = parseInt(queryArgs['ntl'], 10);
+ if (isFinite(ntl))
+ NUM_TITLE_LINES = ntl;
}
- }
- var mvx = tile.querySelector('.mv-x');
- mvx.addEventListener('click', function(ev) {
- removeAllOldTiles();
- blacklistTile(tile);
- ev.preventDefault();
- ev.stopPropagation();
- });
-
- return tile;
-};
-
-
-/**
- * Do some initialization and parses the query arguments passed to the iframe.
- */
-var init = function() {
- // Creates a new DOM element to hold the tiles.
- tiles = document.createElement('div');
-
- // Parse query arguments.
- var query = window.location.search.substring(1).split('&');
- queryArgs = {};
- for (var i = 0; i < query.length; ++i) {
- var val = query[i].split('=');
- if (val[0] == '') continue;
- queryArgs[decodeURIComponent(val[0])] = decodeURIComponent(val[1]);
- }
-
- // Apply class for icon NTP, if specified.
- USE_ICONS = queryArgs['icons'] == '1';
- if ('ntl' in queryArgs) {
- var ntl = parseInt(queryArgs['ntl'], 10);
- if (isFinite(ntl))
- NUM_TITLE_LINES = ntl;
- }
+ // Duplicating NTP_DESIGN.mainClass.
+ document.querySelector('#most-visited')
+ .classList.add(USE_ICONS ? 'icon-ntp' : 'thumb-ntp');
- // Duplicating NTP_DESIGN.mainClass.
- document.querySelector('#most-visited').classList.add(
- USE_ICONS ? 'icon-ntp' : 'thumb-ntp');
-
- // Enable RTL.
- if (queryArgs['rtl'] == '1') {
- var html = document.querySelector('html');
- html.dir = 'rtl';
- }
+ // Enable RTL.
+ if (queryArgs['rtl'] == '1') {
+ var html = document.querySelector('html');
+ html.dir = 'rtl';
+ }
- window.addEventListener('message', handlePostMessage);
-};
+ window.addEventListener('message', handlePostMessage);
+ };
-window.addEventListener('DOMContentLoaded', init);
+ window.addEventListener('DOMContentLoaded', init);
})();

Powered by Google App Engine
This is Rietveld 408576698