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 /** | 5 /** |
| 6 * @param {Element} container Content container. | 6 * @param {Element} container Content container. |
| 7 * @param {cr.ui.ArrayDataModel} dataModel Data model. | 7 * @param {cr.ui.ArrayDataModel} dataModel Data model. |
| 8 * @param {cr.ui.ListSelectionModel} selectionModel Selection model. | 8 * @param {cr.ui.ListSelectionModel} selectionModel Selection model. |
| 9 * @param {MetadataCache} metadataCache Metadata cache. | 9 * @param {MetadataCache} metadataCache Metadata cache. |
| 10 * @param {function} toggleMode Function to switch to the Slide mode. | 10 * @param {function} toggleMode Function to switch to the Slide mode. |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 82 * Inherit from HTMLDivElement. | 82 * Inherit from HTMLDivElement. |
| 83 */ | 83 */ |
| 84 Mosaic.prototype.__proto__ = HTMLDivElement.prototype; | 84 Mosaic.prototype.__proto__ = HTMLDivElement.prototype; |
| 85 | 85 |
| 86 /** | 86 /** |
| 87 * Default layout delay in ms. | 87 * Default layout delay in ms. |
| 88 */ | 88 */ |
| 89 Mosaic.LAYOUT_DELAY = 200; | 89 Mosaic.LAYOUT_DELAY = 200; |
| 90 | 90 |
| 91 /** | 91 /** |
| 92 * Smooth scroll animation duration when scrolling using keyboard or | |
| 93 * clicking on a partly visible tile. In ms. | |
| 94 */ | |
| 95 Mosaic.ANIMATED_SCROLL_DURATION = 500; | |
| 96 | |
| 97 /** | |
| 92 * Decorate a Mosaic instance. | 98 * Decorate a Mosaic instance. |
| 93 * | 99 * |
| 94 * @param {Mosaic} self Self pointer. | 100 * @param {Mosaic} self Self pointer. |
| 95 * @param {cr.ui.ArrayDataModel} dataModel Data model. | 101 * @param {cr.ui.ArrayDataModel} dataModel Data model. |
| 96 * @param {cr.ui.ListSelectionModel} selectionModel Selection model. | 102 * @param {cr.ui.ListSelectionModel} selectionModel Selection model. |
| 97 * @param {MetadataCache} metadataCache Metadata cache. | 103 * @param {MetadataCache} metadataCache Metadata cache. |
| 98 * @param {function(string)} onThumbnailError Thumbnail load error handler. | 104 * @param {function(string)} onThumbnailError Thumbnail load error handler. |
| 99 */ | 105 */ |
| 100 Mosaic.decorate = function(self, dataModel, selectionModel, metadataCache, | 106 Mosaic.decorate = function(self, dataModel, selectionModel, metadataCache, |
| 101 onThumbnailError) { | 107 onThumbnailError) { |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 163 | 169 |
| 164 this.selectionModel_.addEventListener('change', this.onSelection_.bind(this)); | 170 this.selectionModel_.addEventListener('change', this.onSelection_.bind(this)); |
| 165 this.selectionModel_.addEventListener('leadIndexChange', | 171 this.selectionModel_.addEventListener('leadIndexChange', |
| 166 this.onLeadChange_.bind(this)); | 172 this.onLeadChange_.bind(this)); |
| 167 | 173 |
| 168 this.dataModel_.addEventListener('splice', this.onSplice_.bind(this)); | 174 this.dataModel_.addEventListener('splice', this.onSplice_.bind(this)); |
| 169 this.dataModel_.addEventListener('content', this.onContentChange_.bind(this)); | 175 this.dataModel_.addEventListener('content', this.onContentChange_.bind(this)); |
| 170 }; | 176 }; |
| 171 | 177 |
| 172 /** | 178 /** |
| 179 * Smoothly scrolls the container to the specified position using | |
| 180 * f(x) = sqrt(x) speed function normalized to animation duration. | |
| 181 * @param {number} targetPosition Horizontal scroll position in pixels. | |
| 182 */ | |
| 183 Mosaic.prototype.animatedScrollTo = function(targetPosition) { | |
| 184 if (this.scrollTimer_) | |
| 185 clearInterval(this.scrollTimer_); | |
|
yoshiki
2013/02/12 01:30:52
Assign null or undefined after clearing the interv
mtomasz
2013/02/12 04:19:21
Done.
| |
| 186 | |
| 187 // Mouse move events are fired without touching the mouse because of scrolling | |
| 188 // the container. Therefore, these events have to be suppressed. | |
| 189 this.suppressHovering_ = true; | |
| 190 | |
| 191 // Calculates integral area from t1 to t2 of f(x) = sqrt(x) dx. | |
| 192 var integral = function(t1, t2) { | |
| 193 return 2.0 / 3.0 * Math.pow(t2, 3.0 / 2.0) - | |
| 194 2.0 / 3.0 * Math.pow(t1, 3.0 / 2.0); | |
| 195 }; | |
| 196 | |
| 197 var delta = targetPosition - this.scrollLeft; | |
| 198 var factor = delta / integral(0, Mosaic.ANIMATED_SCROLL_DURATION); | |
| 199 var startTime = Date.now(); | |
| 200 var lastPosition = 0; | |
| 201 | |
| 202 this.scrollTimer_ = setInterval(function() { | |
|
yoshiki
2013/02/12 01:30:52
It is better to use "requestAnimationFrame" on ani
mtomasz
2013/02/12 04:19:21
This is great, I didn't know about it! Done.
| |
| 203 var position = Date.now() - startTime; | |
| 204 var step = factor * | |
| 205 integral(Math.max(0, Mosaic.ANIMATED_SCROLL_DURATION - position), | |
| 206 Math.max(0, Mosaic.ANIMATED_SCROLL_DURATION - lastPosition)); | |
| 207 | |
| 208 var lastScrollLeft = this.scrollLeft; | |
| 209 console.log(step); | |
|
yoshiki
2013/02/12 01:30:52
Remove?
mtomasz
2013/02/12 04:19:21
Done.
| |
| 210 this.scrollLeft += step; | |
| 211 | |
| 212 if (lastScrollLeft == this.scrollLeft) { | |
|
yoshiki
2013/02/12 01:30:52
We can replace this condition with just "step == 0
mtomasz
2013/02/12 04:19:21
Nope, this didn't work when reaching edges, eg. wh
| |
| 213 clearInterval(this.scrollTimer_); | |
| 214 this.scrollTimer_ = null; | |
| 215 | |
| 216 // Release the hovering lock after a safe delay to avoid hovering | |
| 217 // a tile because of altering |this.scrollLeft|. | |
| 218 setTimeout(function() { | |
| 219 if (!this.scrollTimer_) | |
| 220 this.suppressHovering_ = false; | |
| 221 }.bind(this), 100); | |
| 222 } | |
| 223 | |
| 224 lastPosition = position; | |
| 225 }.bind(this), 10); | |
| 226 }; | |
| 227 | |
| 228 /** | |
| 173 * @return {Mosaic.Tile} Selected tile or undefined if no selection. | 229 * @return {Mosaic.Tile} Selected tile or undefined if no selection. |
| 174 */ | 230 */ |
| 175 Mosaic.prototype.getSelectedTile = function() { | 231 Mosaic.prototype.getSelectedTile = function() { |
| 176 return this.tiles_ && this.tiles_[this.selectionModel_.selectedIndex]; | 232 return this.tiles_ && this.tiles_[this.selectionModel_.selectedIndex]; |
| 177 }; | 233 }; |
| 178 | 234 |
| 179 /** | 235 /** |
| 180 * @param {number} index Tile index. | 236 * @param {number} index Tile index. |
| 181 * @return {Rect} Tile's image rectangle. | 237 * @return {Rect} Tile's image rectangle. |
| 182 */ | 238 */ |
| 183 Mosaic.prototype.getTileRect = function(index) { | 239 Mosaic.prototype.getTileRect = function(index) { |
| 184 var tile = this.tiles_[index]; | 240 var tile = this.tiles_[index]; |
| 185 return tile && tile.getImageRect(); | 241 return tile && tile.getImageRect(); |
| 186 }; | 242 }; |
| 187 | 243 |
| 188 /** | 244 /** |
| 189 * @param {number} index Tile index. | 245 * @param {number} index Tile index. |
| 190 * Scroll the given tile into the viewport. | 246 * Scroll the given tile into the viewport. |
| 191 */ | 247 */ |
| 192 Mosaic.prototype.scrollIntoView = function(index) { | 248 Mosaic.prototype.scrollIntoView = function(index) { |
| 193 var tile = this.tiles_[index]; | 249 var tile = this.tiles_[index]; |
| 194 if (tile) tile.scrollIntoView(); | 250 if (tile) tile.scrollIntoView(); |
|
yoshiki
2013/02/12 01:30:52
Add the boolean argument |opt_animated|.
mtomasz
2013/02/12 04:19:21
Can you clarify? This argument is optional.
yoshiki
2013/02/12 04:49:10
Sorry, it was my mistake. It's optional.
On 2013/
| |
| 195 }; | 251 }; |
| 196 | 252 |
| 197 /** | 253 /** |
| 198 * Load multiple tiles. | 254 * Load multiple tiles. |
| 199 * | 255 * |
| 200 * @param {Array.<Mosaic.Tile>} tiles Array of tiles. | 256 * @param {Array.<Mosaic.Tile>} tiles Array of tiles. |
| 201 * @param {function} opt_callback Completion callback. | 257 * @param {function} opt_callback Completion callback. |
| 202 * @private | 258 * @private |
| 203 */ | 259 */ |
| 204 Mosaic.prototype.loadTiles_ = function(tiles, opt_callback) { | 260 Mosaic.prototype.loadTiles_ = function(tiles, opt_callback) { |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 305 }; | 361 }; |
| 306 | 362 |
| 307 /** | 363 /** |
| 308 * Mouse event handler. | 364 * Mouse event handler. |
| 309 * | 365 * |
| 310 * @param {Event} event Event. | 366 * @param {Event} event Event. |
| 311 * @private | 367 * @private |
| 312 */ | 368 */ |
| 313 Mosaic.prototype.onMouseEvent_ = function(event) { | 369 Mosaic.prototype.onMouseEvent_ = function(event) { |
| 314 // Navigating with mouse, enable hover state. | 370 // Navigating with mouse, enable hover state. |
| 315 this.classList.add('hover-visible'); | 371 if (!this.suppressHovering_) |
| 372 this.classList.add('hover-visible'); | |
| 316 | 373 |
| 317 if (event.type == 'mousemove') | 374 if (event.type == 'mousemove') |
| 318 return; | 375 return; |
| 319 | 376 |
| 320 var index = -1; | 377 var index = -1; |
| 321 for (var target = event.target; | 378 for (var target = event.target; |
| 322 target && (target != this); | 379 target && (target != this); |
| 323 target = target.parentNode) { | 380 target = target.parentNode) { |
| 324 if (target.classList.contains('mosaic-tile')) { | 381 if (target.classList.contains('mosaic-tile')) { |
| 325 index = this.dataModel_.indexOf(target.getItem()); | 382 index = this.dataModel_.indexOf(target.getItem()); |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 346 /** | 403 /** |
| 347 * Lead item change handler. | 404 * Lead item change handler. |
| 348 * | 405 * |
| 349 * @param {Event} event Event. | 406 * @param {Event} event Event. |
| 350 * @private | 407 * @private |
| 351 */ | 408 */ |
| 352 Mosaic.prototype.onLeadChange_ = function(event) { | 409 Mosaic.prototype.onLeadChange_ = function(event) { |
| 353 var index = event.newValue; | 410 var index = event.newValue; |
| 354 if (index >= 0) { | 411 if (index >= 0) { |
| 355 var tile = this.tiles_[index]; | 412 var tile = this.tiles_[index]; |
| 356 if (tile) tile.scrollIntoView(); | 413 if (tile) tile.scrollIntoView(); |
|
yoshiki
2013/02/12 01:30:52
Add the boolean argument |opt_animated|.
mtomasz
2013/02/12 04:19:21
As above, please clarify.
| |
| 357 } | 414 } |
| 358 }; | 415 }; |
| 359 | 416 |
| 360 /** | 417 /** |
| 361 * Splice event handler. | 418 * Splice event handler. |
| 362 * | 419 * |
| 363 * @param {Event} event Event. | 420 * @param {Event} event Event. |
| 364 * @private | 421 * @private |
| 365 */ | 422 */ |
| 366 Mosaic.prototype.onSplice_ = function(event) { | 423 Mosaic.prototype.onSplice_ = function(event) { |
| (...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 563 */ | 620 */ |
| 564 Mosaic.Layout.PADDING_BOTTOM = 70; | 621 Mosaic.Layout.PADDING_BOTTOM = 70; |
| 565 | 622 |
| 566 /** | 623 /** |
| 567 * Horizontal and vertical spacing between images. Should be kept in sync | 624 * Horizontal and vertical spacing between images. Should be kept in sync |
| 568 * with the style of .mosaic-item in gallery.css (= 2 * ( 4 + 1)) | 625 * with the style of .mosaic-item in gallery.css (= 2 * ( 4 + 1)) |
| 569 */ | 626 */ |
| 570 Mosaic.Layout.SPACING = 10; | 627 Mosaic.Layout.SPACING = 10; |
| 571 | 628 |
| 572 /** | 629 /** |
| 630 * Margin for scrolling using keyboard. Distance between a selected tile | |
| 631 * and window border. | |
| 632 */ | |
| 633 Mosaic.Layout.SCROLL_MARGIN = 30; | |
| 634 | |
| 635 /** | |
| 573 * Layout mode: commit to DOM immediately. | 636 * Layout mode: commit to DOM immediately. |
| 574 */ | 637 */ |
| 575 Mosaic.Layout.MODE_FINAL = 'final'; | 638 Mosaic.Layout.MODE_FINAL = 'final'; |
| 576 | 639 |
| 577 /** | 640 /** |
| 578 * Layout mode: do not commit layout to DOM until it is complete or the viewport | 641 * Layout mode: do not commit layout to DOM until it is complete or the viewport |
| 579 * overflows. | 642 * overflows. |
| 580 */ | 643 */ |
| 581 Mosaic.Layout.MODE_TENTATIVE = 'tentative'; | 644 Mosaic.Layout.MODE_TENTATIVE = 'tentative'; |
| 582 | 645 |
| (...skipping 1013 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1596 this.style.top = top + 'px'; | 1659 this.style.top = top + 'px'; |
| 1597 this.style.width = width + 'px'; | 1660 this.style.width = width + 'px'; |
| 1598 this.style.height = height + 'px'; | 1661 this.style.height = height + 'px'; |
| 1599 | 1662 |
| 1600 if (!this.wrapper_) { // First time, create DOM. | 1663 if (!this.wrapper_) { // First time, create DOM. |
| 1601 this.container_.appendChild(this); | 1664 this.container_.appendChild(this); |
| 1602 var border = util.createChild(this, 'img-border'); | 1665 var border = util.createChild(this, 'img-border'); |
| 1603 this.wrapper_ = util.createChild(border, 'img-wrapper'); | 1666 this.wrapper_ = util.createChild(border, 'img-wrapper'); |
| 1604 } | 1667 } |
| 1605 if (this.hasAttribute('selected')) | 1668 if (this.hasAttribute('selected')) |
| 1606 this.scrollIntoView(); | 1669 this.scrollIntoView(false); |
| 1607 | 1670 |
| 1608 this.thumbnailLoader_.attachImage(this.wrapper_, | 1671 this.thumbnailLoader_.attachImage(this.wrapper_, |
| 1609 ThumbnailLoader.FillMode.FILL); | 1672 ThumbnailLoader.FillMode.FILL); |
| 1610 }; | 1673 }; |
| 1611 | 1674 |
| 1612 /** | 1675 /** |
| 1613 * If the tile is not fully visible scroll the parent to make it fully visible. | 1676 * If the tile is not fully visible scroll the parent to make it fully visible. |
| 1677 * @param {boolean} opt_animated True, if scroll should be animated, | |
| 1678 * default: true. | |
| 1614 */ | 1679 */ |
| 1615 Mosaic.Tile.prototype.scrollIntoView = function() { | 1680 Mosaic.Tile.prototype.scrollIntoView = function(opt_animated) { |
| 1616 if (this.left_ == null) // Not laid out. | 1681 if (this.left_ == null) // Not laid out. |
| 1617 return; | 1682 return; |
| 1618 | 1683 |
| 1619 if (this.left_ < this.container_.scrollLeft) { | 1684 var targetPosition; |
| 1620 this.container_.scrollLeft = this.left_; | 1685 var tileLeft = this.left_ - Mosaic.Layout.SCROLL_MARGIN; |
| 1686 if (tileLeft < this.container_.scrollLeft) { | |
| 1687 targetPosition = tileLeft; | |
| 1621 } else { | 1688 } else { |
| 1622 var tileRight = this.left_ + this.width_; | 1689 var tileRight = this.left_ + this.width_ + Mosaic.Layout.SCROLL_MARGIN; |
| 1623 var scrollRight = this.container_.scrollLeft + this.container_.clientWidth; | 1690 var scrollRight = this.container_.scrollLeft + this.container_.clientWidth; |
| 1624 if (tileRight > scrollRight) | 1691 if (tileRight > scrollRight) |
| 1625 this.container_.scrollLeft = tileRight - this.container_.clientWidth; | 1692 targetPosition = tileRight - this.container_.clientWidth; |
| 1693 } | |
| 1694 | |
| 1695 if (targetPosition) { | |
| 1696 if (opt_animated === false) | |
| 1697 this.container_.scrollLeft = targetPosition; | |
| 1698 else | |
| 1699 this.container_.animatedScrollTo(targetPosition); | |
| 1626 } | 1700 } |
| 1627 }; | 1701 }; |
| 1628 | 1702 |
| 1629 /** | 1703 /** |
| 1630 * @return {Rect} Rectangle occupied by the tile's image, | 1704 * @return {Rect} Rectangle occupied by the tile's image, |
| 1631 * relative to the viewport. | 1705 * relative to the viewport. |
| 1632 */ | 1706 */ |
| 1633 Mosaic.Tile.prototype.getImageRect = function() { | 1707 Mosaic.Tile.prototype.getImageRect = function() { |
| 1634 if (this.left_ == null) // Not laid out. | 1708 if (this.left_ == null) // Not laid out. |
| 1635 return null; | 1709 return null; |
| 1636 | 1710 |
| 1637 var margin = Mosaic.Layout.SPACING / 2; | 1711 var margin = Mosaic.Layout.SPACING / 2; |
| 1638 return new Rect(this.left_ - this.container_.scrollLeft, this.top_, | 1712 return new Rect(this.left_ - this.container_.scrollLeft, this.top_, |
| 1639 this.width_, this.height_).inflate(-margin, -margin); | 1713 this.width_, this.height_).inflate(-margin, -margin); |
| 1640 }; | 1714 }; |
| 1641 | 1715 |
| 1642 /** | 1716 /** |
| 1643 * @return {number} X coordinate of the tile center. | 1717 * @return {number} X coordinate of the tile center. |
| 1644 */ | 1718 */ |
| 1645 Mosaic.Tile.prototype.getCenterX = function() { | 1719 Mosaic.Tile.prototype.getCenterX = function() { |
| 1646 return this.left_ + Math.round(this.width_ / 2); | 1720 return this.left_ + Math.round(this.width_ / 2); |
| 1647 }; | 1721 }; |
| OLD | NEW |