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

Side by Side Diff: chrome/browser/resources/ntp4/suggestions_page.js

Issue 9358073: First version of the time slicing on the urls. Not ready for review yet. Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: added relative time to time slicing Created 8 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 TilePage = ntp.TilePage;
9
10 /**
11 * A counter for generating unique tile IDs.
12 */
13 var tileID = 0;
14
15 /**
16 * Creates a new Suggestions page object for tiling.
17 * @constructor
18 * @extends {HTMLAnchorElement}
19 */
20 function Suggestion() {
21 var el = cr.doc.createElement('a');
22 el.__proto__ = Suggestion.prototype;
23 el.initialize();
24
25 return el;
26 }
27
28 Suggestion.prototype = {
29 __proto__: HTMLAnchorElement.prototype,
30
31 initialize: function() {
32 this.reset();
33
34 this.addEventListener('click', this.handleClick_);
35 this.addEventListener('keydown', this.handleKeyDown_);
36 },
37
38 get index() {
39 assert(this.tile);
40 return this.tile.index;
41 },
42
43 get data() {
44 return this.data_;
45 },
46
47 /**
48 * Clears the DOM hierarchy for this node, setting it back to the default
49 * for a blank thumbnail. TODO(georgey) make it a template.
50 */
51 reset: function() {
52 this.className = 'suggestions filler real';
53 this.innerHTML =
54 '<span class="thumbnail-wrapper fills-parent">' +
55 '<div class="close-button"></div>' +
56 '<span class="thumbnail fills-parent">' +
57 // thumbnail-shield provides a gradient fade effect.
58 '<div class="thumbnail-shield fills-parent"></div>' +
59 '</span>' +
60 '<span class="favicon"></span>' +
61 '</span>' +
62 '<div class="color-stripe"></div>' +
63 '<span class="title"></span>';
64
65 this.querySelector('.close-button').title =
66 templateData.removethumbnailtooltip;
67
68 this.tabIndex = -1;
69 this.data_ = null;
70 this.removeAttribute('id');
71 this.title = '';
72 },
73
74 /**
75 * Update the appearance of this tile according to |data|.
76 * @param {Object} data A dictionary of relevant data for the page.
77 */
78 updateForData: function(data) {
79 if (this.classList.contains('blacklisted') && data) {
80 // Animate appearance of new tile.
81 this.classList.add('new-tile-contents');
82 }
83 this.classList.remove('blacklisted');
84
85 if (!data || data.filler) {
86 if (this.data_)
87 this.reset();
88 return;
89 }
90
91 var id = tileID++;
92 this.id = 'suggestions-tile-' + id;
93 this.data_ = data;
94 this.classList.add('focusable');
95
96 var faviconDiv = this.querySelector('.favicon');
97 var faviconUrl = 'chrome://favicon/size/16/' + data.url;
98 faviconDiv.style.backgroundImage = url(faviconUrl);
99 chrome.send('getFaviconDominantColor', [faviconUrl, this.id]);
100
101 var title = this.querySelector('.title');
102 title.textContent = data.title;
103 title.dir = data.direction;
104
105 // Sets the tooltip.
106 this.title = data.title;
107
108 var thumbnailUrl = 'chrome://thumb/' + data.url;
109 this.querySelector('.thumbnail').style.backgroundImage =
110 url(thumbnailUrl);
111
112 this.href = data.url;
113
114 this.classList.remove('filler');
115 },
116
117 /**
118 * Sets the color of the favicon dominant color bar.
119 * @param {string} color The css-parsable value for the color.
120 */
121 set stripeColor(color) {
122 this.querySelector('.color-stripe').style.backgroundColor = color;
123 },
124
125 /**
126 * Handles a click on the tile.
127 * @param {Event} e The click event.
128 */
129 handleClick_: function(e) {
130 if (e.target.classList.contains('close-button')) {
131 this.blacklist_();
132 e.preventDefault();
133 }
134 },
135
136 /**
137 * Allow blacklisting suggestions site using the keyboard.
138 * @param {Event} e The keydown event.
139 */
140 handleKeyDown_: function(e) {
141 if (!cr.isMac && e.keyCode == 46 || // Del
142 cr.isMac && e.metaKey && e.keyCode == 8) { // Cmd + Backspace
143 this.blacklist_();
144 }
145 },
146
147 /**
148 * Permanently removes a page from Suggestions.
149 */
150 blacklist_: function() {
151 this.showUndoNotification_();
152 chrome.send('blacklistURLFromSuggestions', [this.data_.url]);
153 this.reset();
154 chrome.send('getSuggestions');
155 this.classList.add('blacklisted');
156 },
157
158 /**
159 * Shows notification that you can undo blacklisting.
160 */
161 showUndoNotification_: function() {
162 var data = this.data_;
163 var self = this;
164 var doUndo = function() {
165 chrome.send('removeURLsFromSuggestionsBlacklist', [data.url]);
166 self.updateForData(data);
167 };
168
169 var undo = {
170 action: doUndo,
171 text: templateData.undothumbnailremove,
172 };
173
174 var undoAll = {
175 action: function() {
176 chrome.send('clearSuggestionsURLsBlacklist', []);
177 },
178 text: templateData.restoreThumbnailsShort,
179 };
180
181 ntp.showNotification(templateData.thumbnailremovednotification,
182 [undo, undoAll]);
183 },
184
185 /**
186 * Set the size and position of the suggestions tile.
187 * @param {number} size The total size of |this|.
188 * @param {number} x The x-position.
189 * @param {number} y The y-position.
190 */
191 setBounds: function(size, x, y) {
192 this.style.width = size + 'px';
193 this.style.height = heightForWidth(size) + 'px';
194
195 this.style.left = x + 'px';
196 this.style.right = x + 'px';
197 this.style.top = y + 'px';
198 },
199
200 /**
201 * Returns whether this element can be 'removed' from chrome (i.e. whether
202 * the user can drag it onto the trash and expect something to happen).
203 * @return {boolean} True, since suggestions pages can always be
204 * blacklisted.
205 */
206 canBeRemoved: function() {
207 return true;
208 },
209
210 /**
211 * Removes this element from chrome, i.e. blacklists it.
212 */
213 removeFromChrome: function() {
214 this.blacklist_();
215 this.parentNode.classList.add('finishing-drag');
216 },
217
218 /**
219 * Called when a drag of this tile has ended (after all animations have
220 * finished).
221 */
222 finalizeDrag: function() {
223 this.parentNode.classList.remove('finishing-drag');
224 },
225
226 /**
227 * Called when a drag is starting on the tile. Updates dataTransfer with
228 * data for this tile (for dragging outside of the NTP).
229 * @param {Event.DataTransfer} dataTransfer The drag event data store.
230 */
231 setDragData: function(dataTransfer) {
232 dataTransfer.setData('Text', this.data_.title);
233 dataTransfer.setData('URL', this.data_.url);
234 },
235 };
236
237 var suggestionsPageGridValues = {
238 // The fewest tiles we will show in a row.
239 minColCount: 2,
240 // The suggestions we will show in a row.
241 maxColCount: 4,
242
243 // The smallest a tile can be.
244 minTileWidth: 122,
245 // The biggest a tile can be. 212 (max thumbnail width) + 2.
246 maxTileWidth: 214,
247
248 // The padding between tiles, as a fraction of the tile width.
249 tileSpacingFraction: 1 / 8,
250 };
251 TilePage.initGridValues(suggestionsPageGridValues);
252
253 /**
254 * Calculates the height for a Suggestion tile for a given width. The size
255 * is based on the thumbnail, which should have a 212:132 ratio.
256 * @return {number} The height.
257 */
258 function heightForWidth(width) {
259 // The 2s are for borders, the 31 is for the title.
260 return (width - 2) * 132 / 212 + 2 + 31;
261 }
262
263 var THUMBNAIL_COUNT = 8;
264
265 /**
266 * Creates a new SuggestionsPage object.
267 * @constructor
268 * @extends {TilePage}
269 */
270 function SuggestionsPage() {
271 var el = new TilePage(suggestionsPageGridValues);
272 el.__proto__ = SuggestionsPage.prototype;
273 el.initialize();
274
275 return el;
276 }
277
278 SuggestionsPage.prototype = {
279 __proto__: TilePage.prototype,
280
281 initialize: function() {
282 this.classList.add('suggestions-page');
283 this.data_ = null;
284 this.suggestionsTiles_ = this.getElementsByClassName('suggestions real');
285 },
286
287 /**
288 * Create blank (filler) tiles.
289 * @private
290 */
291 createTiles_: function() {
292 for (var i = 0; i < THUMBNAIL_COUNT; i++) {
293 this.appendTile(new Suggestion());
294 }
295 },
296
297 /**
298 * Update the tiles after a change to |this.data_|.
299 */
300 updateTiles_: function() {
301 for (var i = 0; i < THUMBNAIL_COUNT; i++) {
302 var page = this.data_[i];
303 var tile = this.suggestionsTiles_[i];
304
305 if (i >= this.data_.length)
306 tile.reset();
307 else
308 tile.updateForData(page);
309 }
310 },
311
312 /**
313 * Array of suggestions data objects.
314 * @type {Array}
315 */
316 get data() {
317 return this.data_;
318 },
319 set data(data) {
320 var startTime = Date.now();
321
322 // The first time data is set, create the tiles.
323 if (!this.data_) {
324 this.createTiles_();
325 this.data_ = data.slice(0, THUMBNAIL_COUNT);
326 } else {
327 this.data_ = refreshData(this.data_, data);
328 }
329
330 this.updateTiles_();
331 logEvent('suggestions.layout: ' + (Date.now() - startTime));
332 },
333
334 /** @inheritDoc */
335 shouldAcceptDrag: function(e) {
336 return false;
337 },
338
339 /** @inheritDoc */
340 heightForWidth: heightForWidth,
341 };
342
343 /**
344 * We've gotten additional data for Suggestions page. Update our old data with
345 * the new data. The ordering of the new data is not important, except when a
346 * page is pinned. Thus we try to minimize re-ordering.
347 * @param {Array} oldData The current Suggestions page list.
348 * @param {Array} newData The new Suggestions page list.
349 * @return {Array} The merged page list that should replace the current page
350 * list.
351 */
352 function refreshData(oldData, newData) {
353 oldData = oldData.slice(0, THUMBNAIL_COUNT);
354 newData = newData.slice(0, THUMBNAIL_COUNT);
355
356 // Copy over pinned sites directly.
357 for (var i = 0; i < newData.length; i++) {
358 if (newData[i].pinned) {
359 oldData[i] = newData[i];
360 // Mark the entry as 'updated' so we don't try to update again.
361 oldData[i].updated = true;
362 // Mark the newData page as 'used' so we don't try to re-use it.
363 newData[i].used = true;
364 }
365 }
366
367 // Look through old pages; if they exist in the newData list, keep them
368 // where they are.
369 for (var i = 0; i < oldData.length; i++) {
370 if (!oldData[i] || oldData[i].updated)
371 continue;
372
373 for (var j = 0; j < newData.length; j++) {
374 if (newData[j].used)
375 continue;
376
377 if (newData[j].url == oldData[i].url) {
378 // The background image and other data may have changed.
379 oldData[i] = newData[j];
380 oldData[i].updated = true;
381 newData[j].used = true;
382 break;
383 }
384 }
385 }
386
387 // Look through old pages that haven't been updated yet; replace them.
388 for (var i = 0; i < oldData.length; i++) {
389 if (oldData[i] && oldData[i].updated)
390 continue;
391
392 for (var j = 0; j < newData.length; j++) {
393 if (newData[j].used)
394 continue;
395
396 oldData[i] = newData[j];
397 oldData[i].updated = true;
398 newData[j].used = true;
399 break;
400 }
401
402 if (oldData[i] && !oldData[i].updated)
403 oldData[i] = null;
404 }
405
406 // Clear 'updated' flags so this function will work next time it's called.
407 for (var i = 0; i < THUMBNAIL_COUNT; i++) {
408 if (oldData[i])
409 oldData[i].updated = false;
410 }
411
412 return oldData;
413 }
414
415 return {
416 SuggestionsPage: SuggestionsPage,
417 refreshData: refreshData,
418 };
419 });
OLDNEW
« no previous file with comments | « chrome/browser/resources/ntp4/suggestions_page.css ('k') | chrome/browser/resources/shared/js/cr/ui/card_slider.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698