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

Side by Side 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: Fixing error during initialization 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 unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 cr.define('ntp', function() { 5 cr.define('ntp', function() {
6 'use strict'; 6 'use strict';
7 7
8
9 /**
10 * A virtual Tile class. Each TilePage subclass should have its own Tile
11 * subclass implemented too (e.g. MostVisitedPage contains MostVisited
12 * tiles, and MostVisited is a Tile subclass).
13 * @constructor
14 * @param {Object} gridValues Tile page configuration object.
15 */
16 function Tile(gridValues) {
17 throw 'Tile is a virtual class and is not supposed to be instantiated';
18 }
19
20 /**
21 * Creates a Tile subclass.
22 */
23 Tile.subclass = function(Subclass) {
24 var Base = Tile.prototype;
25 for (var name in Base) {
26 if (!Subclass.hasOwnProperty(name)) {
27 Subclass[name] = Base[name];
28 }
29 }
30 for (var name in TileGetters) {
31 if (!Subclass.hasOwnProperty(name)) {
32 Subclass.__defineGetter__(name, TileGetters[name]);
33 }
34 }
35 return Subclass;
36 };
37
38 Tile.prototype = {
39 initialize: function(gridValues) {
40 // TODO: rename to tile
41 this.baseClassName = 'tile ' + gridValues.tileClassName;
42 this.className = this.baseClassName;
43
44 // TODO set using CSS?
45 if (gridValues.reinforceStyles) {
46 var style = this.style;
47 style.width = gridValues.tileWidth + 'px';
48 style.height = gridValues.tileHeight + 'px';
49 style.borderWidth = gridValues.tileBorderWidth + 'px';
50 }
51 },
52
53 tearDown_: function() {
54 },
55 };
56
57 var TileGetters = {
58 'tileCell': function() {
59 return findAncestorByClass(this, 'tile-cell');
60 },
61 'index': function() {
62 assert(this.tileCell);
63 return this.tileCell.index;
64 }
65 };
66
67
68
8 // We can't pass the currently dragging tile via dataTransfer because of 69 // We can't pass the currently dragging tile via dataTransfer because of
9 // http://crbug.com/31037 70 // http://crbug.com/31037
71 // TODO(xci) drag
10 var currentlyDraggingTile = null; 72 var currentlyDraggingTile = null;
11 function getCurrentlyDraggingTile() { 73 function getCurrentlyDraggingTile() {
12 return currentlyDraggingTile; 74 return currentlyDraggingTile;
13 } 75 }
14 function setCurrentlyDraggingTile(tile) { 76 function setCurrentlyDraggingTile(tile) {
15 currentlyDraggingTile = tile; 77 currentlyDraggingTile = tile;
16 if (tile) 78 if (tile)
17 ntp.enterRearrangeMode(); 79 ntp.enterRearrangeMode();
18 else 80 else
19 ntp.leaveRearrangeMode(); 81 ntp.leaveRearrangeMode();
20 } 82 }
21 83
22 /** 84 /**
23 * Changes the current dropEffect of a drag. This modifies the native cursor 85 * Changes the current dropEffect of a drag. This modifies the native cursor
24 * and serves as an indicator of what we should do at the end of the drag as 86 * and serves as an indicator of what we should do at the end of the drag as
25 * well as give indication to the user if a drop would succeed if they let go. 87 * well as give indication to the user if a drop would succeed if they let go.
26 * @param {DataTransfer} dataTransfer A dataTransfer object from a drag event. 88 * @param {DataTransfer} dataTransfer A dataTransfer object from a drag event.
27 * @param {string} effect A drop effect to change to (i.e. copy, move, none). 89 * @param {string} effect A drop effect to change to (i.e. copy, move, none).
28 */ 90 */
91 // TODO(xci) drag
29 function setCurrentDropEffect(dataTransfer, effect) { 92 function setCurrentDropEffect(dataTransfer, effect) {
30 dataTransfer.dropEffect = effect; 93 dataTransfer.dropEffect = effect;
31 if (currentlyDraggingTile) 94 if (currentlyDraggingTile)
32 currentlyDraggingTile.lastDropEffect = dataTransfer.dropEffect; 95 currentlyDraggingTile.lastDropEffect = dataTransfer.dropEffect;
33 } 96 }
34 97
35 /** 98 /**
36 * Creates a new Tile object. Tiles wrap content on a TilePage, providing 99 * Creates a new TileCell object. A TileCell represents a cell in the
37 * some styling and drag functionality. 100 * TilePage's grid. A TilePage uses TileCells to position Tiles in the proper
101 * place and to animate them individually. Each TileCell is associated to
102 * one Tile at a time (or none if it is a filler object), and that association
103 * might change when the grid is resized. When that happens, the grid is
104 * updated and the Tiles are moved to the proper TileCell. We cannot move the
105 * the TileCell itself during the resize because this transition is animated
106 * with CSS and there's no way to stop CSS animations, and we really want to
107 * animate with CSS to take advantage of hardware acceleration.
38 * @constructor 108 * @constructor
39 * @extends {HTMLDivElement} 109 * @extends {HTMLDivElement}
110 * @param {HTMLElement} tile Tile element that will be associated to the cell.
111 * @param {Object} gridValues Tile page configuration object.
40 */ 112 */
41 function Tile(contents) { 113 function TileCell(tile, gridValues) {
42 var tile = cr.doc.createElement('div'); 114 var tileCell = cr.doc.createElement('div');
43 tile.__proto__ = Tile.prototype; 115 tileCell.__proto__ = TileCell.prototype;
44 tile.initialize(contents); 116 tileCell.initialize(tile, gridValues);
45 117
46 return tile; 118 return tileCell;
47 } 119 }
48 120
49 Tile.prototype = { 121 TileCell.prototype = {
50 __proto__: HTMLDivElement.prototype, 122 __proto__: HTMLDivElement.prototype,
51 123
52 initialize: function(contents) { 124 initialize: function(tile, gridValues) {
53 // 'real' as opposed to doppleganger. 125 // 'real' as opposed to doppleganger.
54 this.className = 'tile real'; 126 // TODO(xci) remove the real class.
55 this.appendChild(contents); 127 this.className = 'tile-cell real';
56 contents.tile = this;
57 128
58 this.addEventListener('dragstart', this.onDragStart_); 129 if (gridValues.reinforceStyles) {
59 this.addEventListener('drag', this.onDragMove_); 130 var style = this.style;
60 this.addEventListener('dragend', this.onDragEnd_); 131 var borderWidth = 2 * gridValues.tileBorderWidth;
132 style.width = gridValues.tileWidth + borderWidth + 'px';
133 style.height = gridValues.tileHeight + borderWidth + 'px';
134 // TODO(xci): should we change to marginRight for RTL languages?
135 style.marginLeft = gridValues.tileHorMargin + 'px';
136 style.marginBottom = gridValues.tileVerMargin + 'px';
137 }
61 138
62 this.firstChild.addEventListener( 139 this.assign_(tile);
63 'webkitAnimationEnd', this.onContentsAnimationEnd_.bind(this));
64
65 this.eventTracker = new EventTracker();
66 }, 140 },
67 141
68 get index() { 142 get index() {
69 return Array.prototype.indexOf.call(this.tilePage.tileElements_, this); 143 return Array.prototype.indexOf.call(this.tilePage.tileElements_,
144 this.firstChild);
70 }, 145 },
71 146
72 get tilePage() { 147 get tilePage() {
73 return findAncestorByClass(this, 'tile-page'); 148 return findAncestorByClass(this, 'tile-page');
74 }, 149 },
75 150
76 /** 151 assign_: function(tile) {
77 * Position the tile at |x, y|, and store this as the grid location, i.e. 152 if (this.firstChild) {
78 * where the tile 'belongs' when it's not being dragged. 153 this.replaceChild(tile, this.firstChild);
79 * @param {number} x The x coordinate, in pixels. 154 } else {
80 * @param {number} y The y coordinate, in pixels. 155 this.appendChild(tile);
81 */ 156 }
82 setGridPosition: function(x, y) { 157 },
83 this.gridX = x; 158
84 this.gridY = y; 159 tearDown_: function() {
85 this.moveTo(x, y);
86 }, 160 },
87 161
88 /** 162 /**
89 * Position the tile at |x, y|.
90 * @param {number} x The x coordinate, in pixels.
91 * @param {number} y The y coordinate, in pixels.
92 */
93 moveTo: function(x, y) {
94 // left overrides right in LTR, and right takes precedence in RTL.
95 this.style.left = toCssPx(x);
96 this.style.right = toCssPx(x);
97 this.style.top = toCssPx(y);
98 },
99
100 /**
101 * The handler for dragstart events fired on |this|. 163 * The handler for dragstart events fired on |this|.
102 * @param {Event} e The event for the drag. 164 * @param {Event} e The event for the drag.
103 * @private 165 * @private
104 */ 166 */
167 // TODO(xci) drag
105 onDragStart_: function(e) { 168 onDragStart_: function(e) {
106 // The user may start dragging again during a previous drag's finishing 169 // The user may start dragging again during a previous drag's finishing
107 // animation. 170 // animation.
108 if (this.classList.contains('dragging')) 171 if (this.classList.contains('dragging'))
109 this.finalizeDrag_(); 172 this.finalizeDrag_();
110 173
111 setCurrentlyDraggingTile(this); 174 setCurrentlyDraggingTile(this);
112 175
113 e.dataTransfer.effectAllowed = 'copyMove'; 176 e.dataTransfer.effectAllowed = 'copyMove';
114 this.firstChild.setDragData(e.dataTransfer); 177 this.firstChild.setDragData(e.dataTransfer);
(...skipping 19 matching lines...) Expand all
134 this.parentNode.getBoundingClientRect().top; 197 this.parentNode.getBoundingClientRect().top;
135 198
136 this.onDragMove_(e); 199 this.onDragMove_(e);
137 }, 200 },
138 201
139 /** 202 /**
140 * The handler for drag events fired on |this|. 203 * The handler for drag events fired on |this|.
141 * @param {Event} e The event for the drag. 204 * @param {Event} e The event for the drag.
142 * @private 205 * @private
143 */ 206 */
207 // TODO(xci) drag
144 onDragMove_: function(e) { 208 onDragMove_: function(e) {
145 if (e.view != window || (e.x == 0 && e.y == 0)) { 209 if (e.view != window || (e.x == 0 && e.y == 0)) {
146 this.dragClone.hidden = true; 210 this.dragClone.hidden = true;
147 return; 211 return;
148 } 212 }
149 213
150 this.dragClone.hidden = false; 214 this.dragClone.hidden = false;
151 this.dragClone.style.left = toCssPx(e.x - this.dragOffsetX); 215 this.dragClone.style.left = toCssPx(e.x - this.dragOffsetX);
152 this.dragClone.style.top = toCssPx(e.y - this.dragOffsetY); 216 this.dragClone.style.top = toCssPx(e.y - this.dragOffsetY);
153 }, 217 },
154 218
155 /** 219 /**
156 * The handler for dragend events fired on |this|. 220 * The handler for dragend events fired on |this|.
157 * @param {Event} e The event for the drag. 221 * @param {Event} e The event for the drag.
158 * @private 222 * @private
159 */ 223 */
224 // TODO(xci) drag
160 onDragEnd_: function(e) { 225 onDragEnd_: function(e) {
161 this.dragClone.hidden = false; 226 this.dragClone.hidden = false;
162 this.dragClone.classList.add('placing'); 227 this.dragClone.classList.add('placing');
163 228
164 setCurrentlyDraggingTile(null); 229 setCurrentlyDraggingTile(null);
165 230
166 // tilePage will be null if we've already been removed. 231 // tilePage will be null if we've already been removed.
167 var tilePage = this.tilePage; 232 var tilePage = this.tilePage;
168 if (tilePage) 233 if (tilePage)
169 tilePage.positionTile_(this.index); 234 //tilePage.positionTile_(this.index); // TODO(xci) function was deleted!
170 235
171 // Take an appropriate action with the drag clone. 236 // Take an appropriate action with the drag clone.
172 if (this.landedOnTrash) { 237 if (this.landedOnTrash) {
173 this.dragClone.classList.add('deleting'); 238 this.dragClone.classList.add('deleting');
174 } else if (tilePage) { 239 } else if (tilePage) {
175 // TODO(dbeam): Until we fix dropEffect to the correct behavior it will 240 // TODO(dbeam): Until we fix dropEffect to the correct behavior it will
176 // differ on windows - crbug.com/39399. That's why we use the custom 241 // differ on windows - crbug.com/39399. That's why we use the custom
177 // this.lastDropEffect instead of e.dataTransfer.dropEffect. 242 // this.lastDropEffect instead of e.dataTransfer.dropEffect.
178 if (tilePage.selected && this.lastDropEffect != 'copy') { 243 if (tilePage.selected && this.lastDropEffect != 'copy') {
179 // The drag clone can still be hidden from the last drag move event. 244 // The drag clone can still be hidden from the last drag move event.
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
212 this.landedOnTrash = false; 277 this.landedOnTrash = false;
213 }, 278 },
214 279
215 /** 280 /**
216 * Creates a clone of this node offset by the coordinates. Used for the 281 * Creates a clone of this node offset by the coordinates. Used for the
217 * dragging effect where a tile appears to float off one side of the grid 282 * dragging effect where a tile appears to float off one side of the grid
218 * and re-appear on the other. 283 * and re-appear on the other.
219 * @param {number} x x-axis offset, in pixels. 284 * @param {number} x x-axis offset, in pixels.
220 * @param {number} y y-axis offset, in pixels. 285 * @param {number} y y-axis offset, in pixels.
221 */ 286 */
287 // TODO(xci) drag
222 showDoppleganger: function(x, y) { 288 showDoppleganger: function(x, y) {
223 // We always have to clear the previous doppleganger to make sure we get 289 // We always have to clear the previous doppleganger to make sure we get
224 // style updates for the contents of this tile. 290 // style updates for the contents of this tile.
225 this.clearDoppleganger(); 291 this.clearDoppleganger();
226 292
227 var clone = this.cloneNode(true); 293 var clone = this.cloneNode(true);
228 clone.classList.remove('real'); 294 clone.classList.remove('real');
229 clone.classList.add('doppleganger'); 295 clone.classList.add('doppleganger');
230 var clonelets = clone.querySelectorAll('.real'); 296 var clonelets = clone.querySelectorAll('.real');
231 for (var i = 0; i < clonelets.length; i++) { 297 for (var i = 0; i < clonelets.length; i++) {
232 clonelets[i].classList.remove('real'); 298 clonelets[i].classList.remove('real');
233 } 299 }
234 300
235 this.appendChild(clone); 301 this.appendChild(clone);
236 this.doppleganger_ = clone; 302 this.doppleganger_ = clone;
237 303
238 if (isRTL()) 304 if (isRTL())
239 x *= -1; 305 x *= -1;
240 306
241 this.doppleganger_.style.WebkitTransform = 'translate(' + x + 'px, ' + 307 this.doppleganger_.style.WebkitTransform = 'translate(' + x + 'px, ' +
242 y + 'px)'; 308 y + 'px)';
243 }, 309 },
244 310
245 /** 311 /**
246 * Destroys the current doppleganger. 312 * Destroys the current doppleganger.
247 */ 313 */
314 // TODO(xci) drag
248 clearDoppleganger: function() { 315 clearDoppleganger: function() {
249 if (this.doppleganger_) { 316 if (this.doppleganger_) {
250 this.removeChild(this.doppleganger_); 317 this.removeChild(this.doppleganger_);
251 this.doppleganger_ = null; 318 this.doppleganger_ = null;
252 } 319 }
253 }, 320 },
254 321
255 /** 322 /**
256 * Returns status of doppleganger. 323 * Returns status of doppleganger.
257 * @return {boolean} True if there is a doppleganger showing for |this|. 324 * @return {boolean} True if there is a doppleganger showing for |this|.
258 */ 325 */
326 // TODO(xci) drag
259 hasDoppleganger: function() { 327 hasDoppleganger: function() {
260 return !!this.doppleganger_; 328 return !!this.doppleganger_;
261 }, 329 },
262 330
263 /** 331 /**
264 * Cleans up after the drag is over. This is either called when the 332 * Cleans up after the drag is over. This is either called when the
265 * drag representation finishes animating to the final position, or when 333 * drag representation finishes animating to the final position, or when
266 * the next drag starts (if the user starts a 2nd drag very quickly). 334 * the next drag starts (if the user starts a 2nd drag very quickly).
267 * @private 335 * @private
268 */ 336 */
337 // TODO(xci) drag
269 finalizeDrag_: function() { 338 finalizeDrag_: function() {
270 assert(this.classList.contains('dragging')); 339 assert(this.classList.contains('dragging'));
271 340
272 var clone = this.dragClone; 341 var clone = this.dragClone;
273 this.dragClone = null; 342 this.dragClone = null;
274 343
275 clone.parentNode.removeChild(clone); 344 clone.parentNode.removeChild(clone);
276 this.eventTracker.remove(clone, 'webkitTransitionEnd'); 345 this.eventTracker.remove(clone, 'webkitTransitionEnd');
277 this.classList.remove('dragging'); 346 this.classList.remove('dragging');
278 if (this.firstChild.finalizeDrag) 347 if (this.firstChild.finalizeDrag)
279 this.firstChild.finalizeDrag(); 348 this.firstChild.finalizeDrag();
280 }, 349 },
281 350
282 /** 351 /**
283 * Called when the drag representation node is done migrating to its final 352 * Called when the drag representation node is done migrating to its final
284 * resting spot. 353 * resting spot.
285 * @param {Event} e The transition end event. 354 * @param {Event} e The transition end event.
286 */ 355 */
356 // TODO(xci) drag
287 onDragCloneTransitionEnd_: function(e) { 357 onDragCloneTransitionEnd_: function(e) {
288 if (this.classList.contains('dragging') && 358 if (this.classList.contains('dragging') &&
289 (e.propertyName == 'left' || e.propertyName == 'top' || 359 (e.propertyName == 'left' || e.propertyName == 'top' ||
290 e.propertyName == '-webkit-transform')) { 360 e.propertyName == '-webkit-transform')) {
291 this.finalizeDrag_(); 361 this.finalizeDrag_();
292 } 362 }
293 }, 363 },
294 364
295 /** 365 /**
296 * Called when an app is removed from Chrome. Animates its disappearance. 366 * Called when an app is removed from Chrome. Animates its disappearance.
(...skipping 12 matching lines...) Expand all
309 */ 379 */
310 onContentsAnimationEnd_: function(e) { 380 onContentsAnimationEnd_: function(e) {
311 if (this.firstChild.classList.contains('new-tile-contents')) 381 if (this.firstChild.classList.contains('new-tile-contents'))
312 this.firstChild.classList.remove('new-tile-contents'); 382 this.firstChild.classList.remove('new-tile-contents');
313 if (this.firstChild.classList.contains('removing-tile-contents')) 383 if (this.firstChild.classList.contains('removing-tile-contents'))
314 this.tilePage.removeTile(this, true); 384 this.tilePage.removeTile(this, true);
315 }, 385 },
316 }; 386 };
317 387
318 /** 388 /**
319 * Gives the proportion of the row width that is devoted to a single icon.
320 * @param {number} rowTileCount The number of tiles in a row.
321 * @param {number} tileSpacingFraction The proportion of the tile width which
322 * will be used as spacing between tiles.
323 * @return {number} The ratio between icon width and row width.
324 */
325 function tileWidthFraction(rowTileCount, tileSpacingFraction) {
326 return rowTileCount + (rowTileCount - 1) * tileSpacingFraction;
327 }
328
329 /**
330 * Calculates an assortment of tile-related values for a grid with the
331 * given dimensions.
332 * @param {number} width The pixel width of the grid.
333 * @param {number} numRowTiles The number of tiles in a row.
334 * @param {number} tileSpacingFraction The proportion of the tile width which
335 * will be used as spacing between tiles.
336 * @return {Object} A mapping of pixel values.
337 */
338 function tileValuesForGrid(width, numRowTiles, tileSpacingFraction) {
339 var tileWidth = width / tileWidthFraction(numRowTiles, tileSpacingFraction);
340 var offsetX = tileWidth * (1 + tileSpacingFraction);
341 var interTileSpacing = offsetX - tileWidth;
342
343 return {
344 tileWidth: tileWidth,
345 offsetX: offsetX,
346 interTileSpacing: interTileSpacing,
347 };
348 }
349
350 // The smallest amount of horizontal blank space to display on the sides when
351 // displaying a wide arrangement. There is an additional 26px of margin from
352 // the tile page padding.
353 var MIN_WIDE_MARGIN = 18;
354
355 /**
356 * Creates a new TilePage object. This object contains tiles and controls 389 * Creates a new TilePage object. This object contains tiles and controls
357 * their layout. 390 * their layout.
358 * @param {Object} gridValues Pixel values that define the size and layout
359 * of the tile grid.
360 * @constructor 391 * @constructor
361 * @extends {HTMLDivElement} 392 * @extends {HTMLDivElement}
362 */ 393 */
363 function TilePage(gridValues) { 394 function TilePage() {
364 var el = cr.doc.createElement('div'); 395 var el = cr.doc.createElement('div');
365 el.gridValues_ = gridValues;
366 el.__proto__ = TilePage.prototype; 396 el.__proto__ = TilePage.prototype;
367 el.initialize(); 397 el.initialize();
368 398
369 return el; 399 return el;
370 } 400 }
371 401
372 /**
373 * Takes a collection of grid layout pixel values and updates them with
374 * additional tiling values that are calculated from TilePage constants.
375 * @param {Object} grid The grid layout pixel values to update.
376 */
377 TilePage.initGridValues = function(grid) {
378 // The amount of space we need to display a narrow grid (all narrow grids
379 // are this size).
380 grid.narrowWidth =
381 grid.minTileWidth * tileWidthFraction(grid.minColCount,
382 grid.tileSpacingFraction);
383 // The minimum amount of space we need to display a wide grid.
384 grid.minWideWidth =
385 grid.minTileWidth * tileWidthFraction(grid.maxColCount,
386 grid.tileSpacingFraction);
387 // The largest we will ever display a wide grid.
388 grid.maxWideWidth =
389 grid.maxTileWidth * tileWidthFraction(grid.maxColCount,
390 grid.tileSpacingFraction);
391 // Tile-related pixel values for the narrow display.
392 grid.narrowTileValues = tileValuesForGrid(grid.narrowWidth,
393 grid.minColCount,
394 grid.tileSpacingFraction);
395 // Tile-related pixel values for the minimum narrow display.
396 grid.wideTileValues = tileValuesForGrid(grid.minWideWidth,
397 grid.maxColCount,
398 grid.tileSpacingFraction);
399 };
400
401 TilePage.prototype = { 402 TilePage.prototype = {
402 __proto__: HTMLDivElement.prototype, 403 __proto__: HTMLDivElement.prototype,
403 404
405 // The grid values should be defined by each TilePage subclass.
406 gridValues_: {
407 tileWidth: 0,
408 tileHeight: 0,
409 tileHorMargin: 0, // TODO margin with CSS / there's no margin in first col
410 tileVerMargin: 0,
411 tileBorderWidth: 0,
412 bottomPanelHorMargin: 0, // TODO it doesn't make sense having this here.
413
414 tileCount: 0, // TODO remove this dependency. rename it to maxTileCount.
415 tileClassName: '',
416 // TODO(pedrosimonetti): document this property? Consider removing it?
417 reinforceStyles: true,
418
419 // debug
420 slowFactor: 1,
421 debug: false
422 },
423
424 get tileElements() {
425 return this.tileElements_;
426 },
427
404 initialize: function() { 428 initialize: function() {
405 this.className = 'tile-page'; 429 this.className = 'tile-page';
406 430
407 // Div that acts as a custom scrollbar. The scrollbar has to live
408 // outside the content div so it doesn't flicker when scrolling (due to
409 // repainting after the scroll, then repainting again when moved in the
410 // onScroll handler). |scrollbar_| is only aesthetic, and it only
411 // represents the thumb. Actual events are still handled by the invisible
412 // native scrollbars. This div gives us more flexibility with the visuals.
413 this.scrollbar_ = this.ownerDocument.createElement('div');
414 this.scrollbar_.className = 'tile-page-scrollbar';
415 this.scrollbar_.hidden = true;
416 this.appendChild(this.scrollbar_);
417
418 // This contains everything but the scrollbar. 431 // This contains everything but the scrollbar.
419 this.content_ = this.ownerDocument.createElement('div'); 432 this.content_ = this.ownerDocument.createElement('div');
420 this.content_.className = 'tile-page-content'; 433 this.content_.className = 'tile-page-content';
421 this.appendChild(this.content_); 434 this.appendChild(this.content_);
422 435
423 // Div that sets the vertical position of the tile grid.
424 this.topMargin_ = this.ownerDocument.createElement('div');
425 this.topMargin_.className = 'top-margin';
426 this.content_.appendChild(this.topMargin_);
427
428 // Div that holds the tiles. 436 // Div that holds the tiles.
429 this.tileGrid_ = this.ownerDocument.createElement('div'); 437 this.tileGrid_ = this.ownerDocument.createElement('div');
430 this.tileGrid_.className = 'tile-grid'; 438 this.tileGrid_.className = 'tile-grid';
431 this.tileGrid_.style.minWidth = this.gridValues_.narrowWidth + 'px'; 439 // TODO(xci)
440 //this.tileGrid_.style.minWidth = this.gridValues_.narrowWidth + 'px';
432 this.content_.appendChild(this.tileGrid_); 441 this.content_.appendChild(this.tileGrid_);
433 442
443 // TODO(xci) new!
444 this.tileGridContent_ = this.ownerDocument.createElement('div');
445 this.tileGridContent_.className = 'tile-grid-content';
446 this.tileGrid_.appendChild(this.tileGridContent_);
447
448 // TODO(xci) new!
449 this.tileGrid_.addEventListener('webkitTransitionEnd',
450 this.onTileGridAnimationEnd_.bind(this));
451
434 // Ordered list of our tiles. 452 // Ordered list of our tiles.
435 this.tileElements_ = this.tileGrid_.getElementsByClassName('tile real'); 453 //this.tileElements_ = this.tileGrid_.getElementsByClassName('tile real');
454 this.tileElements_ = [];
455
436 // Ordered list of the elements which want to accept keyboard focus. These 456 // Ordered list of the elements which want to accept keyboard focus. These
437 // elements will not be a part of the normal tab order; the tile grid 457 // elements will not be a part of the normal tab order; the tile grid
438 // initially gets focused and then these elements can be focused via the 458 // initially gets focused and then these elements can be focused via the
439 // arrow keys. 459 // arrow keys.
460 // TODO(xci)
461 /*
440 this.focusableElements_ = 462 this.focusableElements_ =
441 this.tileGrid_.getElementsByClassName('focusable'); 463 this.tileGrid_.getElementsByClassName('focusable');
442 464 */
443 // These are properties used in updateTopMargin.
444 this.animatedTopMarginPx_ = 0;
445 this.topMarginPx_ = 0;
446 465
447 this.eventTracker = new EventTracker(); 466 this.eventTracker = new EventTracker();
448 this.eventTracker.add(window, 'resize', this.onResize_.bind(this)); 467 this.eventTracker.add(window, 'resize', this.onResize_.bind(this));
468 // TODO(xci) new
469 this.eventTracker.add(window, 'keyup', this.onKeyUp_.bind(this));
449 470
450 this.addEventListener('DOMNodeInsertedIntoDocument', 471 // TODO(xci)
451 this.onNodeInsertedIntoDocument_); 472 //this.addEventListener('DOMNodeInsertedIntoDocument',
473 // this.onNodeInsertedIntoDocument_);
452 474
453 this.content_.addEventListener('scroll', this.onScroll_.bind(this)); 475 //this.dragWrapper_ = new cr.ui.DragWrapper(this.tileGrid_, this);
454
455 this.dragWrapper_ = new cr.ui.DragWrapper(this.tileGrid_, this);
456 476
457 this.addEventListener('cardselected', this.handleCardSelection_); 477 this.addEventListener('cardselected', this.handleCardSelection_);
458 this.addEventListener('carddeselected', this.handleCardDeselection_); 478 this.addEventListener('carddeselected', this.handleCardDeselection_);
459 this.addEventListener('focus', this.handleFocus_); 479 //this.addEventListener('focus', this.handleFocus_);
460 this.addEventListener('keydown', this.handleKeyDown_); 480 //this.addEventListener('keydown', this.handleKeyDown_);
461 this.addEventListener('mousedown', this.handleMouseDown_); 481 //this.addEventListener('mousedown', this.handleMouseDown_);
462 482
463 this.focusElementIndex_ = -1; 483 //this.focusElementIndex_ = -1;
464 }, 484 },
465 485
466 get tiles() { 486 get tiles() {
467 return this.tileElements_; 487 return this.tileElements_;
468 }, 488 },
469 489
470 get tileCount() { 490 get tileCount() {
471 return this.tileElements_.length; 491 return this.tileElements_.length;
472 }, 492 },
473 493
474 get selected() { 494 get selected() {
475 return Array.prototype.indexOf.call(this.parentNode.children, this) == 495 return Array.prototype.indexOf.call(this.parentNode.children, this) ==
476 ntp.getCardSlider().currentCard; 496 ntp.getCardSlider().currentCard;
477 }, 497 },
478 498
479 /** 499 /**
480 * The size of the margin (unused space) on the sides of the tile grid, in
481 * pixels.
482 * @type {number}
483 */
484 get sideMargin() {
485 return this.layoutValues_.leftMargin;
486 },
487
488 /**
489 * Returns the width of the scrollbar, in pixels, if it is active, or 0
490 * otherwise.
491 * @type {number}
492 */
493 get scrollbarWidth() {
494 return this.scrollbar_.hidden ? 0 : 13;
495 },
496
497 /**
498 * Returns any extra padding to insert to the bottom of a tile page. By
499 * default there is none, but subclasses can override.
500 * @type {number}
501 */
502 get extraBottomPadding() {
503 return 0;
504 },
505
506 /**
507 * The notification content of this tile (if any, otherwise null). 500 * The notification content of this tile (if any, otherwise null).
508 * @type {!HTMLElement} 501 * @type {!HTMLElement}
509 */ 502 */
510 get notification() { 503 get notification() {
511 return this.topMargin_.nextElementSibling.id == 'notification-container' ? 504 return this.content_.firstChild.id == 'notification-container' ?
512 this.topMargin_.nextElementSibling : null; 505 this.content_.firstChild : null;
513 }, 506 },
514 /** 507 /**
515 * The notification content of this tile (if any, otherwise null). 508 * The notification content of this tile (if any, otherwise null).
516 * @type {!HTMLElement} 509 * @type {!HTMLElement}
517 */ 510 */
518 set notification(node) { 511 set notification(node) {
519 assert(node instanceof HTMLElement, '|node| isn\'t an HTMLElement!'); 512 assert(node instanceof HTMLElement, '|node| isn\'t an HTMLElement!');
520 // NOTE: Implicitly removes from DOM if |node| is inside it. 513 // NOTE: Implicitly removes from DOM if |node| is inside it.
521 this.content_.insertBefore(node, this.topMargin_.nextElementSibling); 514 this.content_.insertBefore(node, this.content_.firstChild);
522 this.positionNotification_(); 515 this.positionNotification_();
523 }, 516 },
524 517
525 /** 518 /**
526 * Fetches the size, in pixels, of the padding-top of the tile contents.
527 * @type {number}
528 */
529 get contentPadding() {
530 if (typeof this.contentPadding_ == 'undefined') {
531 this.contentPadding_ =
532 parseInt(getComputedStyle(this.content_).paddingTop, 10);
533 }
534 return this.contentPadding_;
535 },
536
537 /**
538 * Removes the tilePage from the DOM and cleans up event handlers. 519 * Removes the tilePage from the DOM and cleans up event handlers.
539 */ 520 */
540 remove: function() { 521 remove: function() {
541 // This checks arguments.length as most remove functions have a boolean 522 // This checks arguments.length as most remove functions have a boolean
542 // |opt_animate| argument, but that's not necesarilly applicable to 523 // |opt_animate| argument, but that's not necesarilly applicable to
543 // removing a tilePage. Selecting a different card in an animated way and 524 // removing a tilePage. Selecting a different card in an animated way and
544 // deleting the card afterward is probably a better choice. 525 // deleting the card afterward is probably a better choice.
545 assert(typeof arguments[0] != 'boolean', 526 assert(typeof arguments[0] != 'boolean',
546 'This function takes no |opt_animate| argument.'); 527 'This function takes no |opt_animate| argument.');
547 this.tearDown_(); 528 this.tearDown_();
548 this.parentNode.removeChild(this); 529 this.parentNode.removeChild(this);
549 }, 530 },
550 531
551 /** 532 /**
552 * Cleans up resources that are no longer needed after this TilePage 533 * Cleans up resources that are no longer needed after this TilePage
553 * instance is removed from the DOM. 534 * instance is removed from the DOM.
554 * @private 535 * @private
555 */ 536 */
556 tearDown_: function() { 537 tearDown_: function() {
557 this.eventTracker.removeAll(); 538 this.eventTracker.removeAll();
558 }, 539 },
559 540
560 /** 541 /**
561 * Appends a tile to the end of the tile grid. 542 * Appends a tile to the end of the tile grid.
562 * @param {HTMLElement} tileElement The contents of the tile. 543 * @param {HTMLElement} tileElement The contents of the tile.
563 * @param {boolean} animate If true, the append will be animated. 544 * @param {boolean} animate If true, the append will be animated.
564 * @protected 545 * @protected
565 */ 546 */
547 // TODO(xci)
548 /*
566 appendTile: function(tileElement, animate) { 549 appendTile: function(tileElement, animate) {
567 this.addTileAt(tileElement, this.tileElements_.length, animate); 550 this.addTileAt(tileElement, this.tileElements_.length, animate);
568 }, 551 },
552 */
569 553
570 /** 554 /**
571 * Adds the given element to the tile grid. 555 * Adds the given element to the tile grid.
572 * @param {Node} tileElement The tile object/node to insert. 556 * @param {Node} tileElement The tile object/node to insert.
573 * @param {number} index The location in the tile grid to insert it at. 557 * @param {number} index The location in the tile grid to insert it at.
574 * @param {boolean} animate If true, the tile in question will be 558 * @param {boolean} animate If true, the tile in question will be
575 * animated (other tiles, if they must reposition, do not animate). 559 * animated (other tiles, if they must reposition, do not animate).
576 * @protected 560 * @protected
577 */ 561 */
562 // TODO(xci)
563 /*
578 addTileAt: function(tileElement, index, animate) { 564 addTileAt: function(tileElement, index, animate) {
579 this.classList.remove('animating-tile-page');
580 if (animate)
581 tileElement.classList.add('new-tile-contents');
582
583 // Make sure the index is positive and either in the the bounds of
584 // this.tileElements_ or at the end (meaning append).
585 assert(index >= 0 && index <= this.tileElements_.length);
586
587 var wrapperDiv = new Tile(tileElement);
588 // If is out of the bounds of the tile element list, .insertBefore() will
589 // act just like appendChild().
590 this.tileGrid_.insertBefore(wrapperDiv, this.tileElements_[index]);
591 this.calculateLayoutValues_(); 565 this.calculateLayoutValues_();
592 this.heightChanged_();
593
594 this.repositionTiles_();
595 this.fireAddedEvent(wrapperDiv, index, animate); 566 this.fireAddedEvent(wrapperDiv, index, animate);
596 }, 567 },
568 */
597 569
598 /** 570 /**
599 * Notify interested subscribers that a tile has been removed from this 571 * Notify interested subscribers that a tile has been removed from this
600 * page. 572 * page.
601 * @param {Tile} tile The newly added tile. 573 * @param {TileCell} tile The newly added tile.
602 * @param {number} index The index of the tile that was added. 574 * @param {number} index The index of the tile that was added.
603 * @param {boolean} wasAnimated Whether the removal was animated. 575 * @param {boolean} wasAnimated Whether the removal was animated.
604 */ 576 */
605 fireAddedEvent: function(tile, index, wasAnimated) { 577 fireAddedEvent: function(tile, index, wasAnimated) {
606 var e = document.createEvent('Event'); 578 var e = document.createEvent('Event');
607 e.initEvent('tilePage:tile_added', true, true); 579 e.initEvent('tilePage:tile_added', true, true);
608 e.addedIndex = index; 580 e.addedIndex = index;
609 e.addedTile = tile; 581 e.addedTile = tile;
610 e.wasAnimated = wasAnimated; 582 e.wasAnimated = wasAnimated;
611 this.dispatchEvent(e); 583 this.dispatchEvent(e);
(...skipping 13 matching lines...) Expand all
625 this.calculateLayoutValues_(); 597 this.calculateLayoutValues_();
626 this.cleanupDrag(); 598 this.cleanupDrag();
627 599
628 if (!opt_dontNotify) 600 if (!opt_dontNotify)
629 this.fireRemovedEvent(tile, index, !!opt_animate); 601 this.fireRemovedEvent(tile, index, !!opt_animate);
630 }, 602 },
631 603
632 /** 604 /**
633 * Notify interested subscribers that a tile has been removed from this 605 * Notify interested subscribers that a tile has been removed from this
634 * page. 606 * page.
635 * @param {Tile} tile The tile that was removed. 607 * @param {TileCell} tile The tile that was removed.
636 * @param {number} oldIndex Where the tile was positioned before removal. 608 * @param {number} oldIndex Where the tile was positioned before removal.
637 * @param {boolean} wasAnimated Whether the removal was animated. 609 * @param {boolean} wasAnimated Whether the removal was animated.
638 */ 610 */
639 fireRemovedEvent: function(tile, oldIndex, wasAnimated) { 611 fireRemovedEvent: function(tile, oldIndex, wasAnimated) {
640 var e = document.createEvent('Event'); 612 var e = document.createEvent('Event');
641 e.initEvent('tilePage:tile_removed', true, true); 613 e.initEvent('tilePage:tile_removed', true, true);
642 e.removedIndex = oldIndex; 614 e.removedIndex = oldIndex;
643 e.removedTile = tile; 615 e.removedTile = tile;
644 e.wasAnimated = wasAnimated; 616 e.wasAnimated = wasAnimated;
645 this.dispatchEvent(e); 617 this.dispatchEvent(e);
646 }, 618 },
647 619
648 /** 620 /**
649 * Removes all tiles from the page. 621 * Removes all tiles from the page.
650 */ 622 */
651 removeAllTiles: function() { 623 removeAllTiles: function() {
624 // TODO(xci) dispatch individual tearDown functions
652 this.tileGrid_.innerHTML = ''; 625 this.tileGrid_.innerHTML = '';
653 }, 626 },
654 627
655 /** 628 /**
656 * Called when the page is selected (in the card selector). 629 * Called when the page is selected (in the card selector).
657 * @param {Event} e A custom cardselected event. 630 * @param {Event} e A custom cardselected event.
658 * @private 631 * @private
659 */ 632 */
660 handleCardSelection_: function(e) { 633 handleCardSelection_: function(e) {
661 this.tabIndex = 1; 634 this.tabIndex = 1;
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after
790 }, 763 },
791 764
792 /** 765 /**
793 * Makes some calculations for tile layout. These change depending on 766 * Makes some calculations for tile layout. These change depending on
794 * height, width, and the number of tiles. 767 * height, width, and the number of tiles.
795 * TODO(estade): optimize calls to this function. Do nothing if the page is 768 * TODO(estade): optimize calls to this function. Do nothing if the page is
796 * hidden, but call before being shown. 769 * hidden, but call before being shown.
797 * @private 770 * @private
798 */ 771 */
799 calculateLayoutValues_: function() { 772 calculateLayoutValues_: function() {
800 var grid = this.gridValues_; 773 this.layout_();
801 var availableSpace = this.tileGrid_.clientWidth - 2 * MIN_WIDE_MARGIN;
802 var wide = availableSpace >= grid.minWideWidth;
803 var numRowTiles = wide ? grid.maxColCount : grid.minColCount;
804
805 var effectiveGridWidth = wide ?
806 Math.min(Math.max(availableSpace, grid.minWideWidth),
807 grid.maxWideWidth) :
808 grid.narrowWidth;
809 var realTileValues = tileValuesForGrid(effectiveGridWidth, numRowTiles,
810 grid.tileSpacingFraction);
811
812 // leftMargin centers the grid within the avaiable space.
813 var minMargin = wide ? MIN_WIDE_MARGIN : 0;
814 var leftMargin =
815 Math.max(minMargin,
816 (this.tileGrid_.clientWidth - effectiveGridWidth) / 2);
817
818 var rowHeight = this.heightForWidth(realTileValues.tileWidth) +
819 realTileValues.interTileSpacing;
820
821 this.layoutValues_ = {
822 colWidth: realTileValues.offsetX,
823 gridWidth: effectiveGridWidth,
824 leftMargin: leftMargin,
825 numRowTiles: numRowTiles,
826 rowHeight: rowHeight,
827 tileWidth: realTileValues.tileWidth,
828 wide: wide,
829 };
830 774
831 // We need to update the top margin as well. 775 // We need to update the top margin as well.
832 this.updateTopMargin_(); 776 this.updateTopMargin_();
833 777
778 // TODO(pedrosimonetti): when do we really need to send this message?
834 this.firePageLayoutEvent_(); 779 this.firePageLayoutEvent_();
835 }, 780 },
836 781
837 /** 782 /**
838 * Dispatches the custom pagelayout event. 783 * Dispatches the custom pagelayout event.
839 * @private 784 * @private
840 */ 785 */
841 firePageLayoutEvent_: function() { 786 firePageLayoutEvent_: function() {
842 cr.dispatchSimpleEvent(this, 'pagelayout', true, true); 787 cr.dispatchSimpleEvent(this, 'pagelayout', true, true);
843 }, 788 },
844 789
845 /**
846 * @return {number} The amount of margin that should be animated (in pixels)
847 * for the current grid layout.
848 */
849 getAnimatedLeftMargin_: function() {
850 if (this.layoutValues_.wide)
851 return 0;
852
853 var grid = this.gridValues_;
854 return (grid.minWideWidth - MIN_WIDE_MARGIN - grid.narrowWidth) / 2;
855 },
856
857 /**
858 * Calculates the x/y coordinates for an element and moves it there.
859 * @param {number} index The index of the element to be positioned.
860 * @param {number} indexOffset If provided, this is added to |index| when
861 * positioning the tile. The effect is that the tile will be positioned
862 * in a non-default location.
863 * @private
864 */
865 positionTile_: function(index, indexOffset) {
866 var grid = this.gridValues_;
867 var layout = this.layoutValues_;
868
869 indexOffset = typeof indexOffset != 'undefined' ? indexOffset : 0;
870 // Add the offset _after_ the modulus division. We might want to show the
871 // tile off the side of the grid.
872 var col = index % layout.numRowTiles + indexOffset;
873 var row = Math.floor(index / layout.numRowTiles);
874 // Calculate the final on-screen position for the tile.
875 var realX = col * layout.colWidth + layout.leftMargin;
876 var realY = row * layout.rowHeight;
877
878 // Calculate the portion of the tile's position that should be animated.
879 var animatedTileValues = layout.wide ?
880 grid.wideTileValues : grid.narrowTileValues;
881 // Animate the difference between three-wide and six-wide.
882 var animatedLeftMargin = this.getAnimatedLeftMargin_();
883 var animatedX = col * animatedTileValues.offsetX + animatedLeftMargin;
884 var animatedY = row * (this.heightForWidth(animatedTileValues.tileWidth) +
885 animatedTileValues.interTileSpacing);
886
887 var tile = this.tileElements_[index];
888 tile.setGridPosition(animatedX, animatedY);
889 tile.firstChild.setBounds(layout.tileWidth,
890 realX - animatedX,
891 realY - animatedY);
892
893 // This code calculates whether the tile needs to show a clone of itself
894 // wrapped around the other side of the tile grid.
895 var offTheRight = col == layout.numRowTiles ||
896 (col == layout.numRowTiles - 1 && tile.hasDoppleganger());
897 var offTheLeft = col == -1 || (col == 0 && tile.hasDoppleganger());
898 if (this.isCurrentDragTarget && (offTheRight || offTheLeft)) {
899 var sign = offTheRight ? 1 : -1;
900 tile.showDoppleganger(-layout.numRowTiles * layout.colWidth * sign,
901 layout.rowHeight * sign);
902 } else {
903 tile.clearDoppleganger();
904 }
905
906 if (index == this.tileElements_.length - 1) {
907 this.tileGrid_.style.height = (realY + layout.rowHeight) + 'px';
908 this.queueUpdateScrollbars_();
909 }
910 },
911 790
912 /** 791 /**
913 * Gets the index of the tile that should occupy coordinate (x, y). Note 792 * Gets the index of the tile that should occupy coordinate (x, y). Note
914 * that this function doesn't care where the tiles actually are, and will 793 * that this function doesn't care where the tiles actually are, and will
915 * return an index even for the space between two tiles. This function is 794 * return an index even for the space between two tiles. This function is
916 * effectively the inverse of |positionTile_|. 795 * effectively the inverse of |positionTile_|.
917 * @param {number} x The x coordinate, in pixels, relative to the left of 796 * @param {number} x The x coordinate, in pixels, relative to the left of
918 * |this|. 797 * |this|.
919 * @param {number} y The y coordinate, in pixels, relative to the top of 798 * @param {number} y The y coordinate, in pixels, relative to the top of
920 * |this|. 799 * |this|.
921 * @private 800 * @private
922 */ 801 */
802 // TODO(xci) drag
923 getWouldBeIndexForPoint_: function(x, y) { 803 getWouldBeIndexForPoint_: function(x, y) {
924 var grid = this.gridValues_; 804 var grid = this.gridValues_;
925 var layout = this.layoutValues_; 805 var layout = this.layoutValues_;
926 806
927 var gridClientRect = this.tileGrid_.getBoundingClientRect(); 807 var gridClientRect = this.tileGrid_.getBoundingClientRect();
928 var col = Math.floor((x - gridClientRect.left - layout.leftMargin) / 808 var col = Math.floor((x - gridClientRect.left - layout.leftMargin) /
929 layout.colWidth); 809 layout.colWidth);
930 if (col < 0 || col >= layout.numRowTiles) 810 if (col < 0 || col >= layout.numRowTiles)
931 return -1; 811 return -1;
932 812
933 if (isRTL()) 813 if (isRTL())
934 col = layout.numRowTiles - 1 - col; 814 col = layout.numRowTiles - 1 - col;
935 815
936 var row = Math.floor((y - gridClientRect.top) / layout.rowHeight); 816 var row = Math.floor((y - gridClientRect.top) / layout.rowHeight);
937 return row * layout.numRowTiles + col; 817 return row * layout.numRowTiles + col;
938 }, 818 },
939 819
940 /** 820 /**
941 * Window resize event handler. Window resizes may trigger re-layouts.
942 * @param {Object} e The resize event.
943 */
944 onResize_: function(e) {
945 if (this.lastWidth_ == this.clientWidth &&
946 this.lastHeight_ == this.clientHeight) {
947 return;
948 }
949
950 this.calculateLayoutValues_();
951
952 this.lastWidth_ = this.clientWidth;
953 this.lastHeight_ = this.clientHeight;
954 this.classList.add('animating-tile-page');
955 this.heightChanged_();
956
957 this.positionNotification_();
958 this.repositionTiles_();
959 },
960
961 /**
962 * The tile grid has an image mask which fades at the edges. We only show 821 * The tile grid has an image mask which fades at the edges. We only show
963 * the mask when there is an active drag; it obscures doppleganger tiles 822 * the mask when there is an active drag; it obscures doppleganger tiles
964 * as they enter or exit the grid. 823 * as they enter or exit the grid.
965 * @private 824 * @private
966 */ 825 */
826 // TODO(xci) drag
967 updateMask_: function() { 827 updateMask_: function() {
968 if (!this.isCurrentDragTarget) { 828 if (!this.isCurrentDragTarget) {
969 this.tileGrid_.style.WebkitMaskBoxImage = ''; 829 this.tileGrid_.style.WebkitMaskBoxImage = '';
970 return; 830 return;
971 } 831 }
972 832
973 var leftMargin = this.layoutValues_.leftMargin; 833 var leftMargin = this.layoutValues_.leftMargin;
974 // The fade distance is the space between tiles. 834 // The fade distance is the space between tiles.
975 var fadeDistance = (this.gridValues_.tileSpacingFraction * 835 var fadeDistance = (this.gridValues_.tileSpacingFraction *
976 this.layoutValues_.tileWidth); 836 this.layoutValues_.tileWidth);
977 fadeDistance = Math.min(leftMargin, fadeDistance); 837 fadeDistance = Math.min(leftMargin, fadeDistance);
978 // On Skia we don't use any fade because it works very poorly. See 838 // On Skia we don't use any fade because it works very poorly. See
979 // http://crbug.com/99373 839 // http://crbug.com/99373
980 if (!cr.isMac) 840 if (!cr.isMac)
981 fadeDistance = 1; 841 fadeDistance = 1;
982 var gradient = 842 var gradient =
983 '-webkit-linear-gradient(left,' + 843 '-webkit-linear-gradient(left,' +
984 'transparent, ' + 844 'transparent, ' +
985 'transparent ' + (leftMargin - fadeDistance) + 'px, ' + 845 'transparent ' + (leftMargin - fadeDistance) + 'px, ' +
986 'black ' + leftMargin + 'px, ' + 846 'black ' + leftMargin + 'px, ' +
987 'black ' + (this.tileGrid_.clientWidth - leftMargin) + 'px, ' + 847 'black ' + (this.tileGrid_.clientWidth - leftMargin) + 'px, ' +
988 'transparent ' + (this.tileGrid_.clientWidth - leftMargin + 848 'transparent ' + (this.tileGrid_.clientWidth - leftMargin +
989 fadeDistance) + 'px, ' + 849 fadeDistance) + 'px, ' +
990 'transparent)'; 850 'transparent)';
991 this.tileGrid_.style.WebkitMaskBoxImage = gradient; 851 this.tileGrid_.style.WebkitMaskBoxImage = gradient;
992 }, 852 },
993 853
854 // TODO(xci) delete (used by drag and drop)
994 updateTopMargin_: function() { 855 updateTopMargin_: function() {
995 var layout = this.layoutValues_; 856 return;
996
997 // The top margin is set so that the vertical midpoint of the grid will
998 // be 1/3 down the page.
999 var numTiles = this.tileCount +
1000 (this.isCurrentDragTarget && !this.withinPageDrag_ ? 1 : 0);
1001 var numRows = Math.max(1, Math.ceil(numTiles / layout.numRowTiles));
1002 var usedHeight = layout.rowHeight * numRows;
1003 var newMargin = document.documentElement.clientHeight / 3 -
1004 usedHeight / 3 - this.contentPadding;
1005 // The 'height' style attribute of topMargin is non-zero to work around
1006 // webkit's collapsing margin behavior, so we have to factor that into
1007 // our calculations here.
1008 newMargin = Math.max(newMargin, 0) - this.topMargin_.offsetHeight;
1009
1010 // |newMargin| is the final margin we actually want to show. However,
1011 // part of that should be animated and part should not (for the same
1012 // reason as with leftMargin). The approach is to consider differences
1013 // when the layout changes from wide to narrow or vice versa as
1014 // 'animatable'. These differences accumulate in animatedTopMarginPx_,
1015 // while topMarginPx_ caches the real (total) margin. Either of these
1016 // calculations may come out to be negative, so we use margins as the
1017 // css property.
1018
1019 if (typeof this.topMarginIsForWide_ == 'undefined')
1020 this.topMarginIsForWide_ = layout.wide;
1021 if (this.topMarginIsForWide_ != layout.wide) {
1022 this.animatedTopMarginPx_ += newMargin - this.topMarginPx_;
1023 this.topMargin_.style.marginBottom = toCssPx(this.animatedTopMarginPx_);
1024 }
1025
1026 this.topMarginIsForWide_ = layout.wide;
1027 this.topMarginPx_ = newMargin;
1028 this.topMargin_.style.marginTop =
1029 toCssPx(this.topMarginPx_ - this.animatedTopMarginPx_);
1030 }, 857 },
1031 858
1032 /** 859 /**
1033 * Position the notification if there's one showing. 860 * Position the notification if there's one showing.
1034 */ 861 */
1035 positionNotification_: function() { 862 positionNotification_: function() {
1036 var notification = this.notification; 863 if (this.notification && !this.notification.hidden) {
1037 if (!notification || notification.hidden)
1038 return; 864 return;
1039 865 this.notification.style.margin =
1040 // Update the horizontal position. 866 -this.notification.offsetHeight + 'px ' +
1041 var animatedLeftMargin = this.getAnimatedLeftMargin_(); 867 this.layoutValues_.leftMargin + 'px 0';
1042 notification.style.WebkitMarginStart = animatedLeftMargin + 'px'; 868 }
1043 var leftOffset = (this.layoutValues_.leftMargin - animatedLeftMargin) *
1044 (isRTL() ? -1 : 1);
1045 notification.style.WebkitTransform = 'translateX(' + leftOffset + 'px)';
1046
1047 // Update the allowable widths of the text.
1048 var buttonWidth = notification.querySelector('button').offsetWidth + 8;
1049 notification.querySelector('span').style.maxWidth =
1050 this.layoutValues_.gridWidth - buttonWidth + 'px';
1051
1052 // This makes sure the text doesn't condense smaller than the narrow size
1053 // of the grid (e.g. when a user makes the window really small).
1054 notification.style.minWidth =
1055 this.gridValues_.narrowWidth - buttonWidth + 'px';
1056
1057 // Update the top position.
1058 notification.style.marginTop = -notification.offsetHeight + 'px';
1059 }, 869 },
1060 870
1061 /** 871 /**
1062 * Handles final setup that can only happen after |this| is inserted into 872 * Handles final setup that can only happen after |this| is inserted into
1063 * the page. 873 * the page.
1064 * @private 874 * @private
1065 */ 875 */
1066 onNodeInsertedIntoDocument_: function(e) { 876 onNodeInsertedIntoDocument_: function(e) {
1067 this.calculateLayoutValues_(); 877 this.calculateLayoutValues_();
1068 this.heightChanged_();
1069 },
1070
1071 /**
1072 * Called when the height of |this| has changed: update the size of
1073 * tileGrid.
1074 * @private
1075 */
1076 heightChanged_: function() {
1077 // The tile grid will expand to the bottom footer, or enough to hold all
1078 // the tiles, whichever is greater. It would be nicer if tilePage were
1079 // a flex box, and the tile grid could be box-flex: 1, but this exposes a
1080 // bug where repositioning tiles will cause the scroll position to reset.
1081 this.tileGrid_.style.minHeight = (this.clientHeight -
1082 this.tileGrid_.offsetTop - this.content_.offsetTop -
1083 this.extraBottomPadding -
1084 (this.footerNode_ ? this.footerNode_.clientHeight : 0)) + 'px';
1085 }, 878 },
1086 879
1087 /** 880 /**
1088 * Places an element at the bottom of the content div. Used in bare-minimum 881 * Places an element at the bottom of the content div. Used in bare-minimum
1089 * mode to hold #footer. 882 * mode to hold #footer.
1090 * @param {HTMLElement} footerNode The node to append to content. 883 * @param {HTMLElement} footerNode The node to append to content.
1091 */ 884 */
1092 appendFooter: function(footerNode) { 885 appendFooter: function(footerNode) {
1093 this.footerNode_ = footerNode; 886 this.footerNode_ = footerNode;
1094 this.content_.appendChild(footerNode); 887 this.content_.appendChild(footerNode);
(...skipping 10 matching lines...) Expand all
1105 * @param {Event} e The mousewheel event. 898 * @param {Event} e The mousewheel event.
1106 */ 899 */
1107 handleMouseWheel: function(e) { 900 handleMouseWheel: function(e) {
1108 if (e.wheelDeltaY == 0) 901 if (e.wheelDeltaY == 0)
1109 return false; 902 return false;
1110 903
1111 this.content_.scrollTop -= e.wheelDeltaY / 3; 904 this.content_.scrollTop -= e.wheelDeltaY / 3;
1112 return true; 905 return true;
1113 }, 906 },
1114 907
1115 /**
1116 * Handler for the 'scroll' event on |content_|.
1117 * @param {Event} e The scroll event.
1118 * @private
1119 */
1120 onScroll_: function(e) {
1121 this.queueUpdateScrollbars_();
1122 },
1123
1124 /**
1125 * ID of scrollbar update timer. If 0, there's no scrollbar re-calc queued.
1126 * @private
1127 */
1128 scrollbarUpdate_: 0,
1129
1130 /**
1131 * Queues an update on the custom scrollbar. Used for two reasons: first,
1132 * coalescing of multiple updates, and second, because action like
1133 * repositioning a tile can require a delay before they affect values
1134 * like clientHeight.
1135 * @private
1136 */
1137 queueUpdateScrollbars_: function() {
1138 if (this.scrollbarUpdate_)
1139 return;
1140
1141 this.scrollbarUpdate_ = window.setTimeout(
1142 this.doUpdateScrollbars_.bind(this), 0);
1143 },
1144
1145 /**
1146 * Does the work of calculating the visibility, height and position of the
1147 * scrollbar thumb (there is no track or buttons).
1148 * @private
1149 */
1150 doUpdateScrollbars_: function() {
1151 this.scrollbarUpdate_ = 0;
1152
1153 var content = this.content_;
1154
1155 // Adjust scroll-height to account for possible header-bar.
1156 var adjustedScrollHeight = content.scrollHeight - content.offsetTop;
1157
1158 if (adjustedScrollHeight <= content.clientHeight) {
1159 this.scrollbar_.hidden = true;
1160 return;
1161 } else {
1162 this.scrollbar_.hidden = false;
1163 }
1164
1165 var thumbTop = content.offsetTop +
1166 content.scrollTop / adjustedScrollHeight * content.clientHeight;
1167 var thumbHeight = content.clientHeight / adjustedScrollHeight *
1168 this.clientHeight;
1169
1170 this.scrollbar_.style.top = thumbTop + 'px';
1171 this.scrollbar_.style.height = thumbHeight + 'px';
1172 this.firePageLayoutEvent_();
1173 },
1174
1175 /**
1176 * Get the height for a tile of a certain width. Override this function to
1177 * get non-square tiles.
1178 * @param {number} width The pixel width of a tile.
1179 * @return {number} The height for |width|.
1180 */
1181 heightForWidth: function(width) {
1182 return width;
1183 },
1184
1185 /** Dragging **/ 908 /** Dragging **/
1186 909
910 // TODO(xci) drag
1187 get isCurrentDragTarget() { 911 get isCurrentDragTarget() {
1188 return this.dragWrapper_.isCurrentDragTarget; 912 return this.dragWrapper_.isCurrentDragTarget;
1189 }, 913 },
1190 914
1191 /** 915 /**
1192 * Thunk for dragleave events fired on |tileGrid_|. 916 * Thunk for dragleave events fired on |tileGrid_|.
1193 * @param {Event} e A MouseEvent for the drag. 917 * @param {Event} e A MouseEvent for the drag.
1194 */ 918 */
919 // TODO(xci) drag
1195 doDragLeave: function(e) { 920 doDragLeave: function(e) {
1196 this.cleanupDrag(); 921 this.cleanupDrag();
1197 }, 922 },
1198 923
1199 /** 924 /**
1200 * Performs all actions necessary when a drag enters the tile page. 925 * Performs all actions necessary when a drag enters the tile page.
1201 * @param {Event} e A mouseover event for the drag enter. 926 * @param {Event} e A mouseover event for the drag enter.
1202 */ 927 */
928 // TODO(xci) drag
1203 doDragEnter: function(e) { 929 doDragEnter: function(e) {
1204 // Applies the mask so doppleganger tiles disappear into the fog. 930 // Applies the mask so doppleganger tiles disappear into the fog.
1205 this.updateMask_(); 931 this.updateMask_();
1206 932
1207 this.classList.add('animating-tile-page'); 933 this.classList.add('animating-tile-page');
1208 this.withinPageDrag_ = this.contains(currentlyDraggingTile); 934 this.withinPageDrag_ = this.contains(currentlyDraggingTile);
1209 this.dragItemIndex_ = this.withinPageDrag_ ? 935 this.dragItemIndex_ = this.withinPageDrag_ ?
1210 currentlyDraggingTile.index : this.tileElements_.length; 936 currentlyDraggingTile.index : this.tileElements_.length;
1211 this.currentDropIndex_ = this.dragItemIndex_; 937 this.currentDropIndex_ = this.dragItemIndex_;
1212 938
1213 // The new tile may change the number of rows, hence the top margin 939 // The new tile may change the number of rows, hence the top margin
1214 // will change. 940 // will change.
1215 if (!this.withinPageDrag_) 941 if (!this.withinPageDrag_)
942 // TODO(xci) this function does nothing now!
1216 this.updateTopMargin_(); 943 this.updateTopMargin_();
1217 944
1218 this.doDragOver(e); 945 this.doDragOver(e);
1219 }, 946 },
1220 947
1221 /** 948 /**
1222 * Performs all actions necessary when the user moves the cursor during 949 * Performs all actions necessary when the user moves the cursor during
1223 * a drag over the tile page. 950 * a drag over the tile page.
1224 * @param {Event} e A mouseover event for the drag over. 951 * @param {Event} e A mouseover event for the drag over.
1225 */ 952 */
953 // TODO(xci) drag
1226 doDragOver: function(e) { 954 doDragOver: function(e) {
1227 e.preventDefault(); 955 e.preventDefault();
1228 956
1229 this.setDropEffect(e.dataTransfer); 957 this.setDropEffect(e.dataTransfer);
1230 var newDragIndex = this.getWouldBeIndexForPoint_(e.pageX, e.pageY); 958 var newDragIndex = this.getWouldBeIndexForPoint_(e.pageX, e.pageY);
1231 if (newDragIndex < 0 || newDragIndex >= this.tileElements_.length) 959 if (newDragIndex < 0 || newDragIndex >= this.tileElements_.length)
1232 newDragIndex = this.dragItemIndex_; 960 newDragIndex = this.dragItemIndex_;
1233 this.updateDropIndicator_(newDragIndex); 961 this.updateDropIndicator_(newDragIndex);
1234 }, 962 },
1235 963
1236 /** 964 /**
1237 * Performs all actions necessary when the user completes a drop. 965 * Performs all actions necessary when the user completes a drop.
1238 * @param {Event} e A mouseover event for the drag drop. 966 * @param {Event} e A mouseover event for the drag drop.
1239 */ 967 */
968 // TODO(xci) drag
1240 doDrop: function(e) { 969 doDrop: function(e) {
1241 e.stopPropagation(); 970 e.stopPropagation();
1242 e.preventDefault(); 971 e.preventDefault();
1243 972
1244 var index = this.currentDropIndex_; 973 var index = this.currentDropIndex_;
1245 // Only change data if this was not a 'null drag'. 974 // Only change data if this was not a 'null drag'.
1246 if (!((index == this.dragItemIndex_) && this.withinPageDrag_)) { 975 if (!((index == this.dragItemIndex_) && this.withinPageDrag_)) {
1247 var adjustedIndex = this.currentDropIndex_ + 976 var adjustedIndex = this.currentDropIndex_ +
1248 (index > this.dragItemIndex_ ? 1 : 0); 977 (index > this.dragItemIndex_ ? 1 : 0);
1249 if (this.withinPageDrag_) { 978 if (this.withinPageDrag_) {
(...skipping 16 matching lines...) Expand all
1266 } 995 }
1267 996
1268 this.classList.remove('animating-tile-page'); 997 this.classList.remove('animating-tile-page');
1269 this.cleanupDrag(); 998 this.cleanupDrag();
1270 }, 999 },
1271 1000
1272 /** 1001 /**
1273 * Appends the currently dragged tile to the end of the page. Called 1002 * Appends the currently dragged tile to the end of the page. Called
1274 * from outside the page, e.g. when dropping on a nav dot. 1003 * from outside the page, e.g. when dropping on a nav dot.
1275 */ 1004 */
1005 // TODO(xci) drag
1276 appendDraggingTile: function() { 1006 appendDraggingTile: function() {
1277 var originalPage = currentlyDraggingTile.tilePage; 1007 var originalPage = currentlyDraggingTile.tilePage;
1278 if (originalPage == this) 1008 if (originalPage == this)
1279 return; 1009 return;
1280 1010
1281 this.addDragData(null, this.tileElements_.length); 1011 this.addDragData(null, this.tileElements_.length);
1282 if (originalPage) 1012 if (originalPage)
1283 originalPage.cleanupDrag(); 1013 originalPage.cleanupDrag();
1284 }, 1014 },
1285 1015
1286 /** 1016 /**
1287 * Makes sure all the tiles are in the right place after a drag is over. 1017 * Makes sure all the tiles are in the right place after a drag is over.
1288 */ 1018 */
1019 // TODO(xci) drag
1289 cleanupDrag: function() { 1020 cleanupDrag: function() {
1290 this.repositionTiles_(currentlyDraggingTile); 1021 this.repositionTiles_(currentlyDraggingTile);
1291 // Remove the drag mask. 1022 // Remove the drag mask.
1292 this.updateMask_(); 1023 this.updateMask_();
1293 }, 1024 },
1294 1025
1295 /** 1026 /**
1296 * Reposition all the tiles (possibly ignoring one). 1027 * Reposition all the tiles (possibly ignoring one).
1297 * @param {?Node} ignoreNode An optional node to ignore. 1028 * @param {?Node} ignoreNode An optional node to ignore.
1298 * @private 1029 * @private
1299 */ 1030 */
1031 // TODO(xci) drag
1300 repositionTiles_: function(ignoreNode) { 1032 repositionTiles_: function(ignoreNode) {
1033 /*
1301 for (var i = 0; i < this.tileElements_.length; i++) { 1034 for (var i = 0; i < this.tileElements_.length; i++) {
1302 if (!ignoreNode || ignoreNode !== this.tileElements_[i]) 1035 if (!ignoreNode || ignoreNode !== this.tileElements_[i])
1303 this.positionTile_(i); 1036 ;//this.positionTile_(i); TODO(xci) this function was deleted!
1304 } 1037 }
1038 */
1305 }, 1039 },
1306 1040
1307 /** 1041 /**
1308 * Updates the visual indicator for the drop location for the active drag. 1042 * Updates the visual indicator for the drop location for the active drag.
1309 * @param {Event} e A MouseEvent for the drag. 1043 * @param {Event} e A MouseEvent for the drag.
1310 * @private 1044 * @private
1311 */ 1045 */
1046 // TODO(xci) drag
1312 updateDropIndicator_: function(newDragIndex) { 1047 updateDropIndicator_: function(newDragIndex) {
1313 var oldDragIndex = this.currentDropIndex_; 1048 var oldDragIndex = this.currentDropIndex_;
1314 if (newDragIndex == oldDragIndex) 1049 if (newDragIndex == oldDragIndex)
1315 return; 1050 return;
1316 1051
1317 var repositionStart = Math.min(newDragIndex, oldDragIndex); 1052 var repositionStart = Math.min(newDragIndex, oldDragIndex);
1318 var repositionEnd = Math.max(newDragIndex, oldDragIndex); 1053 var repositionEnd = Math.max(newDragIndex, oldDragIndex);
1319 1054
1320 for (var i = repositionStart; i <= repositionEnd; i++) { 1055 for (var i = repositionStart; i <= repositionEnd; i++) {
1321 if (i == this.dragItemIndex_) 1056 if (i == this.dragItemIndex_)
1322 continue; 1057 continue;
1323 else if (i > this.dragItemIndex_) 1058 else if (i > this.dragItemIndex_)
1324 var adjustment = i <= newDragIndex ? -1 : 0; 1059 var adjustment = i <= newDragIndex ? -1 : 0;
1325 else 1060 else
1326 var adjustment = i >= newDragIndex ? 1 : 0; 1061 var adjustment = i >= newDragIndex ? 1 : 0;
1327 1062
1328 this.positionTile_(i, adjustment); 1063 //this.positionTile_(i, adjustment); TODO(xci) function was deleted!
1329 } 1064 }
1330 this.currentDropIndex_ = newDragIndex; 1065 this.currentDropIndex_ = newDragIndex;
1331 }, 1066 },
1332 1067
1333 /** 1068 /**
1334 * Checks if a page can accept a drag with the given data. 1069 * Checks if a page can accept a drag with the given data.
1335 * @param {Event} e The drag event if the drag object. Implementations will 1070 * @param {Event} e The drag event if the drag object. Implementations will
1336 * likely want to check |e.dataTransfer|. 1071 * likely want to check |e.dataTransfer|.
1337 * @return {boolean} True if this page can handle the drag. 1072 * @return {boolean} True if this page can handle the drag.
1338 */ 1073 */
1074 // TODO(xci) drag
1339 shouldAcceptDrag: function(e) { 1075 shouldAcceptDrag: function(e) {
1340 return false; 1076 return false;
1341 }, 1077 },
1342 1078
1343 /** 1079 /**
1344 * Called to accept a drag drop. Will not be called for in-page drops. 1080 * Called to accept a drag drop. Will not be called for in-page drops.
1345 * @param {Object} dataTransfer The data transfer object that holds the drop 1081 * @param {Object} dataTransfer The data transfer object that holds the drop
1346 * data. This should only be used if currentlyDraggingTile is null. 1082 * data. This should only be used if currentlyDraggingTile is null.
1347 * @param {number} index The tile index at which the drop occurred. 1083 * @param {number} index The tile index at which the drop occurred.
1348 */ 1084 */
1085 // TODO(xci) drag
1349 addDragData: function(dataTransfer, index) { 1086 addDragData: function(dataTransfer, index) {
1350 assert(false); 1087 assert(false);
1351 }, 1088 },
1352 1089
1353 /** 1090 /**
1354 * Called when a tile has been moved (via dragging). Override this to make 1091 * Called when a tile has been moved (via dragging). Override this to make
1355 * backend updates. 1092 * backend updates.
1356 * @param {Node} draggedTile The tile that was dropped. 1093 * @param {Node} draggedTile The tile that was dropped.
1357 * @param {number} prevIndex The previous index of the tile. 1094 * @param {number} prevIndex The previous index of the tile.
1358 */ 1095 */
1359 tileMoved: function(draggedTile, prevIndex) { 1096 tileMoved: function(draggedTile, prevIndex) {
1360 }, 1097 },
1361 1098
1362 /** 1099 /**
1363 * Sets the drop effect on |dataTransfer| to the desired value (e.g. 1100 * Sets the drop effect on |dataTransfer| to the desired value (e.g.
1364 * 'copy'). 1101 * 'copy').
1365 * @param {Object} dataTransfer The drag event dataTransfer object. 1102 * @param {Object} dataTransfer The drag event dataTransfer object.
1366 */ 1103 */
1104 // TODO(xci) drag
1367 setDropEffect: function(dataTransfer) { 1105 setDropEffect: function(dataTransfer) {
1368 assert(false); 1106 assert(false);
1369 }, 1107 },
1108
1109
1110 // #########################################################################
1111 // XCI - Extended Chrome Instant
1112 // #########################################################################
1113
1114
1115 // properties
1116 // -------------------------------------------------------------------------
1117 colCount: 5,
1118 rowCount: 2,
1119
1120 numOfVisibleRows: 0,
1121 animatingColCount: 5, // TODO how to handle initialization of this value?
1122
1123 // TODO move to layout?
1124 pageOffset: 0,
1125
1126 appendTile: function(tile) {
1127 this.tileElements_.push(tile);
1128 this.renderGrid_();
1129 },
1130
1131 // TODO(cxi): if this is not being used, delete.
1132 addTileAt: function(tile, index) {
1133 this.tileElements_.splice(index, 0, tile);
1134 this.appendTile(tile);
1135 },
1136
1137 // internal helpers
1138 // -------------------------------------------------------------------------
1139
1140 // TODO move to layout?
1141 getNumOfVisibleRows_: function() {
1142 return this.numOfVisibleRows;
1143 },
1144
1145 getTileRequiredWidth_: function() {
1146 var grid = this.gridValues_;
1147 return grid.tileWidth + 2 * grid.tileBorderWidth + grid.tileHorMargin;
1148 },
1149
1150 getColCountForWidth_: function(width) {
1151 var availableWidth = width + this.gridValues_.tileHorMargin;
1152 var requiredWidth = this.getTileRequiredWidth_();
1153 var colCount = Math.floor(availableWidth / requiredWidth);
1154 return colCount;
1155 },
1156
1157 getWidthForColCount_: function(colCount) {
1158 var requiredWidth = this.getTileRequiredWidth_();
1159 var width = colCount * requiredWidth - this.gridValues_.tileHorMargin;
1160 return width;
1161 },
1162
1163 getBottomPanelWidth_: function() {
1164 var windowWidth = cr.doc.documentElement.clientWidth;
1165 var width;
1166 // TODO(xci): add contants?
1167 if (windowWidth >= 948) {
1168 width = 748;
1169 } else if (windowWidth >= 500) {
1170 width = windowWidth - 2 * this.gridValues_.bottomPanelHorMargin;
1171 } else if (windowWidth >= 300) {
1172 // TODO(pedrosimonetti): check math and document
1173 width = Math.floor(((windowWidth - 300) / 200) * 100 + 200);
1174 } else {
1175 width = 200;
1176 }
1177 return width;
1178 },
1179
1180 getAvailableColCount_: function() {
1181 return this.getColCountForWidth_(this.getBottomPanelWidth_());
1182 },
1183
1184 // rendering
1185 // -------------------------------------------------------------------------
1186
1187 // TODO(pedrosimonetti): document
1188 renderGrid_: function(colCount) {
1189 colCount = colCount || this.colCount;
1190
1191 var tileGridContent = this.tileGridContent_;
1192
1193 var tileElements = this.tileElements_;
1194
1195 var tileCount = tileElements.length;
1196 var rowCount = Math.ceil(tileCount / colCount);
1197
1198 var tileRows = tileGridContent.getElementsByClassName('tile-row');
1199 var tileRow;
1200 var tileRowTiles;
1201 var tileCell;
1202 var tileElement;
1203 var maxColCount;
1204
1205 var numOfVisibleRows = this.getNumOfVisibleRows_();
1206 var pageOffset = this.pageOffset;
1207
1208 for (var tile = 0, row = 0; row < rowCount; row++) {
1209 tileRow = tileRows[row];
1210
1211 // Create tile row if there's no one yet.
1212 if (!tileRow) {
1213 tileRow = cr.doc.createElement('div');
1214 tileRow.className = 'tile-row tile-row-' + row;// TODO do we need id?
1215 tileGridContent.appendChild(tileRow);
1216 }
1217
1218 // Adjust row visibility.
1219 var rowVisible = (row >= pageOffset &&
1220 row <= (pageOffset + numOfVisibleRows - 1));
1221 this.showTileRow_(tileRow, rowVisible);
1222
1223 // The tiles inside the current row.
1224 tileRowTiles = tileRow.childNodes;
1225
1226 // Remove excessive columns from a particular tile row.
1227 maxColCount = Math.min(colCount, tileCount - tile);
1228 while (tileRowTiles.length > maxColCount) {
1229 tileRow.removeChild(tileRow.lastElementChild);
1230 }
1231
1232 // For each column in the current row.
1233 for (var col = 0; col < colCount; col++, tile++) {
1234 if (tileRowTiles[col]) {
1235 tileCell = tileRowTiles[col];
1236 } else {
1237 var span = cr.doc.createElement('span');
1238 tileCell = new TileCell(span, this.gridValues_);
1239 }
1240
1241 // Reset column class.
1242 this.resetTileCol_(tileCell, col);
1243
1244 // Render Tiles.
1245 if (tile < tileCount) {
1246 tileCell.classList.remove('filler');
1247 tileElement = tileElements[tile];
1248 if (tileCell.firstChild) {
1249 if (tileElement != tileCell.firstChild) {
1250 tileCell.replaceChild(tileElement, tileCell.firstChild);
1251 }
1252 } else {
1253 tileCell.appendChild(tileElement);
1254 }
1255 } else {
1256 // TODO(xci): check how to solve the problem with filler + animation
1257 if (!tileCell.classList.contains('filler')) {
1258 tileCell.classList.add('filler');
1259 tileElement = cr.doc.createElement('span');
1260 if (tileCell.firstChild)
1261 tileCell.replaceChild(tileElement, tileCell.firstChild);
1262 else
1263 tileCell.appendChild(tileElement);
1264 }
1265 }
1266
1267 if (!tileRowTiles[col]) {
1268 tileRow.appendChild(tileCell);
1269 }
1270 }
1271 }
1272
1273 // Remove excessive tile rows from the tile grid.
1274 while (tileRows.length > rowCount) {
1275 tileGridContent.removeChild(tileGridContent.lastElementChild);
1276 }
1277
1278 this.colCount = colCount;
1279 this.rowCount = rowCount;
1280 },
1281
1282 layout_: function() {
1283 var pageList = $('page-list');
1284 var panel = this.content_;
1285 var menu = $('page-list-menu');
1286 var tileGrid = this.tileGrid_;
1287 var tileGridContent = this.tileGridContent_;
1288 var tileRows = tileGridContent.getElementsByClassName('tile-row');
1289
1290 tileGridContent.classList.add('animate-tile');
1291
1292 var bottomPanelWidth = this.getBottomPanelWidth_();
1293 var colCount = this.getColCountForWidth_(bottomPanelWidth);
1294 var lastColCount = this.colCount;
1295 var animatingColCount = this.animatingColCount;
1296
1297 var windowHeight = cr.doc.documentElement.clientHeight;
1298
1299 // TODO better handling of height state
1300 // changeVisibleRows
1301 // TODO need to call paginate when height changes
1302 var numOfVisibleRows = this.numOfVisibleRows;
1303 // TODO(xci) constants?
1304 if (windowHeight > 500 && this.tileElements_.length > 5) {
1305 numOfVisibleRows = 2;
1306 } else {
1307 numOfVisibleRows = 1;
1308 }
1309
1310 if (numOfVisibleRows != this.numOfVisibleRows) {
1311 this.numOfVisibleRows = numOfVisibleRows;
1312 this.paginate_(null, true);
1313 pageList.style.height = (107 * numOfVisibleRows) + 'px';
1314 //tileGrid.style.height = (107 * numOfVisibleRows) + 'px';
1315 }
1316
1317 // changeVisibleCols
1318 if (colCount != animatingColCount) {
1319
1320 var newWidth = this.getWidthForColCount_(colCount);
1321 if (colCount > animatingColCount) {
1322
1323 // TODO actual size check
1324 if (colCount != lastColCount) {
1325 this.renderGrid_(colCount);
1326 }
1327
1328 this.showTileCols_(animatingColCount, false);
1329
1330 // TODO(xci) fix the slowdown debug function and test if we really
1331 // need this closure and if we really need to pass animatingCount.
1332 var self = this;
1333 setTimeout((function(animatingColCount) {
1334 return function() {
1335 self.showTileCols_(animatingColCount, true);
1336 }
1337 })(animatingColCount), 0);
1338
1339 } else {
1340 this.showTileCols_(colCount, false);
1341 }
1342
1343 tileGrid.style.width = newWidth + 'px';
1344 menu.style.width = newWidth + 'px';
1345
1346 // TODO: listen animateEnd
1347 var self = this;
1348 this.onTileGridAnimationEndHandler_ = function() {
1349 if (colCount < lastColCount) {
1350 self.renderGrid_(colCount);
1351 } else {
1352 self.showTileCols_(0, true);
1353 }
1354 };
1355
1356 this.paginate_();
1357 }
1358
1359 panel.style.width = bottomPanelWidth + 'px';
1360
1361 this.animatingColCount = colCount;
1362 },
1363
1364 // animation helpers
1365 // -------------------------------------------------------------------------
1366
1367 // TODO make it local?
1368 showTileRow_: function(row, show) {
1369 row.classList[show ? 'remove' : 'add']('hide-row');
1370 },
1371
1372 // TODO make it local?
1373 showTileCols_: function(col, show) {
1374 var prop = show ? 'remove' : 'add';
1375 var max = 10; // TODO(pedrosimonetti) const?
1376 var tileGridContent = this.tileGridContent_;
1377 for (var i = col; i < max; i++) {
1378 tileGridContent.classList[prop]('hide-col-' + i);
1379 }
1380 },
1381
1382 // TODO make it local?
1383 resetTileCol_: function(tileCell, col) {
1384 var max = 10;
1385 for (var i = 0; i < max; i++) {
1386 if (i != col) {
1387 tileCell.classList.remove('tile-col-' + i);
1388 }
1389 }
1390 tileCell.classList.add('tile-col-' + col);
1391 },
1392
1393 // pagination
1394 // -------------------------------------------------------------------------
1395
1396 paginate_: function(pageOffset, force) {
1397 var numOfVisibleRows = this.getNumOfVisibleRows_();
1398 var pageOffset = typeof pageOffset == 'number' ?
1399 pageOffset : this.pageOffset;
1400
1401 pageOffset = Math.max(0, pageOffset);
1402 pageOffset = Math.min(this.rowCount - numOfVisibleRows, pageOffset);
1403
1404 if (pageOffset != this.pageOffset || force) {
1405 var rows = this.tileGridContent_.getElementsByClassName('tile-row');
1406 for (var i = 0, l = rows.length; i < l; i++) {
1407 var row = rows[i];
1408 if (i >= pageOffset && i <= (pageOffset + numOfVisibleRows - 1)) {
1409 this.showTileRow_(row, true);
1410 } else {
1411 this.showTileRow_(row, false);
1412 }
1413 }
1414
1415 this.pageOffset = pageOffset;
1416 this.tileGridContent_.style.webkitTransform = 'translate3d(0, ' +
1417 (-pageOffset * 106) + 'px, 0)';
1418 }
1419 },
1420
1421 // event handlers
1422 // -------------------------------------------------------------------------
1423
1424 onResize_: function() {
1425 this.layout_();
1426 },
1427
1428 onTileGridAnimationEnd_: function() {
1429 // TODO(xci): figure out how to cleanup each kind animation properly
1430 if (event.target == this.tileGrid_ &&
1431 this.onTileGridAnimationEndHandler_) {
1432 if (this.tileGridContent_.classList.contains('animate-tile')) {
1433 this.onTileGridAnimationEndHandler_();
1434 //this.tileGridContent_.classList.remove('animate-tile')
1435 }
1436 }
1437 },
1438
1439 onKeyUp_: function(e) {
1440 var pageOffset = this.pageOffset;
1441
1442 var keyCode = e.keyCode;
1443 if (keyCode == 40 /* down */) {
1444 pageOffset++;
1445 } else if (keyCode == 38 /* up */) {
1446 pageOffset--;
1447 }
1448
1449 if (pageOffset != this.pageOffset) {
1450 this.paginate_(pageOffset);
1451 }
1452 }
1370 }; 1453 };
1371 1454
1455
1456 var duration = 200;
1457 var defaultDuration = 200;
1458 var animatedProperties = [
1459 '#card-slider-frame .tile-grid',
1460 '#card-slider-frame .tile-grid-content',
1461 '#card-slider-frame .tile-row',
1462 '.animate-tile .tile-cell',
1463 '.debug .animate-tile .tile-cell'
1464 ];
1465
1466 function adjustAnimationTiming(slownessFactor, selectors) {
1467 slownessFactor = slownessFactor || 1;
1468 selectors = selectors || animatedProperties;
1469 duration = defaultDuration * slownessFactor;
1470 for (var i = 0, l = selectors.length; i < l; i++) {
1471 var selector = selectors[i];
1472 var rule = findCSSRule(selector);
1473 if (rule) {
1474 rule.style.webkitTransitionDuration = duration + 'ms';
1475 } else {
1476 throw 'Could not find the CSS rule "' + selector + '"';
1477 }
1478 }
1479 }
1480
1481 function findCSSRule(selectorText) {
1482 var rules = document.styleSheets[0].rules;
1483 for (var i = 0, l = rules.length; i < l; i++) {
1484 var rule = rules[i];
1485 if (rule.selectorText == selectorText)
1486 {
1487 return rule;
1488 }
1489 }
1490 }
1491
1492 function changeSlowFactor(el) {
1493 if (el.value)
1494 adjustAnimationTiming(el.value - 0, animatedProperties);
1495 }
1496
1497 function changeDebugMode(el) {
1498 var prop = el.checked ? 'add' : 'remove';
1499 document.body.classList[prop]('debug');
1500 }
1501
1372 return { 1502 return {
1373 getCurrentlyDraggingTile: getCurrentlyDraggingTile, 1503 // TODO(xci) drag
1374 setCurrentDropEffect: setCurrentDropEffect, 1504 //getCurrentlyDraggingTile2: getCurrentlyDraggingTile,
1375 TilePage: TilePage, 1505 //setCurrentDropEffect2: setCurrentDropEffect,
1506 Tile2: Tile,
1507 TilePage2: TilePage,
1376 }; 1508 };
1377 }); 1509 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698