Chromium Code Reviews| Index: chrome/browser/resources/ntp_search/thumbnail_page.js |
| diff --git a/chrome/browser/resources/ntp_search/thumbnail_page.js b/chrome/browser/resources/ntp_search/thumbnail_page.js |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..6db508898f9b8538b065050b5d3e72d8394a71d1 |
| --- /dev/null |
| +++ b/chrome/browser/resources/ntp_search/thumbnail_page.js |
| @@ -0,0 +1,394 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +cr.define('ntp', function() { |
| + 'use strict'; |
| + |
| + var Tile = ntp.Tile2; |
| + var TilePage = ntp.TilePage2; |
| + |
| + /** |
| + * A counter for generating unique tile IDs. |
| + */ |
| + var tileID = 0; |
| + |
| + /** |
| + * 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.
|
| + * @constructor |
| + * @extends {HTMLAnchorElement} |
| + */ |
| + function Thumbnail(gridValues) { |
| + var el = cr.doc.createElement('a'); |
| + el.__proto__ = Thumbnail.prototype; |
| + el.initialize(gridValues); |
| + |
| + return el; |
| + } |
| + |
| + Thumbnail.prototype = Tile.subclass({ |
| + __proto__: HTMLAnchorElement.prototype, |
| + |
| + // TODO(pedrosimonetti): document |
| + isRemovable: true, |
| + |
| + initialize: function(gridValues) { |
| + Tile.prototype.initialize.apply(this, arguments); |
| + this.reset(); |
| + |
| + this.addEventListener('click', this.handleClick_); |
| + this.addEventListener('keydown', this.handleKeyDown_); |
| + }, |
| + |
| + get index() { |
| + assert(this.tile); |
| + return this.tile.index; |
| + }, |
| + |
| + get data() { |
| + return this.data_; |
| + }, |
| + |
| + /** |
| + * Clears the DOM hierarchy for this node, setting it back to the default |
| + * for a blank thumbnail. |
| + */ |
| + reset: function() { |
| + this.className = 'tile-content most-visited real'; |
| + this.innerHTML = |
| + '<span class="thumbnail-wrapper fills-parent">' + |
| + (this.isRemovable ? '<div class="close-button"></div>' : '') + |
| + '<span class="thumbnail fills-parent">' + |
| + // TODO(pedrosimonetti): remove all references (HTML+CSS+JS) |
| + // thumbnail-shield provides a gradient fade effect. |
| + //'<div class="thumbnail-shield fills-parent"></div>' + |
| + '</span>' + |
| + // TODO(pedrosimonetti): remove all references (HTML+CSS+JS) |
| + //'<span class="favicon"></span>' + |
| + '</span>' + |
| + // TODO(pedrosimonetti): remove all references (HTML+CSS+JS) |
| + //'<div class="color-stripe"></div>' + |
| + '<span class="title"></span>'; |
| + |
| + if (this.isRemovable) { |
| + this.querySelector('.close-button').title = |
| + loadTimeData.getString('removethumbnailtooltip'); |
| + } |
| + |
| + this.tabIndex = -1; |
| + this.data_ = null; |
| + this.removeAttribute('id'); |
| + this.title = ''; |
| + }, |
| + |
| + /** |
| + * Update the appearance of this tile according to |data|. |
| + * @param {Object} data A dictionary of relevant data for the page. |
| + */ |
| + updateForData: function(data) { |
| + if (this.classList.contains('blacklisted') && data) { |
| + // Animate appearance of new tile. |
| + this.classList.add('new-tile-contents'); |
| + } |
| + this.classList.remove('blacklisted'); |
| + |
| + if (!data || data.filler) { |
| + if (this.data_) |
| + this.reset(); |
| + return; |
| + } |
| + |
| + var id = tileID++; |
| + // TODO(pedrosimonetti): refactor |
| + this.id = 'most-visited-tile-' + id; |
| + this.data_ = data; |
| + this.classList.add('focusable'); |
| + |
| + // TODO(pedrosimonetti): remove all references (HTML+CSS+JS) |
| + //var faviconDiv = this.querySelector('.favicon'); |
| + //var faviconUrl = 'chrome://favicon/size/16/' + data.url; |
| + //faviconDiv.style.backgroundImage = url(faviconUrl); |
| + //chrome.send('getFaviconDominantColor', [faviconUrl, this.id]); |
| + |
| + var title = this.querySelector('.title'); |
| + title.textContent = data.title; |
| + title.dir = data.direction; |
| + |
| + // Sets the tooltip. |
| + this.title = data.title; |
| + |
| + var thumbnailUrl = ntp.getThumbnailUrl(data.url); |
| + this.querySelector('.thumbnail').style.backgroundImage = |
| + url(thumbnailUrl); |
| + |
| + this.href = data.url; |
| + |
| + this.classList.remove('filler'); |
| + }, |
| + |
| + /** |
| + * Sets the color of the favicon dominant color bar. |
| + * @param {string} color The css-parsable value for the color. |
| + */ |
| + // TODO(xci) delete |
| + //set stripeColor(color) { |
| + // this.querySelector('.color-stripe').style.backgroundColor = color; |
| + //}, |
| + |
| + /** |
| + * Handles a click on the tile. |
| + * @param {Event} e The click event. |
| + */ |
| + handleClick_: function(e) { |
| + debugger; |
| + if (e.target.classList.contains('close-button')) { |
| + this.blacklist_(); |
| + e.preventDefault(); |
| + } else { |
| + // Records an app launch from the most visited page (Chrome will decide |
| + // whether the url is an app). TODO(estade): this only works for clicks; |
| + // other actions like "open in new tab" from the context menu won't be |
| + // recorded. Can this be fixed? |
| + chrome.send('recordAppLaunchByURL', |
| + [encodeURIComponent(this.href), |
| + ntp.APP_LAUNCH.NTP_MOST_VISITED]); |
| + // Records the index of this tile. |
| + chrome.send('metricsHandler:recordInHistogram', |
| + ['NewTabPage.Thumbnail', this.index, 8]); |
| + chrome.send('mostVisitedAction', |
| + [ntp.NtpFollowAction.CLICKED_TILE]); |
| + } |
| + }, |
| + |
| + /** |
| + * 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.
|
| + */ |
| + handleKeyDown_: function(e) { |
| + if (!cr.isMac && e.keyCode == 46 || // Del |
| + cr.isMac && e.metaKey && e.keyCode == 8) { // Cmd + Backspace |
| + this.blacklist_(); |
| + } |
| + }, |
| + |
| + /** |
| + * Permanently removes a page from Most Visited. |
| + */ |
| + blacklist_: function() { |
| + this.showUndoNotification_(); |
| + chrome.send('blacklistURLFromThumbnail', [this.data_.url]); |
| + this.reset(); |
| + chrome.send('getThumbnail'); |
| + this.classList.add('blacklisted'); |
| + }, |
| + |
| + showUndoNotification_: function() { |
| + var data = this.data_; |
| + var self = this; |
| + var doUndo = function() { |
| + chrome.send('removeURLsFromThumbnailBlacklist', [data.url]); |
| + self.updateForData(data); |
| + } |
| + |
| + var undo = { |
| + action: doUndo, |
| + text: loadTimeData.getString('undothumbnailremove'), |
| + }; |
| + |
| + var undoAll = { |
| + action: function() { |
| + chrome.send('clearThumbnailURLsBlacklist'); |
| + }, |
| + text: loadTimeData.getString('restoreThumbnailsShort'), |
| + }; |
| + |
| + ntp.showNotification( |
| + loadTimeData.getString('thumbnailremovednotification'), |
| + [undo, undoAll]); |
| + }, |
| + |
| + /** |
| + * Returns whether this element can be 'removed' from chrome (i.e. whether |
| + * the user can drag it onto the trash and expect something to happen). |
| + * @return {boolean} True, since most visited pages can always be |
| + * blacklisted. |
| + */ |
| + canBeRemoved: function() { |
| + return this.isRemovable; |
| + }, |
| + |
| + /** |
| + * Removes this element from chrome, i.e. blacklists it. |
| + */ |
| + removeFromChrome: function() { |
| + this.blacklist_(); |
| + this.parentNode.classList.add('finishing-drag'); |
| + }, |
| + |
| + /** |
| + * Called when a drag of this tile has ended (after all animations have |
| + * finished). |
| + */ |
| + finalizeDrag: function() { |
| + this.parentNode.classList.remove('finishing-drag'); |
| + }, |
| + |
| + /** |
| + * Called when a drag is starting on the tile. Updates dataTransfer with |
| + * data for this tile (for dragging outside of the NTP). |
| + */ |
| + setDragData: function(dataTransfer) { |
| + dataTransfer.setData('Text', this.data_.title); |
| + dataTransfer.setData('URL', this.data_.url); |
| + }, |
| + }); |
| + |
| + 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
|
| + |
| + /** |
| + * Creates a new ThumbnailPage object. |
| + * @constructor |
| + * @extends {TilePage} |
| + */ |
| + function ThumbnailPage() { |
| + var el = new TilePage(); |
| + el.__proto__ = ThumbnailPage.prototype; |
| + el.initialize(); |
| + |
| + return el; |
| + } |
| + |
| + ThumbnailPage.prototype = { |
| + __proto__: TilePage.prototype, |
| + |
| + gridValues_: { |
| + tileWidth: 130, |
| + tileHeight: 78, |
| + tileHorMargin: 18, // TODO margin with CSS / there's no margin in first col |
| + tileVerMargin: 22, |
| + tileBorderWidth: 1, |
| + bottomPanelHorMargin: 100, |
| + |
| + tileCount: 10, // debug |
| + tileClassName: 'thumbnail', |
| + reinforceStyles: true, |
|
jeremycho_google
2012/07/31 03:09:16
Please document.
pedrosimonetti2
2012/08/03 18:14:01
Done.
|
| + |
| + // debug |
| + slowFactor: 1, |
| + debug: false |
| + }, |
| + |
| + // TODO(pedrosimonetti): document |
| + ThumbnailClass: Thumbnail, |
| + |
| + initialize: function() { |
| + 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
|
| + this.data_ = null; |
| + this.mostVisitedTiles_ = this.getElementsByClassName('most-visited real'); |
| + |
| + this.addEventListener('carddeselected', this.handleCardDeselected_); |
| + this.addEventListener('cardselected', this.handleCardSelected_); |
| + }, |
| + |
| + /** |
| + * Create blank (filler) tiles. |
| + * @private |
| + */ |
| + createTiles_: function() { |
| + for (var i = 0; i < THUMBNAIL_COUNT; i++) { |
| + this.appendTile(new this.ThumbnailClass(this.gridValues_)); |
| + } |
| + }, |
| + |
| + /** |
| + * Update the tiles after a change to |data_|. |
| + */ |
| + updateTiles_: function() { |
| + for (var i = 0; i < THUMBNAIL_COUNT; i++) { |
| + var page = this.data_[i]; |
| + var tile = this.mostVisitedTiles_[i]; |
| + |
| + // TODO(pedrosimonetti): what do we do when there's no tile here? |
| + if (!tile) { |
| + return; |
| + } |
| + |
| + if (i >= this.data_.length) |
| + tile.reset(); |
| + else |
| + tile.updateForData(page); |
| + } |
| + }, |
| + |
| + /** |
| + * Handles the 'card deselected' event (i.e. the user clicked to another |
| + * pane). |
| + * @param {Event} e The CardChanged event. |
| + */ |
| + handleCardDeselected_: function(e) { |
| + if (!document.documentElement.classList.contains('starting-up')) { |
| + chrome.send('mostVisitedAction', |
| + [ntp.NtpFollowAction.CLICKED_OTHER_NTP_PANE]); |
| + } |
| + }, |
| + |
| + /** |
| + * Handles the 'card selected' event (i.e. the user clicked to select the |
| + * Most Visited pane). |
| + * @param {Event} e The CardChanged event. |
| + */ |
| + handleCardSelected_: function(e) { |
| + if (!document.documentElement.classList.contains('starting-up')) |
| + chrome.send('mostVisitedSelected'); |
| + }, |
| + |
| + /** |
| + * Array of most visited data objects. |
| + * @type {Array} |
| + */ |
| + get data() { |
| + return this.data_; |
| + }, |
| + set data(data) { |
| + var startTime = Date.now(); |
| + |
| + // The first time data is set, create the tiles. |
| + if (!this.data_) { |
| + this.createTiles_(); |
| + this.data_ = data.slice(0, THUMBNAIL_COUNT); |
| + } else { |
| + this.data_ = refreshData(this.data_, data); |
| + } |
| + |
| + this.updateTiles_(); |
| + logEvent('mostVisited.layout: ' + (Date.now() - startTime)); |
| + }, |
| + |
| + /** @inheritDoc */ |
| + shouldAcceptDrag: function(e) { |
| + return false; |
| + }, |
| + }; |
| + |
| + /** |
| + * Executed once the NTP has loaded. Checks if the Most Visited pane is |
| + * shown or not. If it is shown, the 'mostVisitedSelected' message is sent |
| + * to the C++ code, to record the fact that the user has seen this pane. |
| + */ |
| + // TODO(pedrosimonetti) is it possible to keep this inside Thumbnail class? |
| + /* |
| + ThumbnailPage.onLoaded = function() { |
| + if (ntp.getCardSlider() && |
| + ntp.getCardSlider().currentCardValue && |
| + ntp.getCardSlider().currentCardValue.classList |
| + .contains('most-visited-page')) { |
| + chrome.send('mostVisitedSelected'); |
| + } |
| + } |
| + */ |
| + |
| + return { |
| + Thumbnail: Thumbnail, |
| + ThumbnailPage: ThumbnailPage |
| + }; |
| +}); |
| + |