Remove the old signature of NotificationManager::closePersistent().
[chromium-blink-merge.git] / chrome / browser / resources / local_ntp / most_visited_single.js
blobe9eef0233f2dd2da99b24f30b8ba0ba9ef79e3c5
1 /* Copyright 2015 The Chromium Authors. All rights reserved.
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file. */
5  // Single iframe for NTP tiles.
6 (function() {
7 'use strict';
10 /**
11  * The different types of events that are logged from the NTP.  This enum is
12  * used to transfer information from the NTP JavaScript to the renderer and is
13  * not used as a UMA enum histogram's logged value.
14  * Note: Keep in sync with common/ntp_logging_events.h
15  * @enum {number}
16  * @const
17  */
18 var LOG_TYPE = {
19   // The suggestion is coming from the server. Unused here.
20   NTP_SERVER_SIDE_SUGGESTION: 0,
21   // The suggestion is coming from the client.
22   NTP_CLIENT_SIDE_SUGGESTION: 1,
23   // Indicates a tile was rendered, no matter if it's a thumbnail, a gray tile
24   // or an external tile.
25   NTP_TILE: 2,
26   // The tile uses a local thumbnail image.
27   NTP_THUMBNAIL_TILE: 3,
28   // Used when no thumbnail is specified and a gray tile with the domain is used
29   // as the main tile. Unused here.
30   NTP_GRAY_TILE: 4,
31   // The visuals of that tile are handled externally by the page itself.
32   // Unused here.
33   NTP_EXTERNAL_TILE: 5,
34   // There was an error in loading both the thumbnail image and the fallback
35   // (if it was provided), resulting in a gray tile.
36   NTP_THUMBNAIL_ERROR: 6,
37   // Used a gray tile with the domain as the fallback for a failed thumbnail.
38   // Unused here.
39   NTP_GRAY_TILE_FALLBACK: 7,
40   // The visuals of that tile's fallback are handled externally. Unused here.
41   NTP_EXTERNAL_TILE_FALLBACK: 8,
42   // The user moused over an NTP tile.
43   NTP_MOUSEOVER: 9
47 /**
48  * Total number of tiles to show at any time. If the host page doesn't send
49  * enough tiles, we fill them blank.
50  * @const {number}
51  */
52 var NUMBER_OF_TILES = 8;
55 /**
56  * The origin of this request.
57  * @const {string}
58  */
59 var DOMAIN_ORIGIN = '{{ORIGIN}}';
62 /**
63  * Counter for DOM elements that we are waiting to finish loading.
64  * @type {number}
65  */
66 var loadedCounter = 1;
69 /**
70  * DOM element containing the tiles we are going to present next.
71  * Works as a double-buffer that is shown when we receive a "show" postMessage.
72  * @type {Element}
73  */
74 var tiles = null;
77 /**
78  * Log an event on the NTP.
79  * @param {number} eventType Event from LOG_TYPE.
80  */
81 var logEvent = function(eventType) {
82   chrome.embeddedSearch.newTabPage.logEvent(eventType);
86 /**
87  * Down counts the DOM elements that we are waiting for the page to load.
88  * When we get to 0, we send a message to the parent window.
89  * This is usually used as an EventListener of onload/onerror.
90  */
91 var countLoad = function() {
92   loadedCounter -= 1;
93   if (loadedCounter <= 0) {
94     window.parent.postMessage({cmd: 'loaded'}, DOMAIN_ORIGIN);
95     loadedCounter = 1;
96   }
101  * Handles postMessages coming from the host page to the iframe.
102  * We try to keep the logic here to a minimum and just dispatch to the relevant
103  * functions.
104  **/
105 var handlePostMessage = function(event) {
106   var cmd = event.data.cmd;
108   if (cmd == 'tile') {
109     addTile(event.data);
110   } else if (cmd == 'show') {
111     showTiles();
112     countLoad();
113   } else {
114     console.error('Unknown command: ' + event.data);
115   }
120  * Called when the host page has finished sending us tile information and
121  * we are ready to show the new tiles and drop the old ones.
122  */
123 var showTiles = function() {
124   // Store the tiles on the current closure.
125   var cur = tiles;
127   // Create empty tiles until we have NUMBER_OF_TILES.
128   while (cur.childNodes.length < NUMBER_OF_TILES) {
129     addTile({});
130   }
132   var parent = document.querySelector('#most-visited');
134   // Mark old tile DIV for removal after the transition animation is done.
135   var old = parent.querySelector('#mv-tiles');
136   if (old) {
137     old.id = 'mv-tiles-old';
138     cur.addEventListener('webkitTransitionEnd', function(ev) {
139       if (ev.target === cur) {
140         parent.removeChild(old);
141       }
142     });
143   }
145   // Add new tileset.
146   cur.id = 'mv-tiles';
147   parent.appendChild(cur);
148   // We want the CSS transition to trigger, so need to add to the DOM before
149   // setting the style.
150   setTimeout(function() {
151     cur.style.opacity = 1.0;
152   }, 0);
154   // Make sure the tiles variable contain the next tileset we may use.
155   tiles = document.createElement('div');
160  * Called when the host page wants to add a suggestion tile.
161  * For Most Visited, it grabs the data from Chrome and pass on.
162  * For host page generated it just passes the data.
163  * @param {object} args Data for the tile to be rendered.
164  */
165 var addTile = function(args) {
166   if (args.rid) {
167     var data = chrome.embeddedSearch.searchBox.getMostVisitedItemData(args.rid);
168     tiles.appendChild(renderTile(data));
169     logEvent(LOG_TYPE.NTP_CLIENT_SIDE_SUGGESTION);
170   } else {
171     tiles.appendChild(renderTile(null));
172   }
177  * Called when the user decided to add a tile to the blacklist.
178  * It sets of the animation for the blacklist and sends the blacklisted id
179  * to the host page.
180  * @param {Element} tile DOM node of the tile we want to remove.
181  */
182 var blacklistTile = function(tile) {
183   tile.classList.add('blacklisted');
184   var sent = false;
185   tile.addEventListener('webkitTransitionEnd', function() {
186     if (sent) return;
187     sent = true;
188     window.parent.postMessage({cmd: 'tileBlacklisted',
189                                rid: Number(tile.getAttribute('data-rid'))},
190                               DOMAIN_ORIGIN);
191   });
196  * Renders a MostVisited tile to the DOM.
197  * @param {object} data Object containing rid, url, title, favicon, thumbnail.
198  *     data is null if you want to construct an empty tile.
199  */
200 var renderTile = function(data) {
201   var tile = document.createElement('a');
203   if (data == null) {
204     tile.className = 'mv-empty-tile';
205     return tile;
206   }
208   logEvent(LOG_TYPE.NTP_TILE);
210   tile.className = 'mv-tile';
211   tile.setAttribute('data-rid', data.rid);
212   tile.innerHTML = '<div class="mv-favicon"></div>' +
213     '<div class="mv-title"></div><div class="mv-thumb"></div>' +
214     '<div title="' + configData['removeThumbnailTooltip'] +
215     '" class="mv-x"></div>';
217   tile.href = data.url;
218   tile.title = data.title;
219   tile.addEventListener('keypress', function(ev) {
220     if (ev.keyCode == 127) { // DELETE
221       blacklistTile(tile);
222       ev.stopPropagation();
223       return false;
224     }
225   });
226   // TODO(fserb): remove this or at least change to mouseenter.
227   tile.addEventListener('mouseover', function() {
228     logEvent(LOG_TYPE.NTP_MOUSEOVER);
229   });
231   var title = tile.querySelector('.mv-title');
232   title.innerText = data.title;
233   title.style.direction = data.direction || 'ltr';
235   var thumb = tile.querySelector('.mv-thumb');
236   if (data.thumbnailUrl) {
237     var img = document.createElement('img');
238     img.title = data.title;
239     img.src = data.thumbnailUrl;
240     loadedCounter += 1;
241     img.addEventListener('load', countLoad);
242     img.addEventListener('error', countLoad);
243     img.addEventListener('error', function(ev) {
244       thumb.classList.add('failed-img');
245       thumb.removeChild(img);
246       logEvent(LOG_TYPE.NTP_THUMBNAIL_ERROR);
247     });
248     thumb.appendChild(img);
249     logEvent(LOG_TYPE.NTP_THUMBNAIL_TILE);
250   } else {
251     thumb.classList.add('failed-img');
252   }
254   var favicon = tile.querySelector('.mv-favicon');
255   if (data.faviconUrl) {
256     var fi = document.createElement('img');
257     fi.src = '../' + data.faviconUrl;
258     // Set the title to empty so screen readers won't say the image name.
259     fi.title = '';
260     loadedCounter += 1;
261     fi.addEventListener('load', countLoad);
262     fi.addEventListener('error', countLoad);
263     fi.addEventListener('error', function(ev) {
264       favicon.classList.add('failed-favicon');
265     });
266     favicon.appendChild(fi);
267   } else {
268     favicon.classList.add('failed-favicon');
269   }
271   var mvx = tile.querySelector('.mv-x');
272   mvx.addEventListener('click', function(ev) {
273     blacklistTile(tile);
274     ev.stopPropagation();
275     return false;
276   });
278   return tile;
283  * Do some initialization and parses the query arguments passed to the iframe.
284  */
285 var init = function() {
286   // Creates a new DOM element to hold the tiles.
287   tiles = document.createElement('div');
289   // Parse query arguments.
290   var query = window.location.search.substring(1).split('&');
291   var args = {};
292   for (var i = 0; i < query.length; ++i) {
293     var val = query[i].split('=');
294     if (val[0] == '') continue;
295     args[decodeURIComponent(val[0])] = decodeURIComponent(val[1]);
296   }
298   // Enable RTL.
299   if (args['rtl'] == '1') {
300     var html = document.querySelector('html');
301     html.dir = 'rtl';
302   }
304   window.addEventListener('message', handlePostMessage);
308 window.addEventListener('DOMContentLoaded', init);
309 })();