Chromium Code Reviews| 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 * @type {number} | |
| 10 * @const | |
| 11 */ | |
| 12 var TILE_ROW_HEIGHT = 92; | |
| 13 | |
| 14 /** | |
| 15 * @type {number} | |
| 16 * @const | |
| 17 */ | |
| 18 var SCROLL_BAR_WIDTH = 12; | |
| 19 | |
| 8 //---------------------------------------------------------------------------- | 20 //---------------------------------------------------------------------------- |
| 9 // Tile | 21 // Tile |
| 10 //---------------------------------------------------------------------------- | 22 //---------------------------------------------------------------------------- |
| 11 | 23 |
| 12 /** | 24 /** |
| 13 * A virtual Tile class. Each TilePage subclass should have its own Tile | 25 * A virtual Tile class. Each TilePage subclass should have its own Tile |
| 14 * subclass implemented too (e.g. MostVisitedPage contains MostVisited | 26 * subclass implemented too (e.g. MostVisitedPage contains MostVisited |
| 15 * tiles, and MostVisited is a Tile subclass). | 27 * tiles, and MostVisited is a Tile subclass). |
| 16 * @constructor | 28 * @constructor |
| 17 * @param {Object} config TilePage configuration object. | 29 * @param {Object} config TilePage configuration object. |
| (...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 190 | 202 |
| 191 /** | 203 /** |
| 192 * Creates a new TilePage object. This object contains tiles and controls | 204 * Creates a new TilePage object. This object contains tiles and controls |
| 193 * their layout. | 205 * their layout. |
| 194 * @constructor | 206 * @constructor |
| 195 * @extends {HTMLDivElement} | 207 * @extends {HTMLDivElement} |
| 196 */ | 208 */ |
| 197 function TilePage() { | 209 function TilePage() { |
| 198 var el = cr.doc.createElement('div'); | 210 var el = cr.doc.createElement('div'); |
| 199 el.__proto__ = TilePage.prototype; | 211 el.__proto__ = TilePage.prototype; |
| 200 el.initialize(); | |
| 201 | 212 |
| 202 return el; | 213 return el; |
| 203 } | 214 } |
| 204 | 215 |
| 205 TilePage.prototype = { | 216 TilePage.prototype = { |
| 206 __proto__: HTMLDivElement.prototype, | 217 __proto__: HTMLDivElement.prototype, |
| 207 | 218 |
| 208 /** | 219 /** |
| 209 * Reference to the Tile subclass that will be used to create the tiles. | 220 * Reference to the Tile subclass that will be used to create the tiles. |
| 210 * @constructor | 221 * @constructor |
| 211 * @extends {Tile} | 222 * @extends {Tile} |
| 212 */ | 223 */ |
| 213 TileClass: Tile, | 224 TileClass: Tile, |
| 214 | 225 |
| 215 // The config object should be defined by a TilePage subclass if it | 226 // The config object should be defined by a TilePage subclass if it |
| 216 // wants the non-default behavior. | 227 // wants the non-default behavior. |
| 217 config: { | 228 config: { |
| 218 // The width of a cell. | 229 // The width of a cell. |
| 219 cellWidth: 110, | 230 cellWidth: 110, |
| 220 // The start margin of a cell (left or right according to text direction). | 231 // The start margin of a cell (left or right according to text direction). |
| 221 cellMarginStart: 12, | 232 cellMarginStart: 12, |
| 222 // The maximum number of Tiles to be displayed. | 233 // The maximum number of Tiles to be displayed. |
| 223 maxTileCount: 6 | 234 maxTileCount: 6, |
| 235 // Whether the TilePage content will be scrollable. | |
| 236 scrollable: false | |
| 224 }, | 237 }, |
| 225 | 238 |
| 226 /** | 239 /** |
| 227 * Initializes a TilePage. | 240 * Initializes a TilePage. |
| 228 */ | 241 */ |
| 229 initialize: function() { | 242 initialize: function() { |
| 230 this.className = 'tile-page'; | 243 this.className = 'tile-page'; |
| 231 | 244 |
| 232 // The content defines the actual space a page has to display tiles. | 245 // The div that wraps the scrollable element. |
| 246 this.frame_ = this.ownerDocument.createElement('div'); | |
| 247 this.frame_.className = 'tile-page-frame'; | |
| 248 this.appendChild(this.frame_); | |
| 249 | |
| 250 // The content/scrollable element. | |
| 233 this.content_ = this.ownerDocument.createElement('div'); | 251 this.content_ = this.ownerDocument.createElement('div'); |
| 234 this.content_.className = 'tile-page-content'; | 252 this.content_.className = 'tile-page-content'; |
| 235 this.appendChild(this.content_); | 253 this.frame_.appendChild(this.content_); |
| 254 | |
| 255 if (this.config.scrollable) { | |
| 256 this.content_.classList.add('scrollable'); | |
| 257 | |
| 258 // The scrollable shadow top. | |
| 259 this.shadowTop_ = document.createElement('div'); | |
|
Dan Beam
2012/11/29 04:59:55
this.ownerDocument.createElement('div') here and b
pedro (no code reviews)
2012/11/29 08:02:37
Done.
| |
| 260 this.shadowTop_.className = 'scrollable-shadow-top'; | |
| 261 this.content_.appendChild(this.shadowTop_); | |
| 262 | |
| 263 // The scrollable shadow bottom. | |
| 264 this.shadowBottom_ = document.createElement('div'); | |
| 265 this.shadowBottom_.className = 'scrollable-shadow-bottom'; | |
| 266 this.content_.appendChild(this.shadowBottom_); | |
| 267 } | |
| 236 | 268 |
| 237 // The div that defines the tile grid viewport. | 269 // The div that defines the tile grid viewport. |
| 238 this.tileGrid_ = this.ownerDocument.createElement('div'); | 270 this.tileGrid_ = this.ownerDocument.createElement('div'); |
| 239 this.tileGrid_.className = 'tile-grid'; | 271 this.tileGrid_.className = 'tile-grid'; |
| 240 this.content_.appendChild(this.tileGrid_); | 272 this.content_.appendChild(this.tileGrid_); |
| 241 | 273 |
| 242 // The tile grid contents, which can be scrolled. | 274 // The tile grid contents, which can be scrolled. |
| 243 this.tileGridContent_ = this.ownerDocument.createElement('div'); | 275 this.tileGridContent_ = this.ownerDocument.createElement('div'); |
| 244 this.tileGridContent_.className = 'tile-grid-content'; | 276 this.tileGridContent_.className = 'tile-grid-content'; |
| 245 this.tileGrid_.appendChild(this.tileGridContent_); | 277 this.tileGrid_.appendChild(this.tileGridContent_); |
| 246 | 278 |
| 247 // The list of Tile elements which is used to fill the TileGrid cells. | 279 // The list of Tile elements which is used to fill the TileGrid cells. |
| 248 this.tiles_ = []; | 280 this.tiles_ = []; |
| 249 | 281 |
| 250 // TODO(pedrosimonetti): Check duplication of these methods. | 282 // TODO(pedrosimonetti): Check duplication of these methods. |
| 251 this.addEventListener('cardselected', this.handleCardSelection_); | 283 this.addEventListener('cardselected', this.handleCardSelection_); |
| 252 this.addEventListener('carddeselected', this.handleCardDeselection_); | 284 this.addEventListener('carddeselected', this.handleCardDeselection_); |
| 253 | 285 |
| 254 this.tileGrid_.addEventListener('webkitTransitionEnd', | 286 this.tileGrid_.addEventListener('webkitTransitionEnd', |
| 255 this.onTileGridTransitionEnd_.bind(this)); | 287 this.onTileGridTransitionEnd_.bind(this)); |
| 288 | |
| 289 this.content_.addEventListener('scroll', this.onScroll_.bind(this)); | |
| 256 }, | 290 }, |
| 257 | 291 |
| 258 /** | 292 /** |
| 259 * The list of Tile elements. | 293 * The list of Tile elements. |
| 260 * @type {Array<Tile>} | 294 * @type {Array<Tile>} |
| 261 */ | 295 */ |
| 262 get tiles() { | 296 get tiles() { |
| 263 return this.tiles_; | 297 return this.tiles_; |
| 264 }, | 298 }, |
| 265 | 299 |
| (...skipping 225 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 491 var config = this.config; | 525 var config = this.config; |
| 492 return config.cellWidth + config.cellMarginStart; | 526 return config.cellWidth + config.cellMarginStart; |
| 493 }, | 527 }, |
| 494 | 528 |
| 495 /** | 529 /** |
| 496 * Gets the the maximum number of columns that can fit in a given width. | 530 * Gets the the maximum number of columns that can fit in a given width. |
| 497 * @param {number} width The width in pixels. | 531 * @param {number} width The width in pixels. |
| 498 * @private | 532 * @private |
| 499 */ | 533 */ |
| 500 getColCountForWidth_: function(width) { | 534 getColCountForWidth_: function(width) { |
| 501 var availableWidth = width + this.config.cellMarginStart; | 535 var content = this.content_; |
|
Dan Beam
2012/11/29 04:59:55
nit: don't really see the point to this var...
pedro (no code reviews)
2012/11/29 08:02:37
Var removed.
| |
| 536 var scrollBarIsVisible = this.config.scrollable && | |
| 537 content.scrollHeight > content.clientHeight; | |
| 538 var scrollBarWidth = scrollBarIsVisible ? SCROLL_BAR_WIDTH : 0; | |
| 539 var availableWidth = width + this.config.cellMarginStart - scrollBarWidth; | |
| 540 | |
| 502 var requiredWidth = this.getTileRequiredWidth_(); | 541 var requiredWidth = this.getTileRequiredWidth_(); |
| 503 var colCount = Math.floor(availableWidth / requiredWidth); | 542 var colCount = Math.floor(availableWidth / requiredWidth); |
| 504 return colCount; | 543 return colCount; |
| 505 }, | 544 }, |
| 506 | 545 |
| 507 /** | 546 /** |
| 508 * Gets the width for a given number of columns. | 547 * Gets the width for a given number of columns. |
| 509 * @param {number} colCount The number of columns. | 548 * @param {number} colCount The number of columns. |
| 510 * @private | 549 * @private |
| 511 */ | 550 */ |
| 512 getWidthForColCount_: function(colCount) { | 551 getWidthForColCount_: function(colCount) { |
| 513 var requiredWidth = this.getTileRequiredWidth_(); | 552 var requiredWidth = this.getTileRequiredWidth_(); |
| 514 var width = colCount * requiredWidth - this.config.cellMarginStart; | 553 var width = colCount * requiredWidth - this.config.cellMarginStart; |
| 515 return width; | 554 return width; |
| 516 }, | 555 }, |
| 517 | 556 |
| 518 /** | 557 /** |
| 519 * Returns the position of the tile at |index|. | 558 * Returns the position of the tile at |index|. |
| 520 * @param {number} index Tile index. | 559 * @param {number} index Tile index. |
| 521 * @private | 560 * @private |
| 522 * @return {!{top: number, left: number}} Position. | 561 * @return {!{top: number, left: number}} Position. |
| 523 */ | 562 */ |
| 524 getTilePosition_: function(index) { | 563 getTilePosition_: function(index) { |
| 525 var colCount = this.colCount_; | 564 var colCount = this.colCount_; |
| 565 var row = Math.floor(index / colCount); | |
| 526 var col = index % colCount; | 566 var col = index % colCount; |
| 527 if (isRTL()) | 567 if (isRTL()) |
| 528 col = colCount - col - 1; | 568 col = colCount - col - 1; |
| 529 var config = this.config; | 569 var config = this.config; |
| 570 var top = TILE_ROW_HEIGHT * row; | |
| 530 var left = col * (config.cellWidth + config.cellMarginStart); | 571 var left = col * (config.cellWidth + config.cellMarginStart); |
| 531 return {top: 0, left: left}; | 572 return {top: top, left: left}; |
| 532 }, | 573 }, |
| 533 | 574 |
| 534 // rendering | 575 // rendering |
| 535 // ------------------------------------------------------------------------- | 576 // ------------------------------------------------------------------------- |
| 536 | 577 |
| 537 /** | 578 /** |
| 538 * Renders the tile grid, and the individual tiles. Rendering the grid | 579 * Renders the tile grid, and the individual tiles. Rendering the grid |
| 539 * consists of adding/removing tile rows and tile cells according to the | 580 * consists of adding/removing tile rows and tile cells according to the |
| 540 * specified size (defined by the number of columns in the grid). While | 581 * specified size (defined by the number of columns in the grid). While |
| 541 * rendering the grid, the tiles are rendered in order in their respective | 582 * rendering the grid, the tiles are rendered in order in their respective |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 563 for (var tile = 0, row = 0; row < rowCount; row++) { | 604 for (var tile = 0, row = 0; row < rowCount; row++) { |
| 564 var tileRow = tileRows[row]; | 605 var tileRow = tileRows[row]; |
| 565 | 606 |
| 566 // Create tile row if there's no one yet. | 607 // Create tile row if there's no one yet. |
| 567 if (!tileRow) { | 608 if (!tileRow) { |
| 568 tileRow = cr.doc.createElement('div'); | 609 tileRow = cr.doc.createElement('div'); |
| 569 tileRow.className = 'tile-row'; | 610 tileRow.className = 'tile-row'; |
| 570 tileGridContent.appendChild(tileRow); | 611 tileGridContent.appendChild(tileRow); |
| 571 } | 612 } |
| 572 | 613 |
| 573 // Adjust row visibility. | |
| 574 var rowVisible = row >= pageOffset && | |
| 575 row <= (pageOffset + numOfVisibleRows - 1); | |
| 576 this.showTileRow_(tileRow, rowVisible); | |
| 577 | |
| 578 // The tiles inside the current row. | 614 // The tiles inside the current row. |
| 579 var tileRowTiles = tileRow.childNodes; | 615 var tileRowTiles = tileRow.childNodes; |
| 580 | 616 |
| 581 // Remove excessive columns from a particular tile row. | 617 // Remove excessive columns from a particular tile row. |
| 582 var maxColCount = Math.min(colCount, tileCount - tile); | 618 var maxColCount = Math.min(colCount, tileCount - tile); |
| 583 maxColCount = Math.max(0, maxColCount); | 619 maxColCount = Math.max(0, maxColCount); |
| 584 while (tileRowTiles.length > maxColCount) { | 620 while (tileRowTiles.length > maxColCount) { |
| 585 tileRow.removeChild(tileRow.lastElementChild); | 621 tileRow.removeChild(tileRow.lastElementChild); |
| 586 } | 622 } |
| 587 | 623 |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 619 } | 655 } |
| 620 } | 656 } |
| 621 | 657 |
| 622 // Remove excessive tile rows from the tile grid. | 658 // Remove excessive tile rows from the tile grid. |
| 623 while (tileRows.length > rowCount) { | 659 while (tileRows.length > rowCount) { |
| 624 tileGridContent.removeChild(tileGridContent.lastElementChild); | 660 tileGridContent.removeChild(tileGridContent.lastElementChild); |
| 625 } | 661 } |
| 626 | 662 |
| 627 this.colCount_ = colCount; | 663 this.colCount_ = colCount; |
| 628 this.rowCount_ = rowCount; | 664 this.rowCount_ = rowCount; |
| 665 | |
| 666 if (this.onScroll_) | |
|
Dan Beam
2012/11/29 04:59:55
nit: like I already mentioned, this should be prot
pedro (no code reviews)
2012/11/29 08:02:37
Ack.
| |
| 667 this.onScroll_(); | |
| 629 }, | 668 }, |
| 630 | 669 |
| 631 // layout | 670 // layout |
| 632 // ------------------------------------------------------------------------- | 671 // ------------------------------------------------------------------------- |
| 633 | 672 |
| 634 /** | 673 /** |
| 635 * Calculates the layout of the tile page according to the current Bottom | 674 * Calculates the layout of the tile page according to the current Bottom |
| 636 * Panel's size. This method will resize the containers of the tile page, | 675 * Panel's size. This method will resize the containers of the tile page, |
| 637 * and re-render the grid when its dimension changes (number of columns or | 676 * and re-render the grid when its dimension changes (number of columns or |
| 638 * visible rows changes). This method also sets the private properties | 677 * visible rows changes). This method also sets the private properties |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 674 // TODO(pedrosimonetti): move to handler below. | 713 // TODO(pedrosimonetti): move to handler below. |
| 675 var self = this; | 714 var self = this; |
| 676 this.onTileGridTransitionEndHandler_ = function() { | 715 this.onTileGridTransitionEndHandler_ = function() { |
| 677 if (colCount < lastColCount) | 716 if (colCount < lastColCount) |
| 678 self.renderGrid_(colCount); | 717 self.renderGrid_(colCount); |
| 679 else | 718 else |
| 680 self.showTileCols_(0, true); | 719 self.showTileCols_(0, true); |
| 681 }; | 720 }; |
| 682 } | 721 } |
| 683 | 722 |
| 684 this.content_.style.width = contentWidth + 'px'; | 723 this.animatingColCount_ = colCount; |
| 685 | 724 |
| 686 this.animatingColCount_ = colCount; | 725 this.frame_.style.width = contentWidth + 'px'; |
| 726 if (this.onScroll_) | |
| 727 this.onScroll_(); | |
| 687 }, | 728 }, |
| 688 | 729 |
| 689 // tile repositioning animation | 730 // tile repositioning animation |
| 690 // ------------------------------------------------------------------------- | 731 // ------------------------------------------------------------------------- |
| 691 | 732 |
| 692 /** | 733 /** |
| 693 * Tile repositioning state. | 734 * Tile repositioning state. |
| 694 * @type {{index: number, isRemoving: number}} | 735 * @type {{index: number, isRemoving: number}} |
| 695 */ | 736 */ |
| 696 tileRepositioningState_: null, | 737 tileRepositioningState_: null, |
| (...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 945 var positionDiff = isRemoving ? -1 : 1; | 986 var positionDiff = isRemoving ? -1 : 1; |
| 946 for (var i = startIndex; i < endIndex; i++) { | 987 for (var i = startIndex; i < endIndex; i++) { |
| 947 var tile = tiles[i]; | 988 var tile = tiles[i]; |
| 948 this.resetTilePosition_(tile); | 989 this.resetTilePosition_(tile); |
| 949 tile.style.zIndex = ''; | 990 tile.style.zIndex = ''; |
| 950 tileCells[i + positionDiff].assign(tile); | 991 tileCells[i + positionDiff].assign(tile); |
| 951 } | 992 } |
| 952 }, | 993 }, |
| 953 | 994 |
| 954 /** | 995 /** |
| 955 * Animates the display of a row. TODO(pedrosimonetti): Make it local? | 996 * Animates the display of columns. |
| 956 * @param {HTMLElement} row The row element. | |
| 957 * @param {boolean} show Whether or not to show the row. | |
| 958 */ | |
| 959 showTileRow_: function(row, show) { | |
| 960 row.classList[show ? 'remove' : 'add']('hide-row'); | |
| 961 }, | |
| 962 | |
| 963 /** | |
| 964 * Animates the display of columns. TODO(pedrosimonetti): Make it local? | |
| 965 * @param {number} col The column number. | 997 * @param {number} col The column number. |
| 966 * @param {boolean} show Whether or not to show the row. | 998 * @param {boolean} show Whether or not to show the row. |
| 967 */ | 999 */ |
| 968 showTileCols_: function(col, show) { | 1000 showTileCols_: function(col, show) { |
| 969 var prop = show ? 'remove' : 'add'; | 1001 var prop = show ? 'remove' : 'add'; |
| 970 var max = 10; // TODO(pedrosimonetti): Add const? | 1002 var max = 10; // TODO(pedrosimonetti): Add const? |
| 971 var tileGridContent = this.tileGridContent_; | 1003 var tileGridContent = this.tileGridContent_; |
| 972 for (var i = col; i < max; i++) { | 1004 for (var i = col; i < max; i++) { |
| 973 tileGridContent.classList[prop]('hide-col-' + i); | 1005 tileGridContent.classList[prop]('hide-col-' + i); |
| 974 } | 1006 } |
| 975 }, | 1007 }, |
| 976 | 1008 |
| 977 // event handlers | 1009 // event handlers |
| 978 // ------------------------------------------------------------------------- | 1010 // ------------------------------------------------------------------------- |
| 979 | 1011 |
| 980 /** | 1012 /** |
| 1013 * Handles the scroll event. | |
| 1014 * @private | |
|
Dan Beam
2012/11/29 04:59:55
definitely make this @protected, this would fail t
pedro (no code reviews)
2012/11/29 08:02:37
Done.
| |
| 1015 */ | |
| 1016 onScroll_: function(e) { | |
|
Dan Beam
2012/11/29 04:59:55
I prefer:
BaseClass.prototype = {
/** @prot
pedro (no code reviews)
2012/11/29 08:02:37
Done.
| |
| 1017 if (!this.selected) | |
| 1018 return; | |
| 1019 | |
| 1020 // If the TilePage is scrollable, then the opacity of shadow top and | |
| 1021 // bottom must adjusted, indicating when there's an overflow content. | |
| 1022 if (this.config.scrollable) { | |
| 1023 var content = this.content_; | |
| 1024 var maxGap = 16; | |
| 1025 var topGap = Math.min(maxGap, content.scrollTop); | |
| 1026 var bottomGap = Math.min(maxGap, content.scrollHeight - | |
| 1027 content.scrollTop - content.clientHeight); | |
| 1028 | |
| 1029 this.shadowTop_.style.opacity = topGap / 16; | |
| 1030 this.shadowBottom_.style.opacity = bottomGap / 16; | |
| 1031 } | |
| 1032 }, | |
| 1033 | |
| 1034 /** | |
| 981 * Handles the end of the horizontal tile grid transition. | 1035 * Handles the end of the horizontal tile grid transition. |
| 982 * @param {Event} e The tile grid webkitTransitionEnd event. | 1036 * @param {Event} e The tile grid webkitTransitionEnd event. |
| 983 */ | 1037 */ |
| 984 onTileGridTransitionEnd_: function(e) { | 1038 onTileGridTransitionEnd_: function(e) { |
| 985 if (!this.selected) | 1039 if (!this.selected) |
| 986 return; | 1040 return; |
| 987 | 1041 |
| 988 // We should remove the classes that control transitions when the | 1042 // We should remove the classes that control transitions when the |
| 989 // transition ends so when the text is resized (Ctrl + '+'), no other | 1043 // transition ends so when the text is resized (Ctrl + '+'), no other |
| 990 // transition should happen except those defined in the specification. | 1044 // transition should happen except those defined in the specification. |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1043 tile.scrollTop; | 1097 tile.scrollTop; |
| 1044 tile.classList.remove(className); | 1098 tile.classList.remove(className); |
| 1045 } | 1099 } |
| 1046 } | 1100 } |
| 1047 | 1101 |
| 1048 return { | 1102 return { |
| 1049 Tile: Tile, | 1103 Tile: Tile, |
| 1050 TilePage: TilePage, | 1104 TilePage: TilePage, |
| 1051 }; | 1105 }; |
| 1052 }); | 1106 }); |
| OLD | NEW |