[go: nahoru, domu]

blob: 97c9c8edfcf1be1e0b7f4b4b756b69379de231b9 [file] [log] [blame]
// 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';
//----------------------------------------------------------------------------
// Constants
//----------------------------------------------------------------------------
/**
* Bottom Panel's minimum padding bottom.
* @type {number}
* @const
*/
var BOTTOM_PANEL_MIN_PADDING_BOTTOM = 16;
/**
* The height required to show 2 rows of Tiles in the Bottom Panel.
* @type {number}
* @const
*/
var HEIGHT_FOR_TWO_ROWS = 800;
/**
* The height required to show 1 row of Tiles with extra bottom padding.
* @type {number}
* @const
*/
var HEIGHT_FOR_ONE_TALL_ROW = 700;
/**
* The height required to show the Bottom Panel.
* @type {number}
* @const
*/
var HEIGHT_FOR_BOTTOM_PANEL = 520;
/**
* The Bottom Panel width required to show 5 cols of Tiles, which is used
* in the width computation.
* @type {number}
* @const
*/
var MAX_BOTTOM_PANEL_WIDTH = 948;
/**
* The normal Bottom Panel width. If the window width is greater than or
* equal to this value, then the width of the Bottom Panel's content will be
* the available width minus side margin. If the available width is smaller
* than this value, then the width of the Bottom Panel's content will be an
* interpolation between the normal width, and the minimum width defined by
* the constant MIN_BOTTOM_PANEL_CONTENT_WIDTH.
* @type {number}
* @const
*/
var NORMAL_BOTTOM_PANEL_WIDTH = 500;
/**
* The minimum Bottom Panel width. If the available width is smaller than
* this value, then the width of the Bottom Panel's content will be fixed to
* MIN_BOTTOM_PANEL_CONTENT_WIDTH.
* @type {number}
* @const
*/
var MIN_BOTTOM_PANEL_WIDTH = 300;
/**
* The minimum width of the Bottom Panel's content.
* @type {number}
* @const
*/
var MIN_BOTTOM_PANEL_CONTENT_WIDTH = 200;
/**
* The height of the TabBar.
* @type {number}
* @const
*/
var TAB_BAR_HEIGHT = 34;
/**
* The height of the NTP's Upper Section.
* @type {number}
* @const
*/
var UPPER_SECTION_HEIGHT = 289;
//----------------------------------------------------------------------------
// Tile
//----------------------------------------------------------------------------
/**
* 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 Tile(config) {
console.error('Tile is a virtual class and is not supposed to be ' +
'instantiated');
}
/**
* 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.
*/
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 = {
/**
* Initializes a Tile.
* @param {Object} config TilePage configuration object.
*/
initialize: function(config) {
this.className = 'tile';
},
};
var TileGetters = {
/**
* The TileCell associated to this Tile.
* @type {TileCell}
*/
'tileCell': function() {
return findAncestorByClass(this, 'tile-cell');
},
/**
* The index of the Tile.
* @type {number}
*/
'index': function() {
assert(this.tileCell);
return this.tileCell.index;
},
};
//----------------------------------------------------------------------------
// TileCell
//----------------------------------------------------------------------------
/**
* 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);
return tileCell;
}
TileCell.prototype = {
__proto__: HTMLDivElement.prototype,
/**
* Initializes a TileCell.
* @param {Tile} tile The Tile that will be assigned to this TileCell.
* @param {Object} config TilePage configuration object.
*/
initialize: function(tile, config) {
this.className = 'tile-cell';
this.assign_(tile);
},
/**
* The index of the TileCell.
* @type {number}
*/
get index() {
return Array.prototype.indexOf.call(this.tilePage.tiles_,
this.firstChild);
},
/**
* The TilePage associated to this TileCell.
* @type {TilePage}
*/
get tilePage() {
return findAncestorByClass(this, 'tile-page');
},
/**
* Assigns a Tile to the this TileCell.
* @type {TilePage}
*/
assign_: function(tile) {
if (this.firstChild)
this.replaceChild(tile, this.firstChild);
else
this.appendChild(tile);
},
/**
* Called when an app is removed from Chrome. Animates its disappearance.
* @param {boolean=} opt_animate Whether the animation should be animated.
*/
doRemove: function(opt_animate) {
if (opt_animate)
this.firstChild.classList.add('removing-tile-contents');
else
this.tilePage.removeTile(this, false);
},
};
//----------------------------------------------------------------------------
// TilePage
//----------------------------------------------------------------------------
/**
* Creates a new TilePage object. This object contains tiles and controls
* their layout.
* @constructor
* @extends {HTMLDivElement}
*/
function TilePage() {
var el = cr.doc.createElement('div');
el.__proto__ = TilePage.prototype;
el.initialize();
return el;
}
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';
// 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_);
// The div that defines the tile grid viewport.
this.tileGrid_ = this.ownerDocument.createElement('div');
this.tileGrid_.className = 'tile-grid';
this.content_.appendChild(this.tileGrid_);
// 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_);
// The list of Tile elements which is used to fill the TileGrid cells.
this.tiles_ = [];
// Event handlers.
this.eventTracker = new EventTracker();
this.eventTracker.add(window, 'resize', this.onResize_.bind(this));
this.eventTracker.add(window, 'keyup', this.onKeyUp_.bind(this));
this.eventTracker.add(this, 'cardselected', this.handleCardSelection_);
this.eventTracker.add(this, 'carddeselected',
this.handleCardDeselection_);
this.eventTracker.add(this.tileGrid_, 'webkitTransitionEnd',
this.onTileGridTransitionEnd_.bind(this));
this.eventTracker.add($('page-list'), 'webkitTransitionEnd',
this.onPageListTransitionEnd_.bind(this));
},
/**
* The list of Tile elements.
* @type {Array<Tile>}
*/
get tiles() {
return this.tiles_;
},
/**
* The number of Tiles in this TilePage.
* @type {number}
*/
get tileCount() {
return this.tiles_.length;
},
/**
* Whether or not this TilePage is selected.
* @type {boolean}
*/
get selected() {
return Array.prototype.indexOf.call(this.parentNode.children, this) ==
ntp.getCardSlider().currentCard;
},
/**
* Removes the tilePage from the DOM and cleans up event handlers.
*/
remove: function() {
// This checks arguments.length as most remove functions have a boolean
// |opt_animate| argument, but that's not necesarilly applicable to
// removing a tilePage. Selecting a different card in an animated way and
// deleting the card afterward is probably a better choice.
assert(typeof arguments[0] != 'boolean',
'This function takes no |opt_animate| argument.');
this.tearDown_();
this.parentNode.removeChild(this);
},
/**
* Cleans up resources that are no longer needed after this TilePage
* instance is removed from the DOM.
* @private
*/
tearDown_: function() {
this.eventTracker.removeAll();
},
/**
* Notify interested subscribers that a tile has been removed from this
* 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.
*/
fireAddedEvent: function(tile, index, wasAnimated) {
var e = document.createEvent('Event');
e.initEvent('tilePage:tile_added', true, true);
e.addedIndex = index;
e.addedTile = tile;
e.wasAnimated = wasAnimated;
this.dispatchEvent(e);
},
/**
* Removes the given tile and animates the repositioning of the other tiles.
* @param {boolean=} opt_animate Whether the removal should be animated.
* @param {boolean=} opt_dontNotify Whether a page should be removed if the
* last tile is removed from it.
*/
removeTile: function(tile, opt_animate, opt_dontNotify) {
if (opt_animate)
this.classList.add('animating-tile-page');
var index = tile.index;
tile.parentNode.removeChild(tile);
this.renderGrid_();
if (!opt_dontNotify)
this.fireRemovedEvent(tile, index, !!opt_animate);
},
/**
* Notify interested subscribers that a tile has been removed from this
* page.
* @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.
*/
fireRemovedEvent: function(tile, oldIndex, wasAnimated) {
var e = document.createEvent('Event');
e.initEvent('tilePage:tile_removed', true, true);
e.removedIndex = oldIndex;
e.removedTile = tile;
e.wasAnimated = wasAnimated;
this.dispatchEvent(e);
},
/**
* Removes all tiles from the page.
*/
removeAllTiles: function() {
// TODO(pedrosimonetti): Dispatch individual tearDown functions.
this.tileGrid_.innerHTML = '';
},
/**
* Called when the page is selected (in the card selector).
* @param {Event} e A custom cardselected event.
* @private
*/
handleCardSelection_: function(e) {
this.layout();
},
/**
* Called when the page loses selection (in the card selector).
* @param {Event} e A custom carddeselected event.
* @private
*/
handleCardDeselection_: function(e) {
},
// #########################################################################
// Extended Chrome Instant
// #########################################################################
// properties
// -------------------------------------------------------------------------
// The number of columns.
colCount_: 0,
// The number of rows.
rowCount_: 0,
// The number of visible rows. We initialize this value with zero so
// we can detect when the first time the page is rendered.
numOfVisibleRows_: 0,
// The number of the last column being animated. We initialize this value
// with zero so we can detect when the first time the page is rendered.
animatingColCount_: 0,
// The index of the topmost row visible.
pageOffset_: 0,
/**
* 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
*/
appendTile: function(tile) {
var index = this.tiles_.length;
this.tiles_.push(tile);
this.fireAddedEvent(tile, index);
},
/**
* 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
*/
addTileAt: function(tile, index) {
this.tiles_.splice(index, 0, tile);
this.fireAddedEvent(tile, index);
},
// internal helpers
// -------------------------------------------------------------------------
/**
* Gets the required width for a Tile.
* @private
*/
getTileRequiredWidth_: function() {
var conf = this.config_;
return conf.cellWidth + conf.cellMarginStart;
},
/**
* Gets the the maximum number of columns that can fit in a given width.
* @param {number} width The width in pixels.
* @private
*/
getColCountForWidth_: function(width) {
var availableWidth = width + this.config_.cellMarginStart;
var requiredWidth = this.getTileRequiredWidth_();
var colCount = Math.floor(availableWidth / requiredWidth);
return colCount;
},
/**
* Gets the width for a given number of columns.
* @param {number} colCount The number of columns.
* @private
*/
getWidthForColCount_: function(colCount) {
var requiredWidth = this.getTileRequiredWidth_();
var width = colCount * requiredWidth - this.config_.cellMarginStart;
return width;
},
/**
* Gets the bottom panel width.
* @private
*/
getBottomPanelWidth_: function() {
var windowWidth = cr.doc.documentElement.clientWidth;
var margin = 2 * this.config_.bottomPanelHorizontalMargin;
var width;
if (windowWidth >= MAX_BOTTOM_PANEL_WIDTH) {
width = MAX_BOTTOM_PANEL_WIDTH - margin;
} else if (windowWidth >= NORMAL_BOTTOM_PANEL_WIDTH) {
width = windowWidth - margin;
} else if (windowWidth >= MIN_BOTTOM_PANEL_WIDTH) {
// Interpolation between the previous and next states.
var minMargin = MIN_BOTTOM_PANEL_WIDTH - MIN_BOTTOM_PANEL_CONTENT_WIDTH;
var factor = (windowWidth - MIN_BOTTOM_PANEL_WIDTH) /
(NORMAL_BOTTOM_PANEL_WIDTH - MIN_BOTTOM_PANEL_WIDTH);
var interpolatedMargin = minMargin + factor * (margin - minMargin);
width = windowWidth - interpolatedMargin;
} else {
width = MIN_BOTTOM_PANEL_CONTENT_WIDTH;
}
return width;
},
// rendering
// -------------------------------------------------------------------------
/**
* Renders the tile grid, and the individual tiles. Rendering the grid
* consists of adding/removing tile rows and tile cells according to the
* specified size (defined by the number of columns in the grid). While
* rendering the grid, the tiles are rendered in order in their respective
* cells and tile fillers are rendered when needed. This method sets the
* private properties colCount_ and rowCount_.
*
* This method should be called every time the contents of the grid changes,
* that is, when the number, contents or order of the tiles has changed.
* @param {number=} opt_colCount The number of columns.
* @private
*/
renderGrid_: function(opt_colCount) {
var colCount = opt_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;
},
/**
* Adjusts the layout of the tile page according to the current window size.
* This method will resize the containers of the bottom panel + tile page,
* and re-render the grid when its dimension changes (number of columns or
* visible rows changes). This method also sets the private properties
* numOfVisibleRows_ and animatingColCount_, and these values are
* initialized when each tile page is created by using the opt_force
* parameter.
*
* This method should be called every time the dimension of the grid changes
* or when you need to reinforce its dimension.
* @param {boolean=} opt_force Whether the layout should be forced, which
* implies not animating the resizing.
*/
layout: function(opt_force) {
// Only adjusts the layout if the page is currently selected, or when
// it is being forced.
if (!(this.selected || opt_force))
return;
var shouldAnimate = !opt_force;
var bottomPanelWidth = this.getBottomPanelWidth_();
var colCount = this.getColCountForWidth_(bottomPanelWidth);
var lastColCount = this.colCount_;
var animatingColCount = this.animatingColCount_;
// TODO(pedrosimonetti): Separate height/width logic in different methods.
// Height logic
var windowHeight = cr.doc.documentElement.clientHeight +
UPPER_SECTION_HEIGHT + TAB_BAR_HEIGHT;
this.showBottomPanel_(windowHeight >= HEIGHT_FOR_BOTTOM_PANEL);
// If the number of visible rows has changed, then we need to resize the
// grid and animate the affected rows. We also need to keep track of
// whether the number of visible rows has changed because we might have
// to render the grid when the number of columns hasn't changed.
var numberOfRowsHasChanged = false;
var numOfVisibleRows = windowHeight >= HEIGHT_FOR_TWO_ROWS ? 2 : 1;
if (numOfVisibleRows != this.numOfVisibleRows_) {
this.numOfVisibleRows_ = numOfVisibleRows;
numberOfRowsHasChanged = true;
var pageList = $('page-list');
if (shouldAnimate)
pageList.classList.add('animate-page-height');
// By forcing the pagination, all affected rows will be animated.
this.paginate_(null, true);
pageList.style.height =
(this.config_.rowHeight * numOfVisibleRows) + 'px';
}
// Calculate the size of the bottom padding.
var paddingBottom;
if (windowHeight < HEIGHT_FOR_ONE_TALL_ROW) {
paddingBottom = BOTTOM_PANEL_MIN_PADDING_BOTTOM;
} else if (windowHeight < HEIGHT_FOR_TWO_ROWS) {
paddingBottom = BOTTOM_PANEL_MIN_PADDING_BOTTOM +
Math.round((windowHeight - HEIGHT_FOR_ONE_TALL_ROW) / 2);
} else if (windowHeight >= HEIGHT_FOR_TWO_ROWS) {
paddingBottom = BOTTOM_PANEL_MIN_PADDING_BOTTOM +
Math.round((windowHeight - HEIGHT_FOR_TWO_ROWS) / 2);
}
var cardSliderFrame = $('card-slider-frame');
cardSliderFrame.style.bottom = paddingBottom + 'px';
// Width logic
// If the number of columns being animated has changed, then we need to
// resize the grid, animate the tiles, and render the grid when its actual
// size changes.
if (colCount != animatingColCount) {
if (shouldAnimate)
this.tileGrid_.classList.add('animate-grid-width');
var newWidth = this.getWidthForColCount_(colCount);
// If the grid is expanding horizontally we need to render the grid
// first so the revealing tiles are visible as soon as the animation
// starts.
if (colCount > animatingColCount) {
// We only have to render the grid if the actual number of columns
// has changed.
if (colCount != lastColCount)
this.renderGrid_(colCount);
// Animates the affected columns.
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 {
// If the grid is shrinking horizontally, then we need to render the
// grid only after the animation ends so the tiles that are being
// hidden remains visible until the end of the animation. See
// onTileGridTransitionEndHandler_ below.
this.showTileCols_(colCount, false);
}
this.tileGrid_.style.width = newWidth + 'px';
$('page-list-menu').style.width = newWidth + 'px';
var self = this;
this.onTileGridTransitionEndHandler_ = function() {
if (colCount < lastColCount)
self.renderGrid_(colCount);
else
self.showTileCols_(0, true);
};
this.paginate_();
} else if (numberOfRowsHasChanged) {
// If the number of columns being animated hasn't changed but the number
// of rows has changed then we have to render the grid too.
this.renderGrid_(colCount);
}
this.content_.style.width = bottomPanelWidth + 'px';
this.animatingColCount_ = colCount;
},
// animation helpers
// -------------------------------------------------------------------------
/**
* Animates the display the Bottom Panel.
* @param {boolean} show Whether or not to show the Bottom Panel.
*/
showBottomPanel_: function(show) {
$('card-slider-frame').classList[show ? 'remove' : 'add'](
'hide-card-slider');
},
/**
* 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.
*/
showTileRow_: function(row, show) {
row.classList[show ? 'remove' : 'add']('hide-row');
},
/**
* 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.
*/
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
// -------------------------------------------------------------------------
/**
* Resets the display of columns.
* @param {number=} opt_pageOffset The index of the topmost row visible.
* Default value is the last pageOffset_.
* @param {boolean=} opt_force Forces the pagination. Default is false.
*/
paginate_: function(opt_pageOffset, opt_force) {
var numOfVisibleRows = this.numOfVisibleRows_;
var pageOffset = typeof opt_pageOffset == 'number' ?
opt_pageOffset : this.pageOffset_;
pageOffset = Math.min(this.rowCount_ - numOfVisibleRows, pageOffset);
pageOffset = Math.max(0, pageOffset);
if (pageOffset != this.pageOffset || opt_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)';
}
},
// event handlers
// -------------------------------------------------------------------------
/**
* Handles the window resize event.
* @param {Event} e The window resize event.
*/
onResize_: function(e) {
this.layout();
},
/**
* Handles the end of the horizontal tile grid transition.
* @param {Event} e The tile grid webkitTransitionEnd event.
*/
onTileGridTransitionEnd_: function(e) {
// We should remove the classes that control transitions when the
// transition ends so when the text is resized (Ctrl + '+'), no other
// transition should happen except those defined in the specification.
// For example, the tile has a transition for its 'width' property which
// is used when the tile is being hidden. But when you resize the text,
// and therefore the tile changes its 'width', this change should not be
// animated.
// When the tile grid width transition ends, we need to remove the class
// 'animate-grid-width' which handles the tile grid width transition, and
// individual tile transitions. TODO(pedrosimonetti): Investigate if we
// can improve the performance here by using a more efficient selector.
var tileGrid = this.tileGrid_;
if (event.target == tileGrid &&
tileGrid.classList.contains('animate-grid-width')) {
tileGrid.classList.remove('animate-grid-width');
if (this.onTileGridTransitionEndHandler_)
this.onTileGridTransitionEndHandler_();
}
},
/**
* Handles the end of the vertical page list transition.
* @param {Event} e The tile grid webkitTransitionEnd event.
*/
onPageListTransitionEnd_: function(e) {
// For the same reason as explained in onTileGridTransitionEnd_, we need
// to remove the class 'animate-page-height' when the vertical transition
// ends.
var pageList = $('page-list');
if (event.target == pageList &&
pageList.classList.contains('animate-page-height')) {
pageList.classList.remove('animate-page-height');
}
},
/**
* Handles the window keyup event.
* @param {Event} e The keyboard event.
*/
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);
},
};
return {
Tile2: Tile,
TilePage2: TilePage,
};
});