OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 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 cr.define('ntp', function() { | |
6 'use strict'; | |
7 | |
8 var Tile = ntp.Tile2; | |
9 var TilePage = ntp.TilePage2; | |
10 | |
11 /** | |
12 * A counter for generating unique tile IDs. | |
13 */ | |
14 var tileID = 0; | |
15 | |
16 /** | |
17 * Creates a new Most Visited object for tiling. | |
jeremycho_google
2012/07/31 03:09:16
Rename references to Most Visited in the comments
pedrosimonetti2
2012/08/03 18:14:01
Done.
| |
18 * @constructor | |
19 * @extends {HTMLAnchorElement} | |
20 */ | |
21 function Thumbnail(gridValues) { | |
22 var el = cr.doc.createElement('a'); | |
23 el.__proto__ = Thumbnail.prototype; | |
24 el.initialize(gridValues); | |
25 | |
26 return el; | |
27 } | |
28 | |
29 Thumbnail.prototype = Tile.subclass({ | |
30 __proto__: HTMLAnchorElement.prototype, | |
31 | |
32 // TODO(pedrosimonetti): document | |
33 isRemovable: true, | |
34 | |
35 initialize: function(gridValues) { | |
36 Tile.prototype.initialize.apply(this, arguments); | |
37 this.reset(); | |
38 | |
39 this.addEventListener('click', this.handleClick_); | |
40 this.addEventListener('keydown', this.handleKeyDown_); | |
41 }, | |
42 | |
43 get index() { | |
44 assert(this.tile); | |
45 return this.tile.index; | |
46 }, | |
47 | |
48 get data() { | |
49 return this.data_; | |
50 }, | |
51 | |
52 /** | |
53 * Clears the DOM hierarchy for this node, setting it back to the default | |
54 * for a blank thumbnail. | |
55 */ | |
56 reset: function() { | |
57 this.className = 'tile-content most-visited real'; | |
58 this.innerHTML = | |
59 '<span class="thumbnail-wrapper fills-parent">' + | |
60 (this.isRemovable ? '<div class="close-button"></div>' : '') + | |
61 '<span class="thumbnail fills-parent">' + | |
62 // TODO(pedrosimonetti): remove all references (HTML+CSS+JS) | |
63 // thumbnail-shield provides a gradient fade effect. | |
64 //'<div class="thumbnail-shield fills-parent"></div>' + | |
65 '</span>' + | |
66 // TODO(pedrosimonetti): remove all references (HTML+CSS+JS) | |
67 //'<span class="favicon"></span>' + | |
68 '</span>' + | |
69 // TODO(pedrosimonetti): remove all references (HTML+CSS+JS) | |
70 //'<div class="color-stripe"></div>' + | |
71 '<span class="title"></span>'; | |
72 | |
73 if (this.isRemovable) { | |
74 this.querySelector('.close-button').title = | |
75 loadTimeData.getString('removethumbnailtooltip'); | |
76 } | |
77 | |
78 this.tabIndex = -1; | |
79 this.data_ = null; | |
80 this.removeAttribute('id'); | |
81 this.title = ''; | |
82 }, | |
83 | |
84 /** | |
85 * Update the appearance of this tile according to |data|. | |
86 * @param {Object} data A dictionary of relevant data for the page. | |
87 */ | |
88 updateForData: function(data) { | |
89 if (this.classList.contains('blacklisted') && data) { | |
90 // Animate appearance of new tile. | |
91 this.classList.add('new-tile-contents'); | |
92 } | |
93 this.classList.remove('blacklisted'); | |
94 | |
95 if (!data || data.filler) { | |
96 if (this.data_) | |
97 this.reset(); | |
98 return; | |
99 } | |
100 | |
101 var id = tileID++; | |
102 // TODO(pedrosimonetti): refactor | |
103 this.id = 'most-visited-tile-' + id; | |
104 this.data_ = data; | |
105 this.classList.add('focusable'); | |
106 | |
107 // TODO(pedrosimonetti): remove all references (HTML+CSS+JS) | |
108 //var faviconDiv = this.querySelector('.favicon'); | |
109 //var faviconUrl = 'chrome://favicon/size/16/' + data.url; | |
110 //faviconDiv.style.backgroundImage = url(faviconUrl); | |
111 //chrome.send('getFaviconDominantColor', [faviconUrl, this.id]); | |
112 | |
113 var title = this.querySelector('.title'); | |
114 title.textContent = data.title; | |
115 title.dir = data.direction; | |
116 | |
117 // Sets the tooltip. | |
118 this.title = data.title; | |
119 | |
120 var thumbnailUrl = ntp.getThumbnailUrl(data.url); | |
121 this.querySelector('.thumbnail').style.backgroundImage = | |
122 url(thumbnailUrl); | |
123 | |
124 this.href = data.url; | |
125 | |
126 this.classList.remove('filler'); | |
127 }, | |
128 | |
129 /** | |
130 * Sets the color of the favicon dominant color bar. | |
131 * @param {string} color The css-parsable value for the color. | |
132 */ | |
133 // TODO(xci) delete | |
134 //set stripeColor(color) { | |
135 // this.querySelector('.color-stripe').style.backgroundColor = color; | |
136 //}, | |
137 | |
138 /** | |
139 * Handles a click on the tile. | |
140 * @param {Event} e The click event. | |
141 */ | |
142 handleClick_: function(e) { | |
143 debugger; | |
144 if (e.target.classList.contains('close-button')) { | |
145 this.blacklist_(); | |
146 e.preventDefault(); | |
147 } else { | |
148 // Records an app launch from the most visited page (Chrome will decide | |
149 // whether the url is an app). TODO(estade): this only works for clicks; | |
150 // other actions like "open in new tab" from the context menu won't be | |
151 // recorded. Can this be fixed? | |
152 chrome.send('recordAppLaunchByURL', | |
153 [encodeURIComponent(this.href), | |
154 ntp.APP_LAUNCH.NTP_MOST_VISITED]); | |
155 // Records the index of this tile. | |
156 chrome.send('metricsHandler:recordInHistogram', | |
157 ['NewTabPage.Thumbnail', this.index, 8]); | |
158 chrome.send('mostVisitedAction', | |
159 [ntp.NtpFollowAction.CLICKED_TILE]); | |
160 } | |
161 }, | |
162 | |
163 /** | |
164 * Allow blacklisting most visited site using the keyboard. | |
jeremycho_google
2012/07/31 03:09:16
Is MostVisited the only type of tile that will be
pedrosimonetti2
2012/08/03 18:14:01
Done.
| |
165 */ | |
166 handleKeyDown_: function(e) { | |
167 if (!cr.isMac && e.keyCode == 46 || // Del | |
168 cr.isMac && e.metaKey && e.keyCode == 8) { // Cmd + Backspace | |
169 this.blacklist_(); | |
170 } | |
171 }, | |
172 | |
173 /** | |
174 * Permanently removes a page from Most Visited. | |
175 */ | |
176 blacklist_: function() { | |
177 this.showUndoNotification_(); | |
178 chrome.send('blacklistURLFromThumbnail', [this.data_.url]); | |
179 this.reset(); | |
180 chrome.send('getThumbnail'); | |
181 this.classList.add('blacklisted'); | |
182 }, | |
183 | |
184 showUndoNotification_: function() { | |
185 var data = this.data_; | |
186 var self = this; | |
187 var doUndo = function() { | |
188 chrome.send('removeURLsFromThumbnailBlacklist', [data.url]); | |
189 self.updateForData(data); | |
190 } | |
191 | |
192 var undo = { | |
193 action: doUndo, | |
194 text: loadTimeData.getString('undothumbnailremove'), | |
195 }; | |
196 | |
197 var undoAll = { | |
198 action: function() { | |
199 chrome.send('clearThumbnailURLsBlacklist'); | |
200 }, | |
201 text: loadTimeData.getString('restoreThumbnailsShort'), | |
202 }; | |
203 | |
204 ntp.showNotification( | |
205 loadTimeData.getString('thumbnailremovednotification'), | |
206 [undo, undoAll]); | |
207 }, | |
208 | |
209 /** | |
210 * Returns whether this element can be 'removed' from chrome (i.e. whether | |
211 * the user can drag it onto the trash and expect something to happen). | |
212 * @return {boolean} True, since most visited pages can always be | |
213 * blacklisted. | |
214 */ | |
215 canBeRemoved: function() { | |
216 return this.isRemovable; | |
217 }, | |
218 | |
219 /** | |
220 * Removes this element from chrome, i.e. blacklists it. | |
221 */ | |
222 removeFromChrome: function() { | |
223 this.blacklist_(); | |
224 this.parentNode.classList.add('finishing-drag'); | |
225 }, | |
226 | |
227 /** | |
228 * Called when a drag of this tile has ended (after all animations have | |
229 * finished). | |
230 */ | |
231 finalizeDrag: function() { | |
232 this.parentNode.classList.remove('finishing-drag'); | |
233 }, | |
234 | |
235 /** | |
236 * Called when a drag is starting on the tile. Updates dataTransfer with | |
237 * data for this tile (for dragging outside of the NTP). | |
238 */ | |
239 setDragData: function(dataTransfer) { | |
240 dataTransfer.setData('Text', this.data_.title); | |
241 dataTransfer.setData('URL', this.data_.url); | |
242 }, | |
243 }); | |
244 | |
245 var THUMBNAIL_COUNT = 8; // TODO(xci) | |
jeremycho_google
2012/07/31 03:09:16
I still see the bug where 10 thumbnails are being
pedrosimonetti2
2012/08/03 18:14:01
Yes, I'll address it in another CL. I took another
| |
246 | |
247 /** | |
248 * Creates a new ThumbnailPage object. | |
249 * @constructor | |
250 * @extends {TilePage} | |
251 */ | |
252 function ThumbnailPage() { | |
253 var el = new TilePage(); | |
254 el.__proto__ = ThumbnailPage.prototype; | |
255 el.initialize(); | |
256 | |
257 return el; | |
258 } | |
259 | |
260 ThumbnailPage.prototype = { | |
261 __proto__: TilePage.prototype, | |
262 | |
263 gridValues_: { | |
264 tileWidth: 130, | |
265 tileHeight: 78, | |
266 tileHorMargin: 18, // TODO margin with CSS / there's no margin in first co l | |
267 tileVerMargin: 22, | |
268 tileBorderWidth: 1, | |
269 bottomPanelHorMargin: 100, | |
270 | |
271 tileCount: 10, // debug | |
272 tileClassName: 'thumbnail', | |
273 reinforceStyles: true, | |
jeremycho_google
2012/07/31 03:09:16
Please document.
pedrosimonetti2
2012/08/03 18:14:01
Done.
| |
274 | |
275 // debug | |
276 slowFactor: 1, | |
277 debug: false | |
278 }, | |
279 | |
280 // TODO(pedrosimonetti): document | |
281 ThumbnailClass: Thumbnail, | |
282 | |
283 initialize: function() { | |
284 this.classList.add('most-visited-page'); | |
jeremycho_google
2012/07/31 03:09:16
Should this be renamed, given that other classes m
pedrosimonetti2
2012/08/03 18:14:01
Yes. I renamed it to 'thumbnail-page' and override
| |
285 this.data_ = null; | |
286 this.mostVisitedTiles_ = this.getElementsByClassName('most-visited real'); | |
287 | |
288 this.addEventListener('carddeselected', this.handleCardDeselected_); | |
289 this.addEventListener('cardselected', this.handleCardSelected_); | |
290 }, | |
291 | |
292 /** | |
293 * Create blank (filler) tiles. | |
294 * @private | |
295 */ | |
296 createTiles_: function() { | |
297 for (var i = 0; i < THUMBNAIL_COUNT; i++) { | |
298 this.appendTile(new this.ThumbnailClass(this.gridValues_)); | |
299 } | |
300 }, | |
301 | |
302 /** | |
303 * Update the tiles after a change to |data_|. | |
304 */ | |
305 updateTiles_: function() { | |
306 for (var i = 0; i < THUMBNAIL_COUNT; i++) { | |
307 var page = this.data_[i]; | |
308 var tile = this.mostVisitedTiles_[i]; | |
309 | |
310 // TODO(pedrosimonetti): what do we do when there's no tile here? | |
311 if (!tile) { | |
312 return; | |
313 } | |
314 | |
315 if (i >= this.data_.length) | |
316 tile.reset(); | |
317 else | |
318 tile.updateForData(page); | |
319 } | |
320 }, | |
321 | |
322 /** | |
323 * Handles the 'card deselected' event (i.e. the user clicked to another | |
324 * pane). | |
325 * @param {Event} e The CardChanged event. | |
326 */ | |
327 handleCardDeselected_: function(e) { | |
328 if (!document.documentElement.classList.contains('starting-up')) { | |
329 chrome.send('mostVisitedAction', | |
330 [ntp.NtpFollowAction.CLICKED_OTHER_NTP_PANE]); | |
331 } | |
332 }, | |
333 | |
334 /** | |
335 * Handles the 'card selected' event (i.e. the user clicked to select the | |
336 * Most Visited pane). | |
337 * @param {Event} e The CardChanged event. | |
338 */ | |
339 handleCardSelected_: function(e) { | |
340 if (!document.documentElement.classList.contains('starting-up')) | |
341 chrome.send('mostVisitedSelected'); | |
342 }, | |
343 | |
344 /** | |
345 * Array of most visited data objects. | |
346 * @type {Array} | |
347 */ | |
348 get data() { | |
349 return this.data_; | |
350 }, | |
351 set data(data) { | |
352 var startTime = Date.now(); | |
353 | |
354 // The first time data is set, create the tiles. | |
355 if (!this.data_) { | |
356 this.createTiles_(); | |
357 this.data_ = data.slice(0, THUMBNAIL_COUNT); | |
358 } else { | |
359 this.data_ = refreshData(this.data_, data); | |
360 } | |
361 | |
362 this.updateTiles_(); | |
363 logEvent('mostVisited.layout: ' + (Date.now() - startTime)); | |
364 }, | |
365 | |
366 /** @inheritDoc */ | |
367 shouldAcceptDrag: function(e) { | |
368 return false; | |
369 }, | |
370 }; | |
371 | |
372 /** | |
373 * Executed once the NTP has loaded. Checks if the Most Visited pane is | |
374 * shown or not. If it is shown, the 'mostVisitedSelected' message is sent | |
375 * to the C++ code, to record the fact that the user has seen this pane. | |
376 */ | |
377 // TODO(pedrosimonetti) is it possible to keep this inside Thumbnail class? | |
378 /* | |
379 ThumbnailPage.onLoaded = function() { | |
380 if (ntp.getCardSlider() && | |
381 ntp.getCardSlider().currentCardValue && | |
382 ntp.getCardSlider().currentCardValue.classList | |
383 .contains('most-visited-page')) { | |
384 chrome.send('mostVisitedSelected'); | |
385 } | |
386 } | |
387 */ | |
388 | |
389 return { | |
390 Thumbnail: Thumbnail, | |
391 ThumbnailPage: ThumbnailPage | |
392 }; | |
393 }); | |
394 | |
OLD | NEW |