| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 }); |
| OLD | NEW |