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 |
+ }; |
+}); |
+ |