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

Unified Diff: chrome/browser/resources/ntp_search/tile_page.js

Issue 10829131: Refactoring NTP5: new implementation of TilePage and MostVisitedPage (which now inherits from Thumb… (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Manually fixing mock_data_autogen.js lines with more than 80 chars. Created 8 years, 4 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/resources/ntp_search/tile_page.css ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/resources/ntp_search/tile_page.js
diff --git a/chrome/browser/resources/ntp_search/tile_page.js b/chrome/browser/resources/ntp_search/tile_page.js
index 924600e3bf2a45a23914cfbc594aec9e53e85cb3..7499dc22747055c40115976377474c501bb31793 100644
--- a/chrome/browser/resources/ntp_search/tile_page.js
+++ b/chrome/browser/resources/ntp_search/tile_page.js
@@ -5,291 +5,138 @@
cr.define('ntp', function() {
'use strict';
- // We can't pass the currently dragging tile via dataTransfer because of
- // http://crbug.com/31037
- var currentlyDraggingTile = null;
- function getCurrentlyDraggingTile() {
- return currentlyDraggingTile;
- }
- function setCurrentlyDraggingTile(tile) {
- currentlyDraggingTile = tile;
- if (tile)
- ntp.enterRearrangeMode();
- else
- ntp.leaveRearrangeMode();
- }
+ //----------------------------------------------------------------------------
+ // Tile
+ //----------------------------------------------------------------------------
/**
- * Changes the current dropEffect of a drag. This modifies the native cursor
- * and serves as an indicator of what we should do at the end of the drag as
- * well as give indication to the user if a drop would succeed if they let go.
- * @param {DataTransfer} dataTransfer A dataTransfer object from a drag event.
- * @param {string} effect A drop effect to change to (i.e. copy, move, none).
+ * A virtual Tile class. Each TilePage subclass should have its own Tile
+ * subclass implemented too (e.g. MostVisitedPage contains MostVisited
+ * tiles, and MostVisited is a Tile subclass).
+ * @constructor
+ * @param {Object} config TilePage configuration object.
*/
- function setCurrentDropEffect(dataTransfer, effect) {
- dataTransfer.dropEffect = effect;
- if (currentlyDraggingTile)
- currentlyDraggingTile.lastDropEffect = dataTransfer.dropEffect;
+ function Tile(config) {
+ console.error('Tile is a virtual class and is not supposed to be ' +
+ 'instantiated');
}
/**
- * Creates a new Tile object. Tiles wrap content on a TilePage, providing
- * some styling and drag functionality.
- * @constructor
- * @extends {HTMLDivElement}
+ * Creates a Tile subclass. We need to use this function to create a Tile
+ * subclass because a Tile must also subclass a HTMLElement (which can be
+ * any HTMLElement), so we need to individually add methods and getters here.
+ * @param {Object} Subclass The prototype object of the class we want to be
+ * a Tile subclass.
+ * @param {Object} The extended Subclass object.
*/
- function Tile(contents) {
- var tile = cr.doc.createElement('div');
- tile.__proto__ = Tile.prototype;
- tile.initialize(contents);
-
- return tile;
- }
+ Tile.subclass = function(Subclass) {
+ var Base = Tile.prototype;
+ for (var name in Base) {
+ if (!Subclass.hasOwnProperty(name))
+ Subclass[name] = Base[name];
+ }
+ for (var name in TileGetters) {
+ if (!Subclass.hasOwnProperty(name))
+ Subclass.__defineGetter__(name, TileGetters[name]);
+ }
+ return Subclass;
+ };
Tile.prototype = {
- __proto__: HTMLDivElement.prototype,
-
- initialize: function(contents) {
- // 'real' as opposed to doppleganger.
- this.className = 'tile real';
- this.appendChild(contents);
- contents.tile = this;
-
- this.addEventListener('dragstart', this.onDragStart_);
- this.addEventListener('drag', this.onDragMove_);
- this.addEventListener('dragend', this.onDragEnd_);
-
- this.firstChild.addEventListener(
- 'webkitAnimationEnd', this.onContentsAnimationEnd_.bind(this));
-
- this.eventTracker = new EventTracker();
- },
-
- get index() {
- return Array.prototype.indexOf.call(this.tilePage.tileElements_, this);
- },
-
- get tilePage() {
- return findAncestorByClass(this, 'tile-page');
- },
-
- /**
- * Position the tile at |x, y|, and store this as the grid location, i.e.
- * where the tile 'belongs' when it's not being dragged.
- * @param {number} x The x coordinate, in pixels.
- * @param {number} y The y coordinate, in pixels.
- */
- setGridPosition: function(x, y) {
- this.gridX = x;
- this.gridY = y;
- this.moveTo(x, y);
- },
-
/**
- * Position the tile at |x, y|.
- * @param {number} x The x coordinate, in pixels.
- * @param {number} y The y coordinate, in pixels.
+ * Initializes a Tile.
+ * @param {Object} config TilePage configuration object.
*/
- moveTo: function(x, y) {
- // left overrides right in LTR, and right takes precedence in RTL.
- this.style.left = toCssPx(x);
- this.style.right = toCssPx(x);
- this.style.top = toCssPx(y);
- },
-
- /**
- * The handler for dragstart events fired on |this|.
- * @param {Event} e The event for the drag.
- * @private
- */
- onDragStart_: function(e) {
- // The user may start dragging again during a previous drag's finishing
- // animation.
- if (this.classList.contains('dragging'))
- this.finalizeDrag_();
-
- setCurrentlyDraggingTile(this);
-
- e.dataTransfer.effectAllowed = 'copyMove';
- this.firstChild.setDragData(e.dataTransfer);
-
- // The drag clone is the node we use as a representation during the drag.
- // It's attached to the top level document element so that it floats above
- // image masks.
- this.dragClone = this.cloneNode(true);
- this.dragClone.style.right = '';
- this.dragClone.classList.add('drag-representation');
- $('card-slider-frame').appendChild(this.dragClone);
- this.eventTracker.add(this.dragClone, 'webkitTransitionEnd',
- this.onDragCloneTransitionEnd_.bind(this));
-
- this.classList.add('dragging');
- // offsetLeft is mirrored in RTL. Un-mirror it.
- var offsetLeft = isRTL() ?
- this.parentNode.clientWidth - this.offsetLeft :
- this.offsetLeft;
- this.dragOffsetX = e.x - offsetLeft - this.parentNode.offsetLeft;
- this.dragOffsetY = e.y - this.offsetTop -
- // Unlike offsetTop, this value takes scroll position into account.
- this.parentNode.getBoundingClientRect().top;
-
- this.onDragMove_(e);
- },
+ initialize: function(config) {
+ this.className = 'tile';
+ }
+ };
+ var TileGetters = {
/**
- * The handler for drag events fired on |this|.
- * @param {Event} e The event for the drag.
- * @private
+ * The TileCell associated to this Tile.
+ * @type {TileCell}
*/
- onDragMove_: function(e) {
- if (e.view != window || (e.x == 0 && e.y == 0)) {
- this.dragClone.hidden = true;
- return;
- }
-
- this.dragClone.hidden = false;
- this.dragClone.style.left = toCssPx(e.x - this.dragOffsetX);
- this.dragClone.style.top = toCssPx(e.y - this.dragOffsetY);
+ 'tileCell': function() {
+ return findAncestorByClass(this, 'tile-cell');
},
/**
- * The handler for dragend events fired on |this|.
- * @param {Event} e The event for the drag.
- * @private
+ * The index of the Tile.
+ * @type {number}
*/
- onDragEnd_: function(e) {
- this.dragClone.hidden = false;
- this.dragClone.classList.add('placing');
-
- setCurrentlyDraggingTile(null);
-
- // tilePage will be null if we've already been removed.
- var tilePage = this.tilePage;
- if (tilePage)
- tilePage.positionTile_(this.index);
-
- // Take an appropriate action with the drag clone.
- if (this.landedOnTrash) {
- this.dragClone.classList.add('deleting');
- } else if (tilePage) {
- // TODO(dbeam): Until we fix dropEffect to the correct behavior it will
- // differ on windows - crbug.com/39399. That's why we use the custom
- // this.lastDropEffect instead of e.dataTransfer.dropEffect.
- if (tilePage.selected && this.lastDropEffect != 'copy') {
- // The drag clone can still be hidden from the last drag move event.
- this.dragClone.hidden = false;
- // The tile's contents may have moved following the respositioning;
- // adjust for that.
- var contentDiffX = this.dragClone.firstChild.offsetLeft -
- this.firstChild.offsetLeft;
- var contentDiffY = this.dragClone.firstChild.offsetTop -
- this.firstChild.offsetTop;
- this.dragClone.style.left =
- toCssPx(this.gridX + this.parentNode.offsetLeft -
- contentDiffX);
- this.dragClone.style.top =
- toCssPx(this.gridY +
- this.parentNode.getBoundingClientRect().top -
- contentDiffY);
- } else if (this.dragClone.hidden) {
- this.finalizeDrag_();
- } else {
- // The CSS3 transitions spec intentionally leaves it up to individual
- // user agents to determine when styles should be applied. On some
- // platforms (at the moment, Windows), when you apply both classes
- // immediately a transition may not occur correctly. That's why we're
- // using a setTimeout here to queue adding the class until the
- // previous class (currently: .placing) sets up a transition.
- // http://dev.w3.org/csswg/css3-transitions/#starting
- window.setTimeout(function() {
- if (this.dragClone)
- this.dragClone.classList.add('dropped-on-other-page');
- }.bind(this), 0);
- }
- }
-
- delete this.lastDropEffect;
- this.landedOnTrash = false;
- },
+ 'index': function() {
+ assert(this.tileCell);
+ return this.tileCell.index;
+ }
+ };
- /**
- * Creates a clone of this node offset by the coordinates. Used for the
- * dragging effect where a tile appears to float off one side of the grid
- * and re-appear on the other.
- * @param {number} x x-axis offset, in pixels.
- * @param {number} y y-axis offset, in pixels.
- */
- showDoppleganger: function(x, y) {
- // We always have to clear the previous doppleganger to make sure we get
- // style updates for the contents of this tile.
- this.clearDoppleganger();
-
- var clone = this.cloneNode(true);
- clone.classList.remove('real');
- clone.classList.add('doppleganger');
- var clonelets = clone.querySelectorAll('.real');
- for (var i = 0; i < clonelets.length; i++) {
- clonelets[i].classList.remove('real');
- }
+ //----------------------------------------------------------------------------
+ // TileCell
+ //----------------------------------------------------------------------------
- this.appendChild(clone);
- this.doppleganger_ = clone;
+ /**
+ * Creates a new TileCell object. A TileCell represents a cell in the
+ * TilePage's grid. A TilePage uses TileCells to position Tiles in the proper
+ * place and to animate them individually. Each TileCell is associated to
+ * one Tile at a time (or none if it is a filler object), and that association
+ * might change when the grid is resized. When that happens, the grid is
+ * updated and the Tiles are moved to the proper TileCell. We cannot move the
+ * the TileCell itself during the resize because this transition is animated
+ * with CSS and there's no way to stop CSS animations, and we really want to
+ * animate with CSS to take advantage of hardware acceleration.
+ * @constructor
+ * @extends {HTMLDivElement}
+ * @param {HTMLElement} tile Tile element that will be associated to the cell.
+ * @param {Object} config TilePage configuration object.
+ */
+ function TileCell(tile, config) {
+ var tileCell = cr.doc.createElement('div');
+ tileCell.__proto__ = TileCell.prototype;
+ tileCell.initialize(tile, config);
- if (isRTL())
- x *= -1;
+ return tileCell;
+ }
- this.doppleganger_.style.WebkitTransform = 'translate(' + x + 'px, ' +
- y + 'px)';
- },
+ TileCell.prototype = {
+ __proto__: HTMLDivElement.prototype,
/**
- * Destroys the current doppleganger.
+ * Initializes a TileCell.
+ * @param {Tile} tile The Tile that will be assigned to this TileCell.
+ * @param {Object} config TilePage configuration object.
*/
- clearDoppleganger: function() {
- if (this.doppleganger_) {
- this.removeChild(this.doppleganger_);
- this.doppleganger_ = null;
- }
+ initialize: function(tile, config) {
+ this.className = 'tile-cell';
+ this.assign_(tile);
},
/**
- * Returns status of doppleganger.
- * @return {boolean} True if there is a doppleganger showing for |this|.
+ * The index of the TileCell.
+ * @type {number}
*/
- hasDoppleganger: function() {
- return !!this.doppleganger_;
+ get index() {
+ return Array.prototype.indexOf.call(this.tilePage.tiles_,
+ this.firstChild);
},
/**
- * Cleans up after the drag is over. This is either called when the
- * drag representation finishes animating to the final position, or when
- * the next drag starts (if the user starts a 2nd drag very quickly).
- * @private
+ * The TilePage associated to this TileCell.
+ * @type {TilePage}
*/
- finalizeDrag_: function() {
- assert(this.classList.contains('dragging'));
-
- var clone = this.dragClone;
- this.dragClone = null;
-
- clone.parentNode.removeChild(clone);
- this.eventTracker.remove(clone, 'webkitTransitionEnd');
- this.classList.remove('dragging');
- if (this.firstChild.finalizeDrag)
- this.firstChild.finalizeDrag();
+ get tilePage() {
+ return findAncestorByClass(this, 'tile-page');
},
/**
- * Called when the drag representation node is done migrating to its final
- * resting spot.
- * @param {Event} e The transition end event.
+ * Assigns a Tile to the this TileCell.
+ * @type {TilePage}
*/
- onDragCloneTransitionEnd_: function(e) {
- if (this.classList.contains('dragging') &&
- (e.propertyName == 'left' || e.propertyName == 'top' ||
- e.propertyName == '-webkit-transform')) {
- this.finalizeDrag_();
- }
+ assign_: function(tile) {
+ if (this.firstChild)
+ this.replaceChild(tile, this.firstChild);
+ else
+ this.appendChild(tile);
},
/**
@@ -301,206 +148,103 @@ cr.define('ntp', function() {
this.firstChild.classList.add('removing-tile-contents');
else
this.tilePage.removeTile(this, false);
- },
-
- /**
- * Callback for the webkitAnimationEnd event on the tile's contents.
- * @param {Event} e The event object.
- */
- onContentsAnimationEnd_: function(e) {
- if (this.firstChild.classList.contains('new-tile-contents'))
- this.firstChild.classList.remove('new-tile-contents');
- if (this.firstChild.classList.contains('removing-tile-contents'))
- this.tilePage.removeTile(this, true);
- },
+ }
};
- /**
- * Gives the proportion of the row width that is devoted to a single icon.
- * @param {number} rowTileCount The number of tiles in a row.
- * @param {number} tileSpacingFraction The proportion of the tile width which
- * will be used as spacing between tiles.
- * @return {number} The ratio between icon width and row width.
- */
- function tileWidthFraction(rowTileCount, tileSpacingFraction) {
- return rowTileCount + (rowTileCount - 1) * tileSpacingFraction;
- }
-
- /**
- * Calculates an assortment of tile-related values for a grid with the
- * given dimensions.
- * @param {number} width The pixel width of the grid.
- * @param {number} numRowTiles The number of tiles in a row.
- * @param {number} tileSpacingFraction The proportion of the tile width which
- * will be used as spacing between tiles.
- * @return {Object} A mapping of pixel values.
- */
- function tileValuesForGrid(width, numRowTiles, tileSpacingFraction) {
- var tileWidth = width / tileWidthFraction(numRowTiles, tileSpacingFraction);
- var offsetX = tileWidth * (1 + tileSpacingFraction);
- var interTileSpacing = offsetX - tileWidth;
-
- return {
- tileWidth: tileWidth,
- offsetX: offsetX,
- interTileSpacing: interTileSpacing,
- };
- }
-
- // The smallest amount of horizontal blank space to display on the sides when
- // displaying a wide arrangement. There is an additional 26px of margin from
- // the tile page padding.
- var MIN_WIDE_MARGIN = 18;
+ //----------------------------------------------------------------------------
+ // TilePage
+ //----------------------------------------------------------------------------
/**
* Creates a new TilePage object. This object contains tiles and controls
* their layout.
- * @param {Object} gridValues Pixel values that define the size and layout
- * of the tile grid.
* @constructor
* @extends {HTMLDivElement}
*/
- function TilePage(gridValues) {
+ function TilePage() {
var el = cr.doc.createElement('div');
- el.gridValues_ = gridValues;
el.__proto__ = TilePage.prototype;
el.initialize();
return el;
}
- /**
- * Takes a collection of grid layout pixel values and updates them with
- * additional tiling values that are calculated from TilePage constants.
- * @param {Object} grid The grid layout pixel values to update.
- */
- TilePage.initGridValues = function(grid) {
- // The amount of space we need to display a narrow grid (all narrow grids
- // are this size).
- grid.narrowWidth =
- grid.minTileWidth * tileWidthFraction(grid.minColCount,
- grid.tileSpacingFraction);
- // The minimum amount of space we need to display a wide grid.
- grid.minWideWidth =
- grid.minTileWidth * tileWidthFraction(grid.maxColCount,
- grid.tileSpacingFraction);
- // The largest we will ever display a wide grid.
- grid.maxWideWidth =
- grid.maxTileWidth * tileWidthFraction(grid.maxColCount,
- grid.tileSpacingFraction);
- // Tile-related pixel values for the narrow display.
- grid.narrowTileValues = tileValuesForGrid(grid.narrowWidth,
- grid.minColCount,
- grid.tileSpacingFraction);
- // Tile-related pixel values for the minimum narrow display.
- grid.wideTileValues = tileValuesForGrid(grid.minWideWidth,
- grid.maxColCount,
- grid.tileSpacingFraction);
- };
-
TilePage.prototype = {
__proto__: HTMLDivElement.prototype,
+ // The config object should be defined by each TilePage subclass.
+ config_: {
+ // The width of a cell.
+ cellWidth: 0,
+ // The start margin of a cell (left or right according to text direction).
+ cellMarginStart: 0,
+ // The border panel horizontal margin.
+ bottomPanelHorizontalMargin: 0,
+ // The height of the tile row.
+ rowHeight: 0,
+ // The maximum number of Tiles to be displayed.
+ maxTileCount: 0
+ },
+
+ /**
+ * Initializes a TilePage.
+ */
initialize: function() {
this.className = 'tile-page';
- // Div that acts as a custom scrollbar. The scrollbar has to live
- // outside the content div so it doesn't flicker when scrolling (due to
- // repainting after the scroll, then repainting again when moved in the
- // onScroll handler). |scrollbar_| is only aesthetic, and it only
- // represents the thumb. Actual events are still handled by the invisible
- // native scrollbars. This div gives us more flexibility with the visuals.
- this.scrollbar_ = this.ownerDocument.createElement('div');
- this.scrollbar_.className = 'tile-page-scrollbar';
- this.scrollbar_.hidden = true;
- this.appendChild(this.scrollbar_);
-
- // This contains everything but the scrollbar.
+ // The content defines the actual space a page has to display tiles.
this.content_ = this.ownerDocument.createElement('div');
this.content_.className = 'tile-page-content';
this.appendChild(this.content_);
- // Div that sets the vertical position of the tile grid.
- this.topMargin_ = this.ownerDocument.createElement('div');
- this.topMargin_.className = 'top-margin';
- this.content_.appendChild(this.topMargin_);
-
- // Div that holds the tiles.
+ // The div that defines the tile grid viewport.
this.tileGrid_ = this.ownerDocument.createElement('div');
this.tileGrid_.className = 'tile-grid';
- this.tileGrid_.style.minWidth = this.gridValues_.narrowWidth + 'px';
this.content_.appendChild(this.tileGrid_);
- // Ordered list of our tiles.
- this.tileElements_ = this.tileGrid_.getElementsByClassName('tile real');
- // Ordered list of the elements which want to accept keyboard focus. These
- // elements will not be a part of the normal tab order; the tile grid
- // initially gets focused and then these elements can be focused via the
- // arrow keys.
- this.focusableElements_ =
- this.tileGrid_.getElementsByClassName('focusable');
+ // The tile grid contents, which can be scrolled.
+ this.tileGridContent_ = this.ownerDocument.createElement('div');
+ this.tileGridContent_.className = 'tile-grid-content';
+ this.tileGrid_.appendChild(this.tileGridContent_);
- // These are properties used in updateTopMargin.
- this.animatedTopMarginPx_ = 0;
- this.topMarginPx_ = 0;
+ // The list of Tile elements which is used to fill the TileGrid cells.
+ this.tiles_ = [];
+
+ // Event handlers.
+ this.tileGrid_.addEventListener('webkitTransitionEnd',
+ this.onTileGridAnimationEnd_.bind(this));
this.eventTracker = new EventTracker();
this.eventTracker.add(window, 'resize', this.onResize_.bind(this));
-
- this.addEventListener('DOMNodeInsertedIntoDocument',
- this.onNodeInsertedIntoDocument_);
-
- this.content_.addEventListener('scroll', this.onScroll_.bind(this));
-
- this.dragWrapper_ = new cr.ui.DragWrapper(this.tileGrid_, this);
+ this.eventTracker.add(window, 'keyup', this.onKeyUp_.bind(this));
this.addEventListener('cardselected', this.handleCardSelection_);
this.addEventListener('carddeselected', this.handleCardDeselection_);
- this.addEventListener('focus', this.handleFocus_);
- this.addEventListener('keydown', this.handleKeyDown_);
- this.addEventListener('mousedown', this.handleMouseDown_);
-
- this.focusElementIndex_ = -1;
- },
-
- get tiles() {
- return this.tileElements_;
- },
-
- get tileCount() {
- return this.tileElements_.length;
- },
-
- get selected() {
- return Array.prototype.indexOf.call(this.parentNode.children, this) ==
- ntp.getCardSlider().currentCard;
},
/**
- * The size of the margin (unused space) on the sides of the tile grid, in
- * pixels.
- * @type {number}
+ * The list of Tile elements.
+ * @type {Array<Tile>}
*/
- get sideMargin() {
- return this.layoutValues_.leftMargin;
+ get tiles() {
+ return this.tiles_;
},
/**
- * Returns the width of the scrollbar, in pixels, if it is active, or 0
- * otherwise.
+ * The number of Tiles in this TilePage.
* @type {number}
*/
- get scrollbarWidth() {
- return this.scrollbar_.hidden ? 0 : 13;
+ get tileCount() {
+ return this.tiles_.length;
},
/**
- * Returns any extra padding to insert to the bottom of a tile page. By
- * default there is none, but subclasses can override.
- * @type {number}
+ * Whether or not this TilePage is selected.
+ * @type {boolean}
*/
- get extraBottomPadding() {
- return 0;
+ get selected() {
+ return Array.prototype.indexOf.call(this.parentNode.children, this) ==
+ ntp.getCardSlider().currentCard;
},
/**
@@ -508,8 +252,8 @@ cr.define('ntp', function() {
* @type {!HTMLElement}
*/
get notification() {
- return this.topMargin_.nextElementSibling.id == 'notification-container' ?
- this.topMargin_.nextElementSibling : null;
+ return this.content_.firstChild.id == 'notification-container' ?
+ this.content_.firstChild : null;
},
/**
* The notification content of this tile (if any, otherwise null).
@@ -518,23 +262,11 @@ cr.define('ntp', function() {
set notification(node) {
assert(node instanceof HTMLElement, '|node| isn\'t an HTMLElement!');
// NOTE: Implicitly removes from DOM if |node| is inside it.
- this.content_.insertBefore(node, this.topMargin_.nextElementSibling);
+ this.content_.insertBefore(node, this.content_.firstChild);
this.positionNotification_();
},
/**
- * Fetches the size, in pixels, of the padding-top of the tile contents.
- * @type {number}
- */
- get contentPadding() {
- if (typeof this.contentPadding_ == 'undefined') {
- this.contentPadding_ =
- parseInt(getComputedStyle(this.content_).paddingTop, 10);
- }
- return this.contentPadding_;
- },
-
- /**
* Removes the tilePage from the DOM and cleans up event handlers.
*/
remove: function() {
@@ -558,47 +290,9 @@ cr.define('ntp', function() {
},
/**
- * Appends a tile to the end of the tile grid.
- * @param {HTMLElement} tileElement The contents of the tile.
- * @param {boolean} animate If true, the append will be animated.
- * @protected
- */
- appendTile: function(tileElement, animate) {
- this.addTileAt(tileElement, this.tileElements_.length, animate);
- },
-
- /**
- * Adds the given element to the tile grid.
- * @param {Node} tileElement The tile object/node to insert.
- * @param {number} index The location in the tile grid to insert it at.
- * @param {boolean} animate If true, the tile in question will be
- * animated (other tiles, if they must reposition, do not animate).
- * @protected
- */
- addTileAt: function(tileElement, index, animate) {
- this.classList.remove('animating-tile-page');
- if (animate)
- tileElement.classList.add('new-tile-contents');
-
- // Make sure the index is positive and either in the the bounds of
- // this.tileElements_ or at the end (meaning append).
- assert(index >= 0 && index <= this.tileElements_.length);
-
- var wrapperDiv = new Tile(tileElement);
- // If is out of the bounds of the tile element list, .insertBefore() will
- // act just like appendChild().
- this.tileGrid_.insertBefore(wrapperDiv, this.tileElements_[index]);
- this.calculateLayoutValues_();
- this.heightChanged_();
-
- this.repositionTiles_();
- this.fireAddedEvent(wrapperDiv, index, animate);
- },
-
- /**
* Notify interested subscribers that a tile has been removed from this
- * page.
- * @param {Tile} tile The newly added tile.
+ * page. TODO(pedrosimonetti): Do we really need to fire this event?
+ * @param {TileCell} tile The newly added tile.
* @param {number} index The index of the tile that was added.
* @param {boolean} wasAnimated Whether the removal was animated.
*/
@@ -632,7 +326,7 @@ cr.define('ntp', function() {
/**
* Notify interested subscribers that a tile has been removed from this
* page.
- * @param {Tile} tile The tile that was removed.
+ * @param {TileCell} tile The tile that was removed.
* @param {number} oldIndex Where the tile was positioned before removal.
* @param {boolean} wasAnimated Whether the removal was animated.
*/
@@ -649,6 +343,7 @@ cr.define('ntp', function() {
* Removes all tiles from the page.
*/
removeAllTiles: function() {
+ // TODO(pedrosimonetti): Dispatch individual tearDown functions.
this.tileGrid_.innerHTML = '';
},
@@ -658,8 +353,6 @@ cr.define('ntp', function() {
* @private
*/
handleCardSelection_: function(e) {
- this.tabIndex = 1;
-
// When we are selected, we re-calculate the layout values. (See comment
// in doDrop.)
this.calculateLayoutValues_();
@@ -671,122 +364,6 @@ cr.define('ntp', function() {
* @private
*/
handleCardDeselection_: function(e) {
- this.tabIndex = -1;
- if (this.currentFocusElement_)
- this.currentFocusElement_.tabIndex = -1;
- },
-
- /**
- * When we get focus, pass it on to the focus element.
- * @param {Event} e The focus event.
- * @private
- */
- handleFocus_: function(e) {
- if (this.focusableElements_.length == 0)
- return;
-
- this.updateFocusElement_();
- },
-
- /**
- * Since we are doing custom focus handling, we have to manually
- * set focusability on click (as well as keyboard nav above).
- * @param {Event} e The focus event.
- * @private
- */
- handleMouseDown_: function(e) {
- var focusable = findAncestorByClass(e.target, 'focusable');
- if (focusable) {
- this.focusElementIndex_ =
- Array.prototype.indexOf.call(this.focusableElements_,
- focusable);
- this.updateFocusElement_();
- } else {
- // This prevents the tile page from getting focus when the user clicks
- // inside the grid but outside of any tile.
- e.preventDefault();
- }
- },
-
- /**
- * Handle arrow key focus nav.
- * @param {Event} e The focus event.
- * @private
- */
- handleKeyDown_: function(e) {
- // We only handle up, down, left, right without control keys.
- if (e.metaKey || e.shiftKey || e.altKey || e.ctrlKey)
- return;
-
- // Wrap the given index to |this.focusableElements_|.
- var wrap = function(idx) {
- return (idx + this.focusableElements_.length) %
- this.focusableElements_.length;
- }.bind(this);
-
- switch (e.keyIdentifier) {
- case 'Right':
- case 'Left':
- var direction = e.keyIdentifier == 'Right' ? 1 : -1;
- this.focusElementIndex_ = wrap(this.focusElementIndex_ + direction);
- break;
- case 'Up':
- case 'Down':
- // Look through all focusable elements. Find the first one that is
- // in the same column.
- var direction = e.keyIdentifier == 'Up' ? -1 : 1;
- var currentIndex =
- Array.prototype.indexOf.call(this.focusableElements_,
- this.currentFocusElement_);
- var newFocusIdx = wrap(currentIndex + direction);
- var tile = this.currentFocusElement_.parentNode;
- for (;; newFocusIdx = wrap(newFocusIdx + direction)) {
- var newTile = this.focusableElements_[newFocusIdx].parentNode;
- var rowTiles = this.layoutValues_.numRowTiles;
- if ((newTile.index - tile.index) % rowTiles == 0)
- break;
- }
-
- this.focusElementIndex_ = newFocusIdx;
- break;
-
- default:
- return;
- }
-
- this.updateFocusElement_();
-
- e.preventDefault();
- e.stopPropagation();
- },
-
- /**
- * Focuses the element for |this.focusElementIndex_|. Makes the current
- * focus element, if any, no longer eligible for focus.
- * @private
- */
- updateFocusElement_: function() {
- this.focusElementIndex_ = Math.min(this.focusableElements_.length - 1,
- this.focusElementIndex_);
- this.focusElementIndex_ = Math.max(0, this.focusElementIndex_);
-
- var newFocusElement = this.focusableElements_[this.focusElementIndex_];
- var lastFocusElement = this.currentFocusElement_;
- if (lastFocusElement && lastFocusElement != newFocusElement)
- lastFocusElement.tabIndex = -1;
-
- newFocusElement.tabIndex = 1;
- newFocusElement.focus();
- this.tabIndex = -1;
- },
-
- /**
- * The current focus element is that element which is eligible for focus.
- * @type {HTMLElement} The node.
- * @private
- */
- get currentFocusElement_() {
- return this.querySelector('.focusable[tabindex="1"]');
},
/**
@@ -797,40 +374,9 @@ cr.define('ntp', function() {
* @private
*/
calculateLayoutValues_: function() {
- var grid = this.gridValues_;
- var availableSpace = this.tileGrid_.clientWidth - 2 * MIN_WIDE_MARGIN;
- var wide = availableSpace >= grid.minWideWidth;
- var numRowTiles = wide ? grid.maxColCount : grid.minColCount;
-
- var effectiveGridWidth = wide ?
- Math.min(Math.max(availableSpace, grid.minWideWidth),
- grid.maxWideWidth) :
- grid.narrowWidth;
- var realTileValues = tileValuesForGrid(effectiveGridWidth, numRowTiles,
- grid.tileSpacingFraction);
-
- // leftMargin centers the grid within the avaiable space.
- var minMargin = wide ? MIN_WIDE_MARGIN : 0;
- var leftMargin =
- Math.max(minMargin,
- (this.tileGrid_.clientWidth - effectiveGridWidth) / 2);
-
- var rowHeight = this.heightForWidth(realTileValues.tileWidth) +
- realTileValues.interTileSpacing;
-
- this.layoutValues_ = {
- colWidth: realTileValues.offsetX,
- gridWidth: effectiveGridWidth,
- leftMargin: leftMargin,
- numRowTiles: numRowTiles,
- rowHeight: rowHeight,
- tileWidth: realTileValues.tileWidth,
- wide: wide,
- };
-
- // We need to update the top margin as well.
- this.updateTopMargin_();
+ this.layout_();
+ // TODO(pedrosimonetti): When do we really need to send this message?
this.firePageLayoutEvent_();
},
@@ -843,250 +389,16 @@ cr.define('ntp', function() {
},
/**
- * @return {number} The amount of margin that should be animated (in pixels)
- * for the current grid layout.
- */
- getAnimatedLeftMargin_: function() {
- if (this.layoutValues_.wide)
- return 0;
-
- var grid = this.gridValues_;
- return (grid.minWideWidth - MIN_WIDE_MARGIN - grid.narrowWidth) / 2;
- },
-
- /**
- * Calculates the x/y coordinates for an element and moves it there.
- * @param {number} index The index of the element to be positioned.
- * @param {number} indexOffset If provided, this is added to |index| when
- * positioning the tile. The effect is that the tile will be positioned
- * in a non-default location.
- * @private
- */
- positionTile_: function(index, indexOffset) {
- var grid = this.gridValues_;
- var layout = this.layoutValues_;
-
- indexOffset = typeof indexOffset != 'undefined' ? indexOffset : 0;
- // Add the offset _after_ the modulus division. We might want to show the
- // tile off the side of the grid.
- var col = index % layout.numRowTiles + indexOffset;
- var row = Math.floor(index / layout.numRowTiles);
- // Calculate the final on-screen position for the tile.
- var realX = col * layout.colWidth + layout.leftMargin;
- var realY = row * layout.rowHeight;
-
- // Calculate the portion of the tile's position that should be animated.
- var animatedTileValues = layout.wide ?
- grid.wideTileValues : grid.narrowTileValues;
- // Animate the difference between three-wide and six-wide.
- var animatedLeftMargin = this.getAnimatedLeftMargin_();
- var animatedX = col * animatedTileValues.offsetX + animatedLeftMargin;
- var animatedY = row * (this.heightForWidth(animatedTileValues.tileWidth) +
- animatedTileValues.interTileSpacing);
-
- var tile = this.tileElements_[index];
- tile.setGridPosition(animatedX, animatedY);
- tile.firstChild.setBounds(layout.tileWidth,
- realX - animatedX,
- realY - animatedY);
-
- // This code calculates whether the tile needs to show a clone of itself
- // wrapped around the other side of the tile grid.
- var offTheRight = col == layout.numRowTiles ||
- (col == layout.numRowTiles - 1 && tile.hasDoppleganger());
- var offTheLeft = col == -1 || (col == 0 && tile.hasDoppleganger());
- if (this.isCurrentDragTarget && (offTheRight || offTheLeft)) {
- var sign = offTheRight ? 1 : -1;
- tile.showDoppleganger(-layout.numRowTiles * layout.colWidth * sign,
- layout.rowHeight * sign);
- } else {
- tile.clearDoppleganger();
- }
-
- if (index == this.tileElements_.length - 1) {
- this.tileGrid_.style.height = (realY + layout.rowHeight) + 'px';
- this.queueUpdateScrollbars_();
- }
- },
-
- /**
- * Gets the index of the tile that should occupy coordinate (x, y). Note
- * that this function doesn't care where the tiles actually are, and will
- * return an index even for the space between two tiles. This function is
- * effectively the inverse of |positionTile_|.
- * @param {number} x The x coordinate, in pixels, relative to the left of
- * |this|.
- * @param {number} y The y coordinate, in pixels, relative to the top of
- * |this|.
- * @private
- */
- getWouldBeIndexForPoint_: function(x, y) {
- var grid = this.gridValues_;
- var layout = this.layoutValues_;
-
- var gridClientRect = this.tileGrid_.getBoundingClientRect();
- var col = Math.floor((x - gridClientRect.left - layout.leftMargin) /
- layout.colWidth);
- if (col < 0 || col >= layout.numRowTiles)
- return -1;
-
- if (isRTL())
- col = layout.numRowTiles - 1 - col;
-
- var row = Math.floor((y - gridClientRect.top) / layout.rowHeight);
- return row * layout.numRowTiles + col;
- },
-
- /**
- * Window resize event handler. Window resizes may trigger re-layouts.
- * @param {Object} e The resize event.
- */
- onResize_: function(e) {
- if (this.lastWidth_ == this.clientWidth &&
- this.lastHeight_ == this.clientHeight) {
- return;
- }
-
- this.calculateLayoutValues_();
-
- this.lastWidth_ = this.clientWidth;
- this.lastHeight_ = this.clientHeight;
- this.classList.add('animating-tile-page');
- this.heightChanged_();
-
- this.positionNotification_();
- this.repositionTiles_();
- },
-
- /**
- * The tile grid has an image mask which fades at the edges. We only show
- * the mask when there is an active drag; it obscures doppleganger tiles
- * as they enter or exit the grid.
- * @private
- */
- updateMask_: function() {
- if (!this.isCurrentDragTarget) {
- this.tileGrid_.style.WebkitMaskBoxImage = '';
- return;
- }
-
- var leftMargin = this.layoutValues_.leftMargin;
- // The fade distance is the space between tiles.
- var fadeDistance = (this.gridValues_.tileSpacingFraction *
- this.layoutValues_.tileWidth);
- fadeDistance = Math.min(leftMargin, fadeDistance);
- // On Skia we don't use any fade because it works very poorly. See
- // http://crbug.com/99373
- if (!cr.isMac)
- fadeDistance = 1;
- var gradient =
- '-webkit-linear-gradient(left,' +
- 'transparent, ' +
- 'transparent ' + (leftMargin - fadeDistance) + 'px, ' +
- 'black ' + leftMargin + 'px, ' +
- 'black ' + (this.tileGrid_.clientWidth - leftMargin) + 'px, ' +
- 'transparent ' + (this.tileGrid_.clientWidth - leftMargin +
- fadeDistance) + 'px, ' +
- 'transparent)';
- this.tileGrid_.style.WebkitMaskBoxImage = gradient;
- },
-
- updateTopMargin_: function() {
- var layout = this.layoutValues_;
-
- // The top margin is set so that the vertical midpoint of the grid will
- // be 1/3 down the page.
- var numTiles = this.tileCount +
- (this.isCurrentDragTarget && !this.withinPageDrag_ ? 1 : 0);
- var numRows = Math.max(1, Math.ceil(numTiles / layout.numRowTiles));
- var usedHeight = layout.rowHeight * numRows;
- var newMargin = document.documentElement.clientHeight / 3 -
- usedHeight / 3 - this.contentPadding;
- // The 'height' style attribute of topMargin is non-zero to work around
- // webkit's collapsing margin behavior, so we have to factor that into
- // our calculations here.
- newMargin = Math.max(newMargin, 0) - this.topMargin_.offsetHeight;
-
- // |newMargin| is the final margin we actually want to show. However,
- // part of that should be animated and part should not (for the same
- // reason as with leftMargin). The approach is to consider differences
- // when the layout changes from wide to narrow or vice versa as
- // 'animatable'. These differences accumulate in animatedTopMarginPx_,
- // while topMarginPx_ caches the real (total) margin. Either of these
- // calculations may come out to be negative, so we use margins as the
- // css property.
-
- if (typeof this.topMarginIsForWide_ == 'undefined')
- this.topMarginIsForWide_ = layout.wide;
- if (this.topMarginIsForWide_ != layout.wide) {
- this.animatedTopMarginPx_ += newMargin - this.topMarginPx_;
- this.topMargin_.style.marginBottom = toCssPx(this.animatedTopMarginPx_);
- }
-
- this.topMarginIsForWide_ = layout.wide;
- this.topMarginPx_ = newMargin;
- this.topMargin_.style.marginTop =
- toCssPx(this.topMarginPx_ - this.animatedTopMarginPx_);
- },
-
- /**
* Position the notification if there's one showing.
+ * TODO(pedrosimonetti): Fix the position of the notification.
*/
positionNotification_: function() {
- var notification = this.notification;
- if (!notification || notification.hidden)
- return;
-
- // Update the horizontal position.
- var animatedLeftMargin = this.getAnimatedLeftMargin_();
- notification.style.WebkitMarginStart = animatedLeftMargin + 'px';
- var leftOffset = (this.layoutValues_.leftMargin - animatedLeftMargin) *
- (isRTL() ? -1 : 1);
- notification.style.WebkitTransform = 'translateX(' + leftOffset + 'px)';
-
- // Update the allowable widths of the text.
- var buttonWidth = notification.querySelector('button').offsetWidth + 8;
- notification.querySelector('span').style.maxWidth =
- this.layoutValues_.gridWidth - buttonWidth + 'px';
-
- // This makes sure the text doesn't condense smaller than the narrow size
- // of the grid (e.g. when a user makes the window really small).
- notification.style.minWidth =
- this.gridValues_.narrowWidth - buttonWidth + 'px';
-
- // Update the top position.
- notification.style.marginTop = -notification.offsetHeight + 'px';
- },
-
- /**
- * Handles final setup that can only happen after |this| is inserted into
- * the page.
- * @private
- */
- onNodeInsertedIntoDocument_: function(e) {
- this.calculateLayoutValues_();
- this.heightChanged_();
- },
-
- /**
- * Called when the height of |this| has changed: update the size of
- * tileGrid.
- * @private
- */
- heightChanged_: function() {
- // The tile grid will expand to the bottom footer, or enough to hold all
- // the tiles, whichever is greater. It would be nicer if tilePage were
- // a flex box, and the tile grid could be box-flex: 1, but this exposes a
- // bug where repositioning tiles will cause the scroll position to reset.
- this.tileGrid_.style.minHeight = (this.clientHeight -
- this.tileGrid_.offsetTop - this.content_.offsetTop -
- this.extraBottomPadding -
- (this.footerNode_ ? this.footerNode_.clientHeight : 0)) + 'px';
},
/**
* Places an element at the bottom of the content div. Used in bare-minimum
* mode to hold #footer.
+ * TODO(pedrosimonetti): Delete & make Footer non-required(shared/js/util).
* @param {HTMLElement} footerNode The node to append to content.
*/
appendFooter: function(footerNode) {
@@ -1112,266 +424,395 @@ cr.define('ntp', function() {
return true;
},
+ // #########################################################################
+ // Extended Chrome Instant
+ // #########################################################################
+
+
+ // properties
+ // -------------------------------------------------------------------------
+
+ // The number of columns.
+ colCount_: 5,
+ // The number of rows.
+ rowCount_: 2,
+ // The number of visible rows.
+ numOfVisibleRows_: 0,
+ // The number of the last column being animated.
+ // TODO(pedrosimonetti): How to handle initialization of this value?
+ animatingColCount_: 5,
+ // The index of the topmost row visible.
+ // TODO(pedrosimonetti): Move to config_?
+ pageOffset_: 0,
+
/**
- * Handler for the 'scroll' event on |content_|.
- * @param {Event} e The scroll event.
- * @private
+ * Appends a tile to the end of the tile grid.
+ * @param {Tile} tile The tile to be added.
+ * @param {number} index The location in the tile grid to insert it at.
+ * @protected
*/
- onScroll_: function(e) {
- this.queueUpdateScrollbars_();
+ appendTile: function(tile) {
+ var index = this.tiles_.length;
+ this.tiles_.push(tile);
+ this.renderGrid_();
+ this.fireAddedEvent(tile, index);
},
/**
- * ID of scrollbar update timer. If 0, there's no scrollbar re-calc queued.
- * @private
+ * Adds the given element to the tile grid.
+ * TODO(pedrosimonetti): If this is not being used, delete.
+ * @param {Tile} tile The tile to be added.
+ * @param {number} index The location in the tile grid to insert it at.
+ * @protected
*/
- scrollbarUpdate_: 0,
+ addTileAt: function(tile, index) {
+ this.tiles_.splice(index, 0, tile);
+ this.renderGrid_();
+ this.fireAddedEvent(tile, index);
+ },
+
+ // internal helpers
+ // -------------------------------------------------------------------------
/**
- * Queues an update on the custom scrollbar. Used for two reasons: first,
- * coalescing of multiple updates, and second, because action like
- * repositioning a tile can require a delay before they affect values
- * like clientHeight.
+ * Gets the required width for a Tile.
* @private
*/
- queueUpdateScrollbars_: function() {
- if (this.scrollbarUpdate_)
- return;
-
- this.scrollbarUpdate_ = window.setTimeout(
- this.doUpdateScrollbars_.bind(this), 0);
+ getTileRequiredWidth_: function() {
+ var conf = this.config_;
+ return conf.cellWidth + conf.cellMarginStart;
},
/**
- * Does the work of calculating the visibility, height and position of the
- * scrollbar thumb (there is no track or buttons).
+ * Gets the the maximum number of columns that can fit in a given width.
+ * @param {number} width The width in pixels.
* @private
*/
- doUpdateScrollbars_: function() {
- this.scrollbarUpdate_ = 0;
-
- var content = this.content_;
-
- // Adjust scroll-height to account for possible header-bar.
- var adjustedScrollHeight = content.scrollHeight - content.offsetTop;
-
- if (adjustedScrollHeight <= content.clientHeight) {
- this.scrollbar_.hidden = true;
- return;
- } else {
- this.scrollbar_.hidden = false;
- }
-
- var thumbTop = content.offsetTop +
- content.scrollTop / adjustedScrollHeight * content.clientHeight;
- var thumbHeight = content.clientHeight / adjustedScrollHeight *
- this.clientHeight;
-
- this.scrollbar_.style.top = thumbTop + 'px';
- this.scrollbar_.style.height = thumbHeight + 'px';
- this.firePageLayoutEvent_();
+ getColCountForWidth_: function(width) {
+ var availableWidth = width + this.config_.cellMarginStart;
+ var requiredWidth = this.getTileRequiredWidth_();
+ var colCount = Math.floor(availableWidth / requiredWidth);
+ return colCount;
},
/**
- * Get the height for a tile of a certain width. Override this function to
- * get non-square tiles.
- * @param {number} width The pixel width of a tile.
- * @return {number} The height for |width|.
+ * Gets the width for a given number of columns.
+ * @param {number} colCount The number of columns.
+ * @private
*/
- heightForWidth: function(width) {
+ getWidthForColCount_: function(colCount) {
+ var requiredWidth = this.getTileRequiredWidth_();
+ var width = colCount * requiredWidth - this.config_.cellMarginStart;
return width;
},
- /** Dragging **/
-
- get isCurrentDragTarget() {
- return this.dragWrapper_.isCurrentDragTarget;
- },
-
/**
- * Thunk for dragleave events fired on |tileGrid_|.
- * @param {Event} e A MouseEvent for the drag.
+ * Gets the bottom panel width.
+ * @private
*/
- doDragLeave: function(e) {
- this.cleanupDrag();
+ getBottomPanelWidth_: function() {
+ var windowWidth = cr.doc.documentElement.clientWidth;
+ var width;
+ // TODO(pedrosimonetti): Add constants?
+ if (windowWidth >= 948)
+ width = 748;
+ else if (windowWidth >= 500)
+ width = windowWidth - 2 * this.config_.bottomPanelHorizontalMargin;
+ else if (windowWidth >= 300)
+ // TODO(pedrosimonetti): Check specification.
+ width = Math.floor(((windowWidth - 300) / 200) * 100 + 200);
+ else
+ width = 200;
+
+ return width;
},
/**
- * Performs all actions necessary when a drag enters the tile page.
- * @param {Event} e A mouseover event for the drag enter.
+ * Gets the number of available columns.
+ * @private
*/
- doDragEnter: function(e) {
- // Applies the mask so doppleganger tiles disappear into the fog.
- this.updateMask_();
-
- this.classList.add('animating-tile-page');
- this.withinPageDrag_ = this.contains(currentlyDraggingTile);
- this.dragItemIndex_ = this.withinPageDrag_ ?
- currentlyDraggingTile.index : this.tileElements_.length;
- this.currentDropIndex_ = this.dragItemIndex_;
-
- // The new tile may change the number of rows, hence the top margin
- // will change.
- if (!this.withinPageDrag_)
- this.updateTopMargin_();
-
- this.doDragOver(e);
+ getAvailableColCount_: function() {
+ return this.getColCountForWidth_(this.getBottomPanelWidth_());
},
+ // rendering
+ // -------------------------------------------------------------------------
+
/**
- * Performs all actions necessary when the user moves the cursor during
- * a drag over the tile page.
- * @param {Event} e A mouseover event for the drag over.
+ * Renders the grid.
+ * @param {number} colCount The number of columns.
+ * @private
*/
- doDragOver: function(e) {
- e.preventDefault();
-
- this.setDropEffect(e.dataTransfer);
- var newDragIndex = this.getWouldBeIndexForPoint_(e.pageX, e.pageY);
- if (newDragIndex < 0 || newDragIndex >= this.tileElements_.length)
- newDragIndex = this.dragItemIndex_;
- this.updateDropIndicator_(newDragIndex);
+ renderGrid_: function(colCount) {
+ colCount = colCount || this.colCount_;
+
+ var tileGridContent = this.tileGridContent_;
+ var tiles = this.tiles_;
+ var tileCount = tiles.length;
+
+ var numOfVisibleRows = this.numOfVisibleRows_;
+ var rowCount = Math.ceil(tileCount / colCount);
+ rowCount = Math.max(rowCount, numOfVisibleRows);
+ var tileRows = tileGridContent.getElementsByClassName('tile-row');
+
+ var pageOffset = this.pageOffset_;
+
+ for (var tile = 0, row = 0; row < rowCount; row++) {
+ var tileRow = tileRows[row];
+
+ // Create tile row if there's no one yet.
+ if (!tileRow) {
+ tileRow = cr.doc.createElement('div');
+ tileRow.className = 'tile-row';
+ tileGridContent.appendChild(tileRow);
+ }
+
+ // Adjust row visibility.
+ var rowVisible = row >= pageOffset &&
+ row <= (pageOffset + numOfVisibleRows - 1);
+ this.showTileRow_(tileRow, rowVisible);
+
+ // The tiles inside the current row.
+ var tileRowTiles = tileRow.childNodes;
+
+ // Remove excessive columns from a particular tile row.
+ var maxColCount = Math.min(colCount, tileCount - tile);
+ maxColCount = Math.max(0, maxColCount);
+ while (tileRowTiles.length > maxColCount) {
+ tileRow.removeChild(tileRow.lastElementChild);
+ }
+
+ // For each column in the current row.
+ for (var col = 0; col < colCount; col++, tile++) {
+ var tileCell;
+ var tileElement;
+ if (tileRowTiles[col]) {
+ tileCell = tileRowTiles[col];
+ } else {
+ var span = cr.doc.createElement('span');
+ tileCell = new TileCell(span, this.config_);
+ }
+
+ // Render Tiles.
+ if (tile < tileCount) {
+ tileCell.classList.remove('filler');
+ tileElement = tiles[tile];
+ if (!tileCell.firstChild)
+ tileCell.appendChild(tileElement);
+ else if (tileElement != tileCell.firstChild)
+ tileCell.replaceChild(tileElement, tileCell.firstChild);
+ } else if (!tileCell.classList.contains('filler')) {
+ tileCell.classList.add('filler');
+ tileElement = cr.doc.createElement('span');
+ tileElement.className = 'tile';
+ if (tileCell.firstChild)
+ tileCell.replaceChild(tileElement, tileCell.firstChild);
+ else
+ tileCell.appendChild(tileElement);
+ }
+
+ if (!tileRowTiles[col])
+ tileRow.appendChild(tileCell);
+ }
+ }
+
+ // Remove excessive tile rows from the tile grid.
+ while (tileRows.length > rowCount) {
+ tileGridContent.removeChild(tileGridContent.lastElementChild);
+ }
+
+ this.colCount_ = colCount;
+ this.rowCount_ = rowCount;
},
/**
- * Performs all actions necessary when the user completes a drop.
- * @param {Event} e A mouseover event for the drag drop.
+ * Adjusts the layout of the tile page according to the current window size.
+ * @param {boolean} force Forces the layout.
+ * @private
*/
- doDrop: function(e) {
- e.stopPropagation();
- e.preventDefault();
-
- var index = this.currentDropIndex_;
- // Only change data if this was not a 'null drag'.
- if (!((index == this.dragItemIndex_) && this.withinPageDrag_)) {
- var adjustedIndex = this.currentDropIndex_ +
- (index > this.dragItemIndex_ ? 1 : 0);
- if (this.withinPageDrag_) {
- this.tileGrid_.insertBefore(
- currentlyDraggingTile,
- this.tileElements_[adjustedIndex]);
- this.tileMoved(currentlyDraggingTile, this.dragItemIndex_);
+ layout_: function(force) {
+ if (force) {
+ this.numOfVisibleRows_ = 0;
+ this.animatingColCount_ = 0;
+ }
+
+ var bottomPanelWidth = this.getBottomPanelWidth_();
+ var colCount = this.getColCountForWidth_(bottomPanelWidth);
+ var lastColCount = this.colCount_;
+ var animatingColCount = this.animatingColCount_;
+
+ var windowHeight = cr.doc.documentElement.clientHeight;
+
+ this.tileGridContent_.classList.add('animate-tile');
+
+ // TODO(pedrosimonetti): Better handling of height state.
+ // TODO(pedrosimonetti): Add constants?
+ var numOfVisibleRows = windowHeight > 500 ? 2 : 1;
+ if (numOfVisibleRows != this.numOfVisibleRows_) {
+ this.numOfVisibleRows_ = numOfVisibleRows;
+ this.paginate_(null, true);
+ $('page-list').style.height =
+ (this.config_.rowHeight * numOfVisibleRows) + 'px';
+ }
+
+ // changeVisibleCols
+ if (colCount != animatingColCount) {
+ var newWidth = this.getWidthForColCount_(colCount);
+ if (colCount > animatingColCount) {
+ // TODO(pedrosimonetti): Do an actual size check.
+ if (colCount != lastColCount)
+ this.renderGrid_(colCount);
+
+ this.showTileCols_(animatingColCount, false);
+
+ var self = this;
+ // We need to save the animatingColCount value in a closure otherwise
+ // the animation of tiles won't work when expanding horizontally.
+ // The problem happens because the layout_ method is called several
+ // times when resizing the window, and the animatingColCount is saved
+ // and restored each time a new column has to be animated. So, if we
+ // don't save the value, by the time the showTileCols_ method is
+ // called the animatingColCount is holding a new value, breaking
+ // the animation.
+ setTimeout((function(animatingColCount) {
+ return function() {
+ self.showTileCols_(animatingColCount, true);
+ }
+ })(animatingColCount), 0);
} else {
- var originalPage = currentlyDraggingTile ?
- currentlyDraggingTile.tilePage : null;
- this.addDragData(e.dataTransfer, adjustedIndex);
- if (originalPage)
- originalPage.cleanupDrag();
+ this.showTileCols_(colCount, false);
}
- // Dropping the icon may cause topMargin to change, but changing it
- // now would cause everything to move (annoying), so we leave it
- // alone. The top margin will be re-calculated next time the window is
- // resized or the page is selected.
+ this.tileGrid_.style.width = newWidth + 'px';
+ $('page-list-menu').style.width = newWidth + 'px';
+
+ var self = this;
+ this.onTileGridAnimationEndHandler_ = function() {
+ if (colCount < lastColCount)
+ self.renderGrid_(colCount);
+ else
+ self.showTileCols_(0, true);
+ };
+
+ this.paginate_();
}
- this.classList.remove('animating-tile-page');
- this.cleanupDrag();
- },
+ this.content_.style.width = bottomPanelWidth + 'px';
- /**
- * Appends the currently dragged tile to the end of the page. Called
- * from outside the page, e.g. when dropping on a nav dot.
- */
- appendDraggingTile: function() {
- var originalPage = currentlyDraggingTile.tilePage;
- if (originalPage == this)
- return;
-
- this.addDragData(null, this.tileElements_.length);
- if (originalPage)
- originalPage.cleanupDrag();
+ this.animatingColCount_ = colCount;
},
+ // animation helpers
+ // -------------------------------------------------------------------------
+
/**
- * Makes sure all the tiles are in the right place after a drag is over.
+ * Animates the display of a row. TODO(pedrosimonetti): Make it local?
+ * @param {HTMLElement} row The row element.
+ * @param {boolean} show Whether or not to show the row.
*/
- cleanupDrag: function() {
- this.repositionTiles_(currentlyDraggingTile);
- // Remove the drag mask.
- this.updateMask_();
+ showTileRow_: function(row, show) {
+ row.classList[show ? 'remove' : 'add']('hide-row');
},
/**
- * Reposition all the tiles (possibly ignoring one).
- * @param {?Node} ignoreNode An optional node to ignore.
- * @private
+ * Animates the display of columns. TODO(pedrosimonetti): Make it local?
+ * @param {number} col The column number.
+ * @param {boolean} show Whether or not to show the row.
*/
- repositionTiles_: function(ignoreNode) {
- for (var i = 0; i < this.tileElements_.length; i++) {
- if (!ignoreNode || ignoreNode !== this.tileElements_[i])
- this.positionTile_(i);
+ showTileCols_: function(col, show) {
+ var prop = show ? 'remove' : 'add';
+ var max = 10; // TODO(pedrosimonetti): Add const?
+ var tileGridContent = this.tileGridContent_;
+ for (var i = col; i < max; i++) {
+ tileGridContent.classList[prop]('hide-col-' + i);
}
},
+ // pagination
+ // -------------------------------------------------------------------------
+
/**
- * Updates the visual indicator for the drop location for the active drag.
- * @param {Event} e A MouseEvent for the drag.
- * @private
+ * Resets the display of columns.
+ * @param {number} pageOffset The index of the topmost row visible.
+ * @param {boolean} force Forces the pagination.
*/
- updateDropIndicator_: function(newDragIndex) {
- var oldDragIndex = this.currentDropIndex_;
- if (newDragIndex == oldDragIndex)
- return;
-
- var repositionStart = Math.min(newDragIndex, oldDragIndex);
- var repositionEnd = Math.max(newDragIndex, oldDragIndex);
-
- for (var i = repositionStart; i <= repositionEnd; i++) {
- if (i == this.dragItemIndex_)
- continue;
- else if (i > this.dragItemIndex_)
- var adjustment = i <= newDragIndex ? -1 : 0;
- else
- var adjustment = i >= newDragIndex ? 1 : 0;
-
- this.positionTile_(i, adjustment);
+ paginate_: function(pageOffset, force) {
+ var numOfVisibleRows = this.numOfVisibleRows_;
+ var pageOffset = typeof pageOffset == 'number' ?
+ pageOffset : this.pageOffset_;
+
+ pageOffset = Math.min(this.rowCount_ - numOfVisibleRows, pageOffset);
+ pageOffset = Math.max(0, pageOffset);
+
+ if (pageOffset != this.pageOffset || force) {
+ var rows = this.tileGridContent_.getElementsByClassName('tile-row');
+ for (var i = 0, length = rows.length; i < length; i++) {
+ var row = rows[i];
+ var isRowVisible = i >= pageOffset &&
+ i <= (pageOffset + numOfVisibleRows - 1);
+ this.showTileRow_(row, isRowVisible);
+ }
+
+ this.pageOffset_ = pageOffset;
+ this.tileGridContent_.style.webkitTransform =
+ 'translate3d(0,' + (-pageOffset * this.config_.rowHeight) + 'px,0)';
}
- this.currentDropIndex_ = newDragIndex;
},
- /**
- * Checks if a page can accept a drag with the given data.
- * @param {Event} e The drag event if the drag object. Implementations will
- * likely want to check |e.dataTransfer|.
- * @return {boolean} True if this page can handle the drag.
- */
- shouldAcceptDrag: function(e) {
- return false;
- },
+ // event handlers
+ // -------------------------------------------------------------------------
/**
- * Called to accept a drag drop. Will not be called for in-page drops.
- * @param {Object} dataTransfer The data transfer object that holds the drop
- * data. This should only be used if currentlyDraggingTile is null.
- * @param {number} index The tile index at which the drop occurred.
+ * Handles the window resize event.
*/
- addDragData: function(dataTransfer, index) {
- assert(false);
+ onResize_: function() {
+ this.layout_();
},
/**
- * Called when a tile has been moved (via dragging). Override this to make
- * backend updates.
- * @param {Node} draggedTile The tile that was dropped.
- * @param {number} prevIndex The previous index of the tile.
+ * Handles the end of the tile grid animation.
*/
- tileMoved: function(draggedTile, prevIndex) {
+ onTileGridAnimationEnd_: function() {
+ // TODO(pedrosimonetti): Figure out how to cleanup each kind of
+ // animation properly.
+ if (event.target == this.tileGrid_ &&
+ this.onTileGridAnimationEndHandler_ &&
+ this.tileGridContent_.classList.contains('animate-tile')) {
+ this.onTileGridAnimationEndHandler_();
+ }
},
/**
- * Sets the drop effect on |dataTransfer| to the desired value (e.g.
- * 'copy').
- * @param {Object} dataTransfer The drag event dataTransfer object.
+ * Handles the window keyup event.
+ * @param {Event} e The keyboard event.
*/
- setDropEffect: function(dataTransfer) {
- assert(false);
- },
+ onKeyUp_: function(e) {
+ var pageOffset = this.pageOffset_;
+
+ var keyCode = e.keyCode;
+ if (keyCode == 40 /* down */)
+ pageOffset++;
+ else if (keyCode == 38 /* up */)
+ pageOffset--;
+
+ // Changes the pagination according to which arrow key was pressed.
+ if (pageOffset != this.pageOffset_)
+ this.paginate_(pageOffset);
+ }
};
+ /**
+ * Shows a deprecate error.
+ */
+ function deprecate() {
+ console.error('This function is deprecated!');
+ }
+
return {
- getCurrentlyDraggingTile: getCurrentlyDraggingTile,
- setCurrentDropEffect: setCurrentDropEffect,
- TilePage: TilePage,
+ // TODO(pedrosimonetti): Drag. Delete after porting the rest of the code.
+ getCurrentlyDraggingTile2: deprecate,
+ setCurrentDropEffect2: deprecate,
+ Tile2: Tile,
+ TilePage2: TilePage
};
});
« no previous file with comments | « chrome/browser/resources/ntp_search/tile_page.css ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698