| 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 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 78 return self; | 78 return self; |
| 79 } | 79 } |
| 80 | 80 |
| 81 /** | 81 /** |
| 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 * @const |
| 89 * @type {number} |
| 88 */ | 90 */ |
| 89 Mosaic.LAYOUT_DELAY = 200; | 91 Mosaic.LAYOUT_DELAY = 200; |
| 90 | 92 |
| 91 /** | 93 /** |
| 92 * Smooth scroll animation duration when scrolling using keyboard or | 94 * Smooth scroll animation duration when scrolling using keyboard or |
| 93 * clicking on a partly visible tile. In ms. | 95 * clicking on a partly visible tile. In ms. |
| 96 * @const |
| 97 * @type {number} |
| 94 */ | 98 */ |
| 95 Mosaic.ANIMATED_SCROLL_DURATION = 500; | 99 Mosaic.ANIMATED_SCROLL_DURATION = 500; |
| 96 | 100 |
| 97 /** | 101 /** |
| 98 * Decorate a Mosaic instance. | 102 * Decorate a Mosaic instance. |
| 99 * | 103 * |
| 100 * @param {Mosaic} self Self pointer. | 104 * @param {Mosaic} self Self pointer. |
| 101 * @param {cr.ui.ArrayDataModel} dataModel Data model. | 105 * @param {cr.ui.ArrayDataModel} dataModel Data model. |
| 102 * @param {cr.ui.ListSelectionModel} selectionModel Selection model. | 106 * @param {cr.ui.ListSelectionModel} selectionModel Selection model. |
| 103 * @param {MetadataCache} metadataCache Metadata cache. | 107 * @param {MetadataCache} metadataCache Metadata cache. |
| (...skipping 26 matching lines...) Expand all Loading... |
| 130 new Mosaic.SelectionController(this.selectionModel_, this.layoutModel_); | 134 new Mosaic.SelectionController(this.selectionModel_, this.layoutModel_); |
| 131 | 135 |
| 132 this.tiles_ = []; | 136 this.tiles_ = []; |
| 133 for (var i = 0; i != this.dataModel_.length; i++) | 137 for (var i = 0; i != this.dataModel_.length; i++) |
| 134 this.tiles_.push(new Mosaic.Tile(this, this.dataModel_.item(i))); | 138 this.tiles_.push(new Mosaic.Tile(this, this.dataModel_.item(i))); |
| 135 | 139 |
| 136 this.selectionModel_.selectedIndexes.forEach(function(index) { | 140 this.selectionModel_.selectedIndexes.forEach(function(index) { |
| 137 this.tiles_[index].select(true); | 141 this.tiles_[index].select(true); |
| 138 }.bind(this)); | 142 }.bind(this)); |
| 139 | 143 |
| 140 this.loadTiles_(this.tiles_); | 144 this.initTiles_(this.tiles_); |
| 141 | 145 |
| 142 // The listeners might be called while some tiles are still loading. | 146 // The listeners might be called while some tiles are still loading. |
| 143 this.initListeners_(); | 147 this.initListeners_(); |
| 144 }; | 148 }; |
| 145 | 149 |
| 146 /** | 150 /** |
| 147 * @return {boolean} Whether mosaic is initialized. | 151 * @return {boolean} Whether mosaic is initialized. |
| 148 */ | 152 */ |
| 149 Mosaic.prototype.isInitialized = function() { | 153 Mosaic.prototype.isInitialized = function() { |
| 150 return !!this.tiles_; | 154 return !!this.tiles_; |
| 151 }; | 155 }; |
| 152 | 156 |
| 153 /** | 157 /** |
| 154 * Start listening to events. | 158 * Start listening to events. |
| 155 * | 159 * |
| 156 * We keep listening to events even when the mosaic is hidden in order to | 160 * We keep listening to events even when the mosaic is hidden in order to |
| 157 * keep the layout up to date. | 161 * keep the layout up to date. |
| 158 * | 162 * |
| 159 * @private | 163 * @private |
| 160 */ | 164 */ |
| 161 Mosaic.prototype.initListeners_ = function() { | 165 Mosaic.prototype.initListeners_ = function() { |
| 162 this.ownerDocument.defaultView.addEventListener( | 166 this.ownerDocument.defaultView.addEventListener( |
| 163 'resize', this.onResize_.bind(this)); | 167 'resize', this.onResize_.bind(this)); |
| 164 | 168 |
| 165 var mouseEventBound = this.onMouseEvent_.bind(this); | 169 var mouseEventBound = this.onMouseEvent_.bind(this); |
| 166 this.addEventListener('mousemove', mouseEventBound); | 170 this.addEventListener('mousemove', mouseEventBound); |
| 167 this.addEventListener('mousedown', mouseEventBound); | 171 this.addEventListener('mousedown', mouseEventBound); |
| 168 this.addEventListener('mouseup', mouseEventBound); | 172 this.addEventListener('mouseup', mouseEventBound); |
| 173 this.addEventListener('scroll', this.onScroll_.bind(this)); |
| 169 | 174 |
| 170 this.selectionModel_.addEventListener('change', this.onSelection_.bind(this)); | 175 this.selectionModel_.addEventListener('change', this.onSelection_.bind(this)); |
| 171 this.selectionModel_.addEventListener('leadIndexChange', | 176 this.selectionModel_.addEventListener('leadIndexChange', |
| 172 this.onLeadChange_.bind(this)); | 177 this.onLeadChange_.bind(this)); |
| 173 | 178 |
| 174 this.dataModel_.addEventListener('splice', this.onSplice_.bind(this)); | 179 this.dataModel_.addEventListener('splice', this.onSplice_.bind(this)); |
| 175 this.dataModel_.addEventListener('content', this.onContentChange_.bind(this)); | 180 this.dataModel_.addEventListener('content', this.onContentChange_.bind(this)); |
| 176 }; | 181 }; |
| 177 | 182 |
| 178 /** | 183 /** |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 254 /** | 259 /** |
| 255 * @param {number} index Tile index. | 260 * @param {number} index Tile index. |
| 256 * Scroll the given tile into the viewport. | 261 * Scroll the given tile into the viewport. |
| 257 */ | 262 */ |
| 258 Mosaic.prototype.scrollIntoView = function(index) { | 263 Mosaic.prototype.scrollIntoView = function(index) { |
| 259 var tile = this.tiles_[index]; | 264 var tile = this.tiles_[index]; |
| 260 if (tile) tile.scrollIntoView(); | 265 if (tile) tile.scrollIntoView(); |
| 261 }; | 266 }; |
| 262 | 267 |
| 263 /** | 268 /** |
| 264 * Load multiple tiles. | 269 * Initializes multiple tiles. |
| 265 * | 270 * |
| 266 * @param {Array.<Mosaic.Tile>} tiles Array of tiles. | 271 * @param {Array.<Mosaic.Tile>} tiles Array of tiles. |
| 267 * @param {function=} opt_callback Completion callback. | 272 * @param {function()=} opt_callback Completion callback. |
| 268 * @private | 273 * @private |
| 269 */ | 274 */ |
| 270 Mosaic.prototype.loadTiles_ = function(tiles, opt_callback) { | 275 Mosaic.prototype.initTiles_ = function(tiles, opt_callback) { |
| 271 // We do not want to use tile indices in asynchronous operations because they | 276 // We do not want to use tile indices in asynchronous operations because they |
| 272 // do not survive data model splices. Copy tile references instead. | 277 // do not survive data model splices. Copy tile references instead. |
| 273 tiles = tiles.slice(); | 278 tiles = tiles.slice(); |
| 274 | 279 |
| 275 // Throttle the metadata access so that we do not overwhelm the file system. | 280 // Throttle the metadata access so that we do not overwhelm the file system. |
| 276 var MAX_CHUNK_SIZE = 10; | 281 var MAX_CHUNK_SIZE = 10; |
| 277 | 282 |
| 278 var loadChunk = function() { | 283 var loadChunk = function() { |
| 279 if (!tiles.length) { | 284 if (!tiles.length) { |
| 280 if (opt_callback) opt_callback(); | 285 if (opt_callback) opt_callback(); |
| 281 return; | 286 return; |
| 282 } | 287 } |
| 283 var chunkSize = Math.min(tiles.length, MAX_CHUNK_SIZE); | 288 var chunkSize = Math.min(tiles.length, MAX_CHUNK_SIZE); |
| 284 var loaded = 0; | 289 var loaded = 0; |
| 285 for (var i = 0; i != chunkSize; i++) { | 290 for (var i = 0; i != chunkSize; i++) { |
| 286 this.loadTile_(tiles.shift(), function() { | 291 this.initTile_(tiles.shift(), function() { |
| 287 if (++loaded == chunkSize) { | 292 if (++loaded == chunkSize) { |
| 288 this.layout(); | 293 this.layout(); |
| 289 loadChunk(); | 294 loadChunk(); |
| 290 } | 295 } |
| 291 }.bind(this)); | 296 }.bind(this)); |
| 292 } | 297 } |
| 293 }.bind(this); | 298 }.bind(this); |
| 294 | 299 |
| 295 loadChunk(); | 300 loadChunk(); |
| 296 }; | 301 }; |
| 297 | 302 |
| 298 /** | 303 /** |
| 299 * Load a single tile. | 304 * Initializes a single tile. |
| 300 * | 305 * |
| 301 * @param {Mosaic.Tile} tile Tile. | 306 * @param {Mosaic.Tile} tile Tile. |
| 302 * @param {function} callback Completion callback. | 307 * @param {function()} callback Completion callback. |
| 303 * @private | 308 * @private |
| 304 */ | 309 */ |
| 305 Mosaic.prototype.loadTile_ = function(tile, callback) { | 310 Mosaic.prototype.initTile_ = function(tile, callback) { |
| 306 var url = tile.getItem().getUrl(); | 311 var url = tile.getItem().getUrl(); |
| 307 var onImageLoaded = function(success) { | 312 var onImageMeasured = callback; |
| 308 if (!success && this.onThumbnailError_) { | |
| 309 this.onThumbnailError_(url); | |
| 310 } | |
| 311 callback(); | |
| 312 }.bind(this); | |
| 313 this.metadataCache_.get(url, Gallery.METADATA_TYPE, | 313 this.metadataCache_.get(url, Gallery.METADATA_TYPE, |
| 314 function(metadata) { tile.load(metadata, onImageLoaded) }); | 314 function(metadata) { |
| 315 tile.init(metadata, onImageMeasured); |
| 316 }); |
| 315 }; | 317 }; |
| 316 | 318 |
| 317 /** | 319 /** |
| 318 * Reload all tiles. | 320 * Reload all tiles. |
| 319 */ | 321 */ |
| 320 Mosaic.prototype.reload = function() { | 322 Mosaic.prototype.reload = function() { |
| 321 this.layoutModel_.reset_(); | 323 this.layoutModel_.reset_(); |
| 322 this.tiles_.forEach(function(t) { t.markUnloaded() }); | 324 this.tiles_.forEach(function(t) { t.markUnloaded() }); |
| 323 this.loadTiles_(this.tiles_); | 325 this.initTiles_(this.tiles_); |
| 324 }; | 326 }; |
| 325 | 327 |
| 326 /** | 328 /** |
| 327 * Layout the tiles in the order of their indices. | 329 * Layout the tiles in the order of their indices. |
| 328 * | 330 * |
| 329 * Starts where it last stopped (at #0 the first time). | 331 * Starts where it last stopped (at #0 the first time). |
| 330 * Stops when all tiles are processed or when the next tile is still loading. | 332 * Stops when all tiles are processed or when the next tile is still loading. |
| 331 */ | 333 */ |
| 332 Mosaic.prototype.layout = function() { | 334 Mosaic.prototype.layout = function() { |
| 333 if (this.layoutTimer_) { | 335 if (this.layoutTimer_) { |
| 334 clearTimeout(this.layoutTimer_); | 336 clearTimeout(this.layoutTimer_); |
| 335 this.layoutTimer_ = null; | 337 this.layoutTimer_ = null; |
| 336 } | 338 } |
| 337 while (true) { | 339 while (true) { |
| 338 var index = this.layoutModel_.getTileCount(); | 340 var index = this.layoutModel_.getTileCount(); |
| 339 if (index == this.tiles_.length) | 341 if (index == this.tiles_.length) |
| 340 break; // All tiles done. | 342 break; // All tiles done. |
| 341 var tile = this.tiles_[index]; | 343 var tile = this.tiles_[index]; |
| 342 if (!tile.isLoaded()) | 344 if (!tile.isInitialized()) |
| 343 break; // Next layout will try to restart from here. | 345 break; // Next layout will try to restart from here. |
| 344 this.layoutModel_.add(tile, index + 1 == this.tiles_.length); | 346 this.layoutModel_.add(tile, index + 1 == this.tiles_.length); |
| 345 } | 347 } |
| 348 this.loadVisibleTiles_(); |
| 346 }; | 349 }; |
| 347 | 350 |
| 348 /** | 351 /** |
| 349 * Schedule the layout. | 352 * Schedule the layout. |
| 350 * | 353 * |
| 351 * @param {number=} opt_delay Delay in ms. | 354 * @param {number=} opt_delay Delay in ms. |
| 352 */ | 355 */ |
| 353 Mosaic.prototype.scheduleLayout = function(opt_delay) { | 356 Mosaic.prototype.scheduleLayout = function(opt_delay) { |
| 354 if (!this.layoutTimer_) { | 357 if (!this.layoutTimer_) { |
| 355 this.layoutTimer_ = setTimeout(function() { | 358 this.layoutTimer_ = setTimeout(function() { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 390 target = target.parentNode) { | 393 target = target.parentNode) { |
| 391 if (target.classList.contains('mosaic-tile')) { | 394 if (target.classList.contains('mosaic-tile')) { |
| 392 index = this.dataModel_.indexOf(target.getItem()); | 395 index = this.dataModel_.indexOf(target.getItem()); |
| 393 break; | 396 break; |
| 394 } | 397 } |
| 395 } | 398 } |
| 396 this.selectionController_.handlePointerDownUp(event, index); | 399 this.selectionController_.handlePointerDownUp(event, index); |
| 397 }; | 400 }; |
| 398 | 401 |
| 399 /** | 402 /** |
| 403 * Scroll handler. |
| 404 * @private |
| 405 */ |
| 406 Mosaic.prototype.onScroll_ = function() { |
| 407 this.loadVisibleTiles_(); |
| 408 }; |
| 409 |
| 410 /** |
| 400 * Selection change handler. | 411 * Selection change handler. |
| 401 * | 412 * |
| 402 * @param {Event} event Event. | 413 * @param {Event} event Event. |
| 403 * @private | 414 * @private |
| 404 */ | 415 */ |
| 405 Mosaic.prototype.onSelection_ = function(event) { | 416 Mosaic.prototype.onSelection_ = function(event) { |
| 406 for (var i = 0; i != event.changes.length; i++) { | 417 for (var i = 0; i != event.changes.length; i++) { |
| 407 var change = event.changes[i]; | 418 var change = event.changes[i]; |
| 408 var tile = this.tiles_[change.index]; | 419 var tile = this.tiles_[change.index]; |
| 409 if (tile) tile.select(change.selected); | 420 if (tile) tile.select(change.selected); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 441 this.tiles_.splice(index, event.removed.length); | 452 this.tiles_.splice(index, event.removed.length); |
| 442 this.scheduleLayout(Mosaic.LAYOUT_DELAY); | 453 this.scheduleLayout(Mosaic.LAYOUT_DELAY); |
| 443 } | 454 } |
| 444 | 455 |
| 445 if (event.added.length) { | 456 if (event.added.length) { |
| 446 var newTiles = []; | 457 var newTiles = []; |
| 447 for (var t = 0; t != event.added.length; t++) | 458 for (var t = 0; t != event.added.length; t++) |
| 448 newTiles.push(new Mosaic.Tile(this, this.dataModel_.item(index + t))); | 459 newTiles.push(new Mosaic.Tile(this, this.dataModel_.item(index + t))); |
| 449 | 460 |
| 450 this.tiles_.splice.apply(this.tiles_, [index, 0].concat(newTiles)); | 461 this.tiles_.splice.apply(this.tiles_, [index, 0].concat(newTiles)); |
| 451 this.loadTiles_(newTiles); | 462 this.initTiles_(newTiles); |
| 452 } | 463 } |
| 453 | 464 |
| 454 if (this.tiles_.length != this.dataModel_.length) | 465 if (this.tiles_.length != this.dataModel_.length) |
| 455 console.error('Mosaic is out of sync'); | 466 console.error('Mosaic is out of sync'); |
| 456 }; | 467 }; |
| 457 | 468 |
| 458 /** | 469 /** |
| 459 * Content change handler. | 470 * Content change handler. |
| 460 * | 471 * |
| 461 * @param {Event} event Event. | 472 * @param {Event} event Event. |
| 462 * @private | 473 * @private |
| 463 */ | 474 */ |
| 464 Mosaic.prototype.onContentChange_ = function(event) { | 475 Mosaic.prototype.onContentChange_ = function(event) { |
| 465 if (!this.tiles_) | 476 if (!this.tiles_) |
| 466 return; | 477 return; |
| 467 | 478 |
| 468 if (!event.metadata) | 479 if (!event.metadata) |
| 469 return; // Thumbnail unchanged, nothing to do. | 480 return; // Thumbnail unchanged, nothing to do. |
| 470 | 481 |
| 471 var index = this.dataModel_.indexOf(event.item); | 482 var index = this.dataModel_.indexOf(event.item); |
| 472 if (index != this.selectionModel_.selectedIndex) | 483 if (index != this.selectionModel_.selectedIndex) |
| 473 console.error('Content changed for unselected item'); | 484 console.error('Content changed for unselected item'); |
| 474 | 485 |
| 475 this.layoutModel_.invalidateFromTile_(index); | 486 this.layoutModel_.invalidateFromTile_(index); |
| 476 this.tiles_[index].load( | 487 this.tiles_[index].init(event.metadata, function() { |
| 477 event.metadata, this.scheduleLayout.bind(this, Mosaic.LAYOUT_DELAY)); | 488 this.tiles_[index].load( |
| 489 this.scheduleLayout.bind(this, Mosaic.LAYOUT_DELAY), |
| 490 this.onThumbnailError_); |
| 491 }.bind(this)); |
| 478 }; | 492 }; |
| 479 | 493 |
| 480 /** | 494 /** |
| 481 * Keydown event handler. | 495 * Keydown event handler. |
| 482 * | 496 * |
| 483 * @param {Event} event Event. | 497 * @param {Event} event Event. |
| 484 * @return {boolean} True if the event has been consumed. | 498 * @return {boolean} True if the event has been consumed. |
| 485 */ | 499 */ |
| 486 Mosaic.prototype.onKeyDown = function(event) { | 500 Mosaic.prototype.onKeyDown = function(event) { |
| 487 this.selectionController_.handleKeyDown(event); | 501 this.selectionController_.handleKeyDown(event); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 521 }; | 535 }; |
| 522 | 536 |
| 523 /** | 537 /** |
| 524 * Hide the mosaic. | 538 * Hide the mosaic. |
| 525 */ | 539 */ |
| 526 Mosaic.prototype.hide = function() { | 540 Mosaic.prototype.hide = function() { |
| 527 this.removeAttribute('visible'); | 541 this.removeAttribute('visible'); |
| 528 }; | 542 }; |
| 529 | 543 |
| 530 /** | 544 /** |
| 545 * Loads visible tiles. Ignores consecutive calls. Does not reload already |
| 546 * loaded images. |
| 547 * @private |
| 548 */ |
| 549 Mosaic.prototype.loadVisibleTiles_ = function() { |
| 550 if (this.loadVisibleTilesTimer_) { |
| 551 clearTimeout(this.loadVisibleTilesTimer_); |
| 552 this.loadVisibleTilesTimer_ = null; |
| 553 } |
| 554 this.loadVisibleTilesTimer_ = setTimeout(function() { |
| 555 var viewportRect = new Rect(0, 0, this.clientWidth, this.clientHeight); |
| 556 for (var index = 0; index < this.tiles_.length; index++) { |
| 557 var tile = this.tiles_[index]; |
| 558 var imageRect = tile.getImageRect(); |
| 559 if (!tile.isLoading() && !tile.isLoaded() && imageRect && |
| 560 imageRect.intersects(viewportRect)) { |
| 561 tile.load(function() {}, this.onThumbnailError_); |
| 562 } |
| 563 } |
| 564 }.bind(this), 100); |
| 565 }; |
| 566 |
| 567 /** |
| 531 * Apply or reset the zoom transform. | 568 * Apply or reset the zoom transform. |
| 532 * | 569 * |
| 533 * @param {Rect} tileRect Tile rectangle. Reset the transform if null. | 570 * @param {Rect} tileRect Tile rectangle. Reset the transform if null. |
| 534 * @param {Rect} imageRect Large image rectangle. Reset the transform if null. | 571 * @param {Rect} imageRect Large image rectangle. Reset the transform if null. |
| 535 * @param {boolean=} opt_instant True of the transition should be instant. | 572 * @param {boolean=} opt_instant True of the transition should be instant. |
| 536 */ | 573 */ |
| 537 Mosaic.prototype.transform = function(tileRect, imageRect, opt_instant) { | 574 Mosaic.prototype.transform = function(tileRect, imageRect, opt_instant) { |
| 538 if (opt_instant) { | 575 if (opt_instant) { |
| 539 this.style.webkitTransitionDuration = '0'; | 576 this.style.webkitTransitionDuration = '0'; |
| 540 } else { | 577 } else { |
| (...skipping 1040 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1581 Mosaic.Tile.prototype.getMaxContentHeight = function() { | 1618 Mosaic.Tile.prototype.getMaxContentHeight = function() { |
| 1582 return this.maxContentHeight_; | 1619 return this.maxContentHeight_; |
| 1583 }; | 1620 }; |
| 1584 | 1621 |
| 1585 /** | 1622 /** |
| 1586 * @return {number} The aspect ratio of the tile image. | 1623 * @return {number} The aspect ratio of the tile image. |
| 1587 */ | 1624 */ |
| 1588 Mosaic.Tile.prototype.getAspectRatio = function() { return this.aspectRatio_ }; | 1625 Mosaic.Tile.prototype.getAspectRatio = function() { return this.aspectRatio_ }; |
| 1589 | 1626 |
| 1590 /** | 1627 /** |
| 1628 * @return {boolean} True if the tile is initialized. |
| 1629 */ |
| 1630 Mosaic.Tile.prototype.isInitialized = function() { |
| 1631 return !!this.maxContentHeight_; |
| 1632 }; |
| 1633 |
| 1634 /** |
| 1591 * @return {boolean} True if the tile is loaded. | 1635 * @return {boolean} True if the tile is loaded. |
| 1592 */ | 1636 */ |
| 1593 Mosaic.Tile.prototype.isLoaded = function() { return !!this.maxContentHeight_ }; | 1637 Mosaic.Tile.prototype.isLoaded = function() { |
| 1638 return this.imageLoaded_; |
| 1639 }; |
| 1640 |
| 1641 /** |
| 1642 * @return {boolean} True if the tile is being loaded. |
| 1643 */ |
| 1644 Mosaic.Tile.prototype.isLoading = function() { |
| 1645 return this.imageLoading_; |
| 1646 }; |
| 1594 | 1647 |
| 1595 /** | 1648 /** |
| 1596 * Mark the tile as not loaded to prevent it from participating in the layout. | 1649 * Mark the tile as not loaded to prevent it from participating in the layout. |
| 1597 */ | 1650 */ |
| 1598 Mosaic.Tile.prototype.markUnloaded = function() { | 1651 Mosaic.Tile.prototype.markUnloaded = function() { |
| 1599 this.maxContentHeight_ = 0; | 1652 this.maxContentHeight_ = 0; |
| 1653 if (this.thumbnailLoader_) { |
| 1654 this.thumbnailLoader_.cancel(); |
| 1655 this.imageLoaded_ = false; |
| 1656 this.imageLoading_ = false; |
| 1657 } |
| 1600 }; | 1658 }; |
| 1601 | 1659 |
| 1602 /** | 1660 /** |
| 1603 * Load the thumbnail image into the tile. | 1661 * Initializes the thumbnail in the tile. Does not load an image, but sets |
| 1662 * target dimensions using metadata. |
| 1604 * | 1663 * |
| 1605 * @param {Object} metadata Metadata object. | 1664 * @param {Object} metadata Metadata object. |
| 1606 * @param {function} callback Completion callback. | 1665 * @param {function()} onImageMeasured Image measured callback. |
| 1607 */ | 1666 */ |
| 1608 Mosaic.Tile.prototype.load = function(metadata, callback) { | 1667 Mosaic.Tile.prototype.init = function(metadata, onImageMeasured) { |
| 1609 this.markUnloaded(); | 1668 this.markUnloaded(); |
| 1610 this.left_ = null; // Mark as not laid out. | 1669 this.left_ = null; // Mark as not laid out. |
| 1611 | 1670 |
| 1612 this.thumbnailLoader_ = new ThumbnailLoader(this.getItem().getUrl(), | 1671 this.thumbnailLoader_ = new ThumbnailLoader( |
| 1613 ThumbnailLoader.LoaderType.CANVAS, | 1672 this.getItem().getUrl(), |
| 1614 metadata); | 1673 ThumbnailLoader.LoaderType.CANVAS, |
| 1674 metadata, |
| 1675 undefined, // Media type. |
| 1676 ThumbnailLoader.UseEmbedded.NO_EMBEDDED); |
| 1615 | 1677 |
| 1678 var setDimensions = function(width, height) { |
| 1679 if (width > height) { |
| 1680 if (width > Mosaic.Tile.MAX_CONTENT_SIZE) { |
| 1681 height = Math.round(height * Mosaic.Tile.MAX_CONTENT_SIZE / width); |
| 1682 width = Mosaic.Tile.MAX_CONTENT_SIZE; |
| 1683 } |
| 1684 } else { |
| 1685 if (height > Mosaic.Tile.MAX_CONTENT_SIZE) { |
| 1686 width = Math.round(width * Mosaic.Tile.MAX_CONTENT_SIZE / height); |
| 1687 height = Mosaic.Tile.MAX_CONTENT_SIZE; |
| 1688 } |
| 1689 } |
| 1690 this.maxContentHeight_ = Math.max(Mosaic.Tile.MIN_CONTENT_SIZE, height); |
| 1691 this.aspectRatio_ = width / height; |
| 1692 onImageMeasured(); |
| 1693 }.bind(this); |
| 1694 |
| 1695 // Dimensions are always acquired from the metadata. If it is not available, |
| 1696 // then the image will not be displayed. |
| 1697 if (metadata.media && metadata.media.width) { |
| 1698 setDimensions(metadata.media.width, metadata.media.height); |
| 1699 } else { |
| 1700 // No dimensions in metadata, then display the generic icon instead. |
| 1701 // TODO(mtomasz): Display a gneric icon instead of a black rectangle. |
| 1702 setDimensions(Mosaic.Tile.GENERIC_ICON_SIZE, |
| 1703 Mosaic.Tile.GENERIC_ICON_SIZE); |
| 1704 } |
| 1705 }; |
| 1706 |
| 1707 /** |
| 1708 * Loads an image into the tile. |
| 1709 * |
| 1710 * @param {function(boolean)} onImageLoaded Callback when image is loaded. |
| 1711 * The argument is true for success, false for failure. |
| 1712 * @param {function()=} opt_onThumbnailError Callback for image loading error. |
| 1713 */ |
| 1714 Mosaic.Tile.prototype.load = function(onImageLoaded, opt_onThumbnailError) { |
| 1715 this.imageLoaded_ = false; |
| 1716 this.imageLoading_ = true; |
| 1616 this.thumbnailLoader_.loadDetachedImage(function(success) { | 1717 this.thumbnailLoader_.loadDetachedImage(function(success) { |
| 1617 if (this.thumbnailLoader_.hasValidImage()) { | 1718 if (!success) { |
| 1618 var width = this.thumbnailLoader_.getWidth(); | 1719 if (opt_onThumbnailError) |
| 1619 var height = this.thumbnailLoader_.getHeight(); | 1720 opt_onThumbnailError(); |
| 1620 if (width > height) { | |
| 1621 if (width > Mosaic.Tile.MAX_CONTENT_SIZE) { | |
| 1622 height = Math.round(height * Mosaic.Tile.MAX_CONTENT_SIZE / width); | |
| 1623 width = Mosaic.Tile.MAX_CONTENT_SIZE; | |
| 1624 } | |
| 1625 } else { | |
| 1626 if (height > Mosaic.Tile.MAX_CONTENT_SIZE) { | |
| 1627 width = Math.round(width * Mosaic.Tile.MAX_CONTENT_SIZE / height); | |
| 1628 height = Mosaic.Tile.MAX_CONTENT_SIZE; | |
| 1629 } | |
| 1630 } | |
| 1631 this.maxContentHeight_ = Math.max(Mosaic.Tile.MIN_CONTENT_SIZE, height); | |
| 1632 this.aspectRatio_ = width / height; | |
| 1633 } else { | |
| 1634 this.maxContentHeight_ = Mosaic.Tile.GENERIC_ICON_SIZE; | |
| 1635 this.aspectRatio_ = 1; | |
| 1636 } | 1721 } |
| 1637 | 1722 if (this.wrapper_) { |
| 1638 callback(success); | 1723 this.thumbnailLoader_.attachImage(this.wrapper_, |
| 1724 ThumbnailLoader.FillMode.FILL); |
| 1725 } |
| 1726 onImageLoaded(success); |
| 1727 this.imageLoaded_ = true; |
| 1728 this.imageLoading_ = false; |
| 1639 }.bind(this)); | 1729 }.bind(this)); |
| 1640 }; | 1730 }; |
| 1641 | 1731 |
| 1642 /** | 1732 /** |
| 1643 * Select/unselect the tile. | 1733 * Select/unselect the tile. |
| 1644 * | 1734 * |
| 1645 * @param {boolean} on True if selected. | 1735 * @param {boolean} on True if selected. |
| 1646 */ | 1736 */ |
| 1647 Mosaic.Tile.prototype.select = function(on) { | 1737 Mosaic.Tile.prototype.select = function(on) { |
| 1648 if (on) | 1738 if (on) |
| (...skipping 22 matching lines...) Expand all Loading... |
| 1671 this.style.height = height + 'px'; | 1761 this.style.height = height + 'px'; |
| 1672 | 1762 |
| 1673 if (!this.wrapper_) { // First time, create DOM. | 1763 if (!this.wrapper_) { // First time, create DOM. |
| 1674 this.container_.appendChild(this); | 1764 this.container_.appendChild(this); |
| 1675 var border = util.createChild(this, 'img-border'); | 1765 var border = util.createChild(this, 'img-border'); |
| 1676 this.wrapper_ = util.createChild(border, 'img-wrapper'); | 1766 this.wrapper_ = util.createChild(border, 'img-wrapper'); |
| 1677 } | 1767 } |
| 1678 if (this.hasAttribute('selected')) | 1768 if (this.hasAttribute('selected')) |
| 1679 this.scrollIntoView(false); | 1769 this.scrollIntoView(false); |
| 1680 | 1770 |
| 1681 this.thumbnailLoader_.attachImage(this.wrapper_, | 1771 if (this.imageLoaded_) { |
| 1682 ThumbnailLoader.FillMode.FILL); | 1772 this.thumbnailLoader_.attachImage(this.wrapper_, |
| 1773 ThumbnailLoader.FillMode.FILL); |
| 1774 } |
| 1683 }; | 1775 }; |
| 1684 | 1776 |
| 1685 /** | 1777 /** |
| 1686 * If the tile is not fully visible scroll the parent to make it fully visible. | 1778 * If the tile is not fully visible scroll the parent to make it fully visible. |
| 1687 * @param {boolean=} opt_animated True, if scroll should be animated, | 1779 * @param {boolean=} opt_animated True, if scroll should be animated, |
| 1688 * default: true. | 1780 * default: true. |
| 1689 */ | 1781 */ |
| 1690 Mosaic.Tile.prototype.scrollIntoView = function(opt_animated) { | 1782 Mosaic.Tile.prototype.scrollIntoView = function(opt_animated) { |
| 1691 if (this.left_ == null) // Not laid out. | 1783 if (this.left_ == null) // Not laid out. |
| 1692 return; | 1784 return; |
| (...skipping 29 matching lines...) Expand all Loading... |
| 1722 return new Rect(this.left_ - this.container_.scrollLeft, this.top_, | 1814 return new Rect(this.left_ - this.container_.scrollLeft, this.top_, |
| 1723 this.width_, this.height_).inflate(-margin, -margin); | 1815 this.width_, this.height_).inflate(-margin, -margin); |
| 1724 }; | 1816 }; |
| 1725 | 1817 |
| 1726 /** | 1818 /** |
| 1727 * @return {number} X coordinate of the tile center. | 1819 * @return {number} X coordinate of the tile center. |
| 1728 */ | 1820 */ |
| 1729 Mosaic.Tile.prototype.getCenterX = function() { | 1821 Mosaic.Tile.prototype.getCenterX = function() { |
| 1730 return this.left_ + Math.round(this.width_ / 2); | 1822 return this.left_ + Math.round(this.width_ / 2); |
| 1731 }; | 1823 }; |
| OLD | NEW |