Home | History | Annotate | Download | only in ntp4
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 cr.define('ntp4', function() {
      6   'use strict';
      7 
      8   /**
      9    * Gives the proportion of the row width that is devoted to a single icon.
     10    * @param {number} rowTileCount The number of tiles in a row.
     11    * @return {number} The ratio between icon width and row width.
     12    */
     13   function tileWidthFraction(rowTileCount) {
     14     return rowTileCount +
     15         (rowTileCount - 1) * TILE_SPACING_FRACTION;
     16   }
     17 
     18   /**
     19    * Calculates an assortment of tile-related values for a grid with the
     20    * given dimensions.
     21    * @param {number} width The pixel width of the grid.
     22    * @param {number} numRowTiles The number of tiles in a row.
     23    * @return {Object} A mapping of pixel values.
     24    */
     25   function tileValuesForGrid(width, numRowTiles) {
     26     var tileWidth = width / tileWidthFraction(numRowTiles);
     27     var offsetX = tileWidth * (1 + TILE_SPACING_FRACTION);
     28     var interTileSpacing = offsetX - tileWidth;
     29 
     30     return {
     31       tileWidth: tileWidth,
     32       offsetX: offsetX,
     33       interTileSpacing: interTileSpacing,
     34     };
     35   }
     36 
     37   // The proportion of the tile width which will be used as spacing between
     38   // tiles.
     39   var TILE_SPACING_FRACTION = 1 / 8;
     40 
     41   // The smallest amount of horizontal blank space to display on the sides when
     42   // displaying a wide arrangement.
     43   var MIN_WIDE_MARGIN = 100;
     44 
     45   /**
     46    * Creates a new TilePage object. This object contains tiles and controls
     47    * their layout.
     48    * @param {string} name The display name for the page.
     49    * @param {Object} gridValues Pixel values that define the size and layout
     50    *     of the tile grid.
     51    * @constructor
     52    * @extends {HTMLDivElement}
     53    */
     54   function TilePage(name, gridValues) {
     55     var el = cr.doc.createElement('div');
     56     el.pageName = name;
     57     el.gridValues_ = gridValues;
     58     el.__proto__ = TilePage.prototype;
     59     el.initialize();
     60 
     61     return el;
     62   }
     63 
     64   /**
     65    * Takes a collection of grid layout pixel values and updates them with
     66    * additional tiling values that are calculated from TilePage constants.
     67    * @param {Object} grid The grid layout pixel values to update.
     68    */
     69   TilePage.initGridValues = function(grid) {
     70     // The amount of space we need to display a narrow grid (all narrow grids
     71     // are this size).
     72     grid.narrowWidth =
     73         grid.minTileWidth * tileWidthFraction(grid.minColCount);
     74     // The minimum amount of space we need to display a wide grid.
     75     grid.minWideWidth =
     76         grid.minTileWidth * tileWidthFraction(grid.maxColCount);
     77     // The largest we will ever display a wide grid.
     78     grid.maxWideWidth =
     79         grid.maxTileWidth * tileWidthFraction(grid.maxColCount);
     80     // Tile-related pixel values for the narrow display.
     81     grid.narrowTileValues = tileValuesForGrid(grid.narrowWidth,
     82                                               grid.minColCount);
     83     // Tile-related pixel values for the minimum narrow display.
     84     grid.wideTileValues = tileValuesForGrid(grid.minWideWidth,
     85                                             grid.maxColCount);
     86   },
     87 
     88   TilePage.prototype = {
     89     __proto__: HTMLDivElement.prototype,
     90 
     91     initialize: function() {
     92       this.className = 'tile-page';
     93 
     94       var title = this.ownerDocument.createElement('span');
     95       title.textContent = this.pageName;
     96       title.className = 'tile-page-title';
     97       this.appendChild(title);
     98 
     99       // Div that holds the tiles.
    100       this.tileGrid_ = this.ownerDocument.createElement('div');
    101       this.tileGrid_.className = 'tile-grid';
    102       this.appendChild(this.tileGrid_);
    103 
    104       // Ordered list of our tiles.
    105       this.tileElements_ = this.tileGrid_.getElementsByClassName('tile');
    106 
    107       this.lastWidth_ = this.clientWidth;
    108 
    109       this.eventTracker = new EventTracker();
    110       this.eventTracker.add(window, 'resize', this.onResize_.bind(this));
    111     },
    112 
    113     /**
    114      * Cleans up resources that are no longer needed after this TilePage
    115      * instance is removed from the DOM.
    116      */
    117     tearDown: function() {
    118       this.eventTracker.removeAll();
    119     },
    120 
    121     /**
    122      * @protected
    123      */
    124     appendTile: function(tileElement) {
    125       var wrapperDiv = tileElement.ownerDocument.createElement('div');
    126       wrapperDiv.appendChild(tileElement);
    127       wrapperDiv.className = 'tile';
    128       this.tileGrid_.appendChild(wrapperDiv);
    129 
    130       this.positionTile_(this.tileElements_.length - 1);
    131       this.classList.remove('resizing-tile-page');
    132     },
    133 
    134     /**
    135      * Calculates the x/y coordinates for an element and moves it there.
    136      * @param {number} The index of the element to be positioned.
    137      * @private
    138      */
    139     positionTile_: function(index) {
    140       var grid = this.gridValues_;
    141 
    142       var availableSpace = this.tileGrid_.clientWidth - 2 * MIN_WIDE_MARGIN;
    143       var wide = availableSpace >= grid.minWideWidth;
    144       // Calculate the portion of the tile's position that should be animated.
    145       var animatedTileValues = wide ?
    146           grid.wideTileValues : grid.narrowTileValues;
    147       // Animate the difference between three-wide and six-wide.
    148       var animatedLeftMargin = wide ?
    149           0 : (grid.minWideWidth - MIN_WIDE_MARGIN - grid.narrowWidth) / 2;
    150 
    151       var numRowTiles = wide ? grid.maxColCount : grid.minColCount;
    152       var col = index % numRowTiles;
    153       var row = Math.floor(index / numRowTiles);
    154       var animatedX = col * animatedTileValues.offsetX + animatedLeftMargin;
    155       var animatedY = row * (this.heightForWidth(animatedTileValues.tileWidth) +
    156                              animatedTileValues.interTileSpacing);
    157 
    158       // Calculate the final on-screen position for the tile.
    159       var effectiveGridWidth = wide ?
    160           Math.min(Math.max(availableSpace, grid.minWideWidth),
    161                    grid.maxWideWidth) :
    162           grid.narrowWidth;
    163       var realTileValues = tileValuesForGrid(effectiveGridWidth, numRowTiles);
    164       // leftMargin centers the grid within the avaiable space.
    165       var minMargin = wide ? MIN_WIDE_MARGIN : 0;
    166       var leftMargin =
    167           Math.max(minMargin,
    168                    (this.tileGrid_.clientWidth - effectiveGridWidth) / 2);
    169       var realX = col * realTileValues.offsetX + leftMargin;
    170       var realY = row * (this.heightForWidth(realTileValues.tileWidth) +
    171                          realTileValues.interTileSpacing);
    172 
    173       var tileWrapper = this.tileElements_[index];
    174       tileWrapper.style.left = animatedX + 'px';
    175       tileWrapper.style.top = animatedY + 'px';
    176       tileWrapper.firstChild.setBounds(realTileValues.tileWidth,
    177                                        realX - animatedX, realY - animatedY);
    178     },
    179 
    180     /**
    181      * Window resize event handler. Window resizes may trigger re-layouts.
    182      * @param {Object} e The resize event.
    183      */
    184     onResize_: function(e) {
    185       // Do nothing if the width didn't change.
    186       if (this.lastWidth_ == this.clientWidth)
    187         return;
    188 
    189       this.lastWidth_ = this.clientWidth;
    190       this.classList.add('resizing-tile-page');
    191 
    192       for (var i = 0; i < this.tileElements_.length; i++) {
    193         this.positionTile_(i);
    194       }
    195     },
    196 
    197     /**
    198      * Get the height for a tile of a certain width. Override this function to
    199      * get non-square tiles.
    200      * @param {number} width The pixel width of a tile.
    201      * @return {number} The height for |width|.
    202      */
    203     heightForWidth: function(width) {
    204       return width;
    205     },
    206   };
    207 
    208   return {
    209     TilePage: TilePage,
    210   };
    211 });
    212