OLD | NEW |
---|---|
(Empty) | |
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. */ | |
4 | |
5 // Single iframe for NTP tiles. | |
6 (function() { | |
7 'use strict'; | |
8 | |
9 <include src="../../../../ui/webui/resources/js/util.js"> | |
10 | |
11 /** | |
12 * The different types of events that are logged from the NTP. This enum is | |
13 * used to transfer information from the NTP javascript to the renderer and is | |
14 * not used as a UMA enum histogram's logged value. | |
15 * Note: Keep in sync with common/ntp_logging_events.h | |
16 * @enum {number} | |
17 * @const | |
18 */ | |
19 var LOGTYPE = { | |
20 // The suggestion is coming from the server. | |
21 NTP_SERVER_SIDE_SUGGESTION: 0, | |
22 // The suggestion is coming from the client. | |
23 NTP_CLIENT_SIDE_SUGGESTION: 1, | |
24 // Indicates a tile was rendered, no matter if it's a thumbnail, a gray tile | |
25 // or an external tile. | |
26 NTP_TILE: 2, | |
27 // The tile uses a local thumbnail image. | |
28 NTP_THUMBNAIL_TILE: 3, | |
29 // Used when no thumbnail is specified and a gray tile with the domain is used | |
30 // as the main tile. | |
31 NTP_GRAY_TILE: 4, | |
32 // The visuals of that tile are handled externally by the page itself. | |
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 grey tile. | |
36 NTP_THUMBNAIL_ERROR: 6, | |
37 // Used a gray tile with the domain as the fallback for a failed thumbnail. | |
38 NTP_GRAY_TILE_FALLBACK: 7, | |
39 // The visuals of that tile's fallback are handled externally. | |
40 NTP_EXTERNAL_TILE_FALLBACK: 8, | |
41 // The user moused over an NTP tile or title. | |
42 NTP_MOUSEOVER: 9 | |
43 }; | |
44 | |
45 /** | |
46 * The origin of this request. | |
47 * @const {string} | |
48 */ | |
49 var DOMAIN_ORIGIN = '{{ORIGIN}}'; | |
50 | |
51 /** | |
52 * Counter for DOM elements that we are waiting to finish loading. | |
53 * @type {number} | |
54 */ | |
55 var loadedCounter = 1; | |
56 | |
57 /** | |
58 * DOM element containing the tiles we are going to present next. | |
59 * Works as a double-buffer that is shown when we receive a "show" postMessage. | |
60 * @type {DOM} | |
61 */ | |
62 var tiles = null; | |
63 | |
64 /** | |
65 * Log an event on the NTP | |
66 * @param {number} eventName Event from NTP_LOGGING_EVENT_TYPE. | |
67 */ | |
68 var logEvent = function(eventName) { | |
69 chrome.embeddedSearch.newTabPage.logEvent(eventName); | |
70 }; | |
71 | |
72 | |
73 /** | |
74 * Down count the DOM elements that we are waiting for the page to load. | |
75 * When we get to 0, we send a message to the parent window. | |
76 * This is usually used as an EventListener of onload/onerror. | |
77 */ | |
78 var countLoad = function() { | |
79 loadedCounter--; | |
80 if (loadedCounter <= 0.0) { | |
huangs
2015/03/11 19:14:58
Why not just 0?
fserb
2015/03/11 19:30:58
Done.
| |
81 window.parent.postMessage({cmd: 'loaded' }, DOMAIN_ORIGIN); | |
82 loadedCounter = 1; | |
83 } | |
84 }; | |
85 | |
86 | |
87 /** | |
88 * Handle postMessages coming from the host page to the iframe. | |
89 * We try to keep the logic here to a minimum and just dispatch to the relevant | |
90 * functions. | |
91 **/ | |
92 var handlePostMessage = function(event) { | |
93 var cmd = event.data.cmd; | |
94 var args = event.data; | |
95 | |
96 if (cmd == 'tile') { | |
97 addTile(args); | |
98 } else if (cmd == 'show') { | |
99 showTiles(); | |
100 countLoad(); | |
101 } else { | |
102 console.error('Unknown command: ' + event.data); | |
103 } | |
104 }; | |
105 | |
106 /** | |
107 * Called when the host page has finished sending us tile information and | |
108 * we are ready to show the new tiles and drop the old ones. | |
109 */ | |
110 var showTiles = function() { | |
111 // store the tiles on the current closure. | |
112 var cur = tiles; | |
113 | |
114 // Create empty tiles until we have 8. | |
115 while (cur.childNodes.length < 8) { | |
116 addTile({}); | |
117 } | |
118 | |
119 var parent = $('most-visited'); | |
120 | |
121 // Mark old tile DIV for removal after the transition animation is done. | |
122 var old = parent.querySelector('#mv-tiles'); | |
123 if (old) { | |
124 old.id = 'mv-tiles-old'; | |
125 cur.addEventListener('webkitTransitionEnd', function(ev) { | |
126 if (ev.target != cur) return; | |
huangs
2015/03/11 19:14:58
if (ev.target === cur) {
parent.removeChild(old)
fserb
2015/03/11 19:30:57
Done.
| |
127 parent.removeChild(old); | |
128 }); | |
129 } | |
130 | |
131 // Add new tileset. | |
132 cur.id = 'mv-tiles'; | |
133 parent.appendChild(cur); | |
134 // We want the CSS transition to trigger, so need to add to the DOM before | |
135 // setting the style. | |
136 setTimeout(function() { | |
137 cur.style.opacity = 1.0; | |
138 }, 0); | |
139 | |
140 // Make sure the tiles variable contain the next tileset we may use. | |
141 tiles = document.createElement('div'); | |
142 }; | |
143 | |
144 | |
145 /** | |
146 * Called when the host page wants to add a suggestion tile. | |
147 * For Most Visited, it grabs the data from Chrome and pass on. | |
148 * For host page generated it just passes the data. | |
149 * @param {object} args Data for the tile to be rendered. | |
150 */ | |
151 var addTile = function(args) { | |
152 if (args.rid) { | |
153 var data = chrome.embeddedSearch.searchBox.getMostVisitedItemData(args.rid); | |
154 tiles.appendChild(renderTile(data)); | |
155 logEvent(LOGTYPE.NTP_CLIENT_SIDE_SUGGESTION); | |
156 } else { | |
157 tiles.appendChild(renderTile(null)); | |
158 } | |
159 }; | |
160 | |
161 | |
162 /** | |
163 * Called when the user decided to add a tile to the blacklist. | |
164 * It sets of the animation for the blacklist and sends the blacklisted id | |
165 * to the host page. | |
166 * @param {DOMNode} tile DOM node of the tile we want to remove. | |
167 */ | |
168 var blacklistTile = function(tile) { | |
169 tile.className += ' blacklisted'; | |
huangs
2015/03/11 19:14:57
Can use tile.classList.add('blacklisted'). Same b
fserb
2015/03/11 19:30:58
Done.
| |
170 var sent = false; | |
171 tile.addEventListener('webkitTransitionEnd', function() { | |
172 if (sent) return; | |
173 sent = true; | |
174 window.parent.postMessage({cmd: 'tileBlacklisted', | |
175 rid: Number(tile.getAttribute('rid'))}, | |
176 DOMAIN_ORIGIN); | |
177 }); | |
178 }; | |
179 | |
180 | |
181 /** | |
182 * Renders a MostVisited tile to the DOM. | |
183 * @param {object} data Object containing rid, url, title, favicon, thumbnail. | |
184 * data is null if you want to construct an empty tile. | |
185 */ | |
186 var renderTile = function(data) { | |
187 var tile = document.createElement('a'); | |
188 tile.className = 'mv-tile'; | |
189 | |
190 if (data == null) { | |
191 tile.className = 'mv-empty-tile'; | |
192 return tile; | |
193 } | |
194 | |
195 logEvent(LOGTYPE.NTP_TILE); | |
196 | |
197 tile.setAttribute('rid', data.rid); | |
198 tile.innerHTML = '<div class="mv-favicon"></div>' + | |
199 '<div class="mv-title"></div><div class="mv-thumb"></div>' + | |
200 '<div title="Don\'t show on this page" class="mv-x"></div>'; | |
201 | |
202 tile.href = data.url; | |
203 tile.title = data.title; | |
204 tile.onkeypress = function(ev) { | |
205 if (ev.keyCode == 127) { // DELETE | |
206 blacklistTile(tile); | |
207 ev.stopPropagation(); | |
208 return false; | |
209 } | |
210 }; | |
211 tile.addEventListener('mouseover', function() { | |
212 logEvent(LOGTYPE.NTP_MOUSEOVER); | |
213 }); | |
214 | |
215 var title = tile.querySelectorAll('.mv-title')[0]; | |
216 title.innerHTML = data.title; | |
217 title.style.direction = data.direction || 'ltr'; | |
218 | |
219 var thumb = tile.querySelectorAll('.mv-thumb')[0]; | |
220 | |
221 var img = document.createElement('img'); | |
222 img.title = data.title; | |
223 if (data.thumbnailUrl) { | |
224 img.src = data.thumbnailUrl; | |
225 loadedCounter += 1; | |
226 img.addEventListener('load', countLoad); | |
227 img.addEventListener('error', countLoad); | |
228 img.addEventListener('error', function(ev) { | |
229 thumb.className += ' failed-img'; | |
230 thumb.removeChild(img); | |
231 logEvent(LOGTYPE.NTP_THUMBNAIL_ERROR); | |
232 }); | |
233 thumb.appendChild(img); | |
234 logEvent(LOGTYPE.NTP_THUMBNAIL_TILE); | |
235 } else { | |
236 thumb.className += ' failed-img'; | |
237 } | |
238 | |
239 var favicon = tile.querySelectorAll('.mv-favicon')[0]; | |
240 if (data.faviconUrl) { | |
241 var fi = document.createElement('img'); | |
242 fi.src = '../' + data.faviconUrl; | |
243 fi.title = ''; | |
244 loadedCounter += 1; | |
245 fi.addEventListener('load', countLoad); | |
246 fi.addEventListener('error', countLoad); | |
247 fi.addEventListener('error', function(ev) { | |
248 favicon.className += ' failed-favicon'; | |
249 }); | |
250 favicon.appendChild(fi); | |
251 } else { | |
252 favicon.className += ' failed-favicon'; | |
253 } | |
254 | |
255 var mvx = tile.querySelectorAll('.mv-x')[0]; | |
256 mvx.onclick = function(ev) { | |
257 blacklistTile(tile); | |
258 ev.stopPropagation(); | |
259 return false; | |
260 }; | |
261 | |
262 return tile; | |
263 }; | |
264 | |
265 | |
266 /** | |
267 * Do some initialization and parses the query arguments passed to the iframe. | |
268 */ | |
269 var init = function() { | |
270 // Creates a new DOM element to hold the tiles. | |
271 tiles = document.createElement('div'); | |
272 | |
273 // Parse query arguments. | |
274 var query = window.location.search.substring(1).split('&'); | |
275 var args = {}; | |
276 for (var i = 0; i < query.length; ++i) { | |
277 var val = query[i].split('='); | |
278 if (val[0] == '') continue; | |
279 args[decodeURIComponent(val[0])] = decodeURIComponent(val[1]); | |
280 } | |
281 | |
282 // Enable RTL. | |
283 if (args['rtl'] == '1') { | |
284 var html = document.querySelector('html'); | |
285 html.dir = 'rtl'; | |
286 } | |
287 | |
288 window.addEventListener('message', handlePostMessage); | |
289 }; | |
290 | |
291 window.addEventListener('DOMContentLoaded', init); | |
292 })(); | |
OLD | NEW |