OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 'use strict'; | 5 'use strict'; |
6 | 6 |
7 /** | 7 /** |
8 * Called from the main frame when unloading. | 8 * Called from the main frame when unloading. |
9 * @param {boolean=} opt_exiting True if the app is exiting. | 9 * @param {boolean=} opt_exiting True if the app is exiting. |
10 */ | 10 */ |
11 function unload(opt_exiting) { Gallery.instance.onUnload(opt_exiting); } | 11 function unload(opt_exiting) { Gallery.instance.onUnload(opt_exiting); } |
12 | 12 |
13 /** | 13 /** |
14 * Overrided metadata worker's path. | 14 * Overrided metadata worker's path. |
15 * @type {string} | 15 * @type {string} |
16 * @const | 16 * @const |
17 */ | 17 */ |
18 ContentProvider.WORKER_SCRIPT = '/js/metadata_worker.js'; | 18 ContentProvider.WORKER_SCRIPT = '/js/metadata_worker.js'; |
19 | 19 |
20 /** | 20 /** |
21 * Data model for gallery. | 21 * Data model for gallery. |
22 * | 22 * |
| 23 * @param {MetadataCache} metadataCache Metadata cache. |
23 * @constructor | 24 * @constructor |
24 * @extends {cr.ui.ArrayDataModel} | 25 * @extends {cr.ui.ArrayDataModel} |
25 */ | 26 */ |
26 function GalleryDataModel() { | 27 function GalleryDataModel(metadataCache) { |
27 cr.ui.ArrayDataModel.call(this, []); | 28 cr.ui.ArrayDataModel.call(this, []); |
28 this.metadataCache_ = null; | 29 this.metadataCache_ = metadataCache; |
29 } | 30 } |
30 | 31 |
31 /** | 32 /** |
32 * Maximum number of full size image cache. | 33 * Maximum number of full size image cache. |
33 * @type {number} | 34 * @type {number} |
34 * @const | 35 * @const |
35 * @private | 36 * @private |
36 */ | 37 */ |
37 GalleryDataModel.MAX_FULL_IMAGE_CACHE_ = 3; | 38 GalleryDataModel.MAX_FULL_IMAGE_CACHE_ = 3; |
38 | 39 |
39 /** | 40 /** |
40 * Maximum number of screen size image cache. | 41 * Maximum number of screen size image cache. |
41 * @type {number} | 42 * @type {number} |
42 * @const | 43 * @const |
43 * @private | 44 * @private |
44 */ | 45 */ |
45 GalleryDataModel.MAX_SCREEN_IMAGE_CACHE_ = 5; | 46 GalleryDataModel.MAX_SCREEN_IMAGE_CACHE_ = 5; |
46 | 47 |
47 GalleryDataModel.prototype = { | 48 GalleryDataModel.prototype = { |
48 __proto__: cr.ui.ArrayDataModel.prototype | 49 __proto__: cr.ui.ArrayDataModel.prototype |
49 }; | 50 }; |
50 | 51 |
51 /** | 52 /** |
52 * Initializes the data model. | |
53 * | |
54 * @param {MetadataCache} metadataCache Metadata cache. | |
55 * @param {Array.<FileEntry>} entries Image entries. | |
56 * @return {Promise} Promise to be fulfilled with after initialization. | |
57 */ | |
58 GalleryDataModel.prototype.initialize = function(metadataCache, entries) { | |
59 // Store metadata cache. | |
60 this.metadataCache_ = metadataCache; | |
61 | |
62 // Obtain metadata. | |
63 var metadataPromise = new Promise(function(fulfill) { | |
64 this.metadataCache_.get(entries, Gallery.METADATA_TYPE, fulfill); | |
65 }.bind(this)); | |
66 | |
67 // Initialize the gallery by using the metadata. | |
68 return metadataPromise.then(function(metadata) { | |
69 // Check the length of metadata. | |
70 if (entries.length !== metadata.length) | |
71 return Promise.reject('Failed to obtain metadata for the entries.'); | |
72 | |
73 // Obtains items. | |
74 var items = entries.map(function(entry, i) { | |
75 var clonedMetadata = MetadataCache.cloneMetadata(metadata[i]); | |
76 return new Gallery.Item( | |
77 entry, clonedMetadata, metadataCache, /* original */ true); | |
78 }); | |
79 | |
80 // Update the models. | |
81 this.push.apply(this, items); | |
82 }.bind(this)); | |
83 }; | |
84 | |
85 /** | |
86 * Saves new image. | 53 * Saves new image. |
87 * | 54 * |
88 * @param {Gallery.Item} item Original gallery item. | 55 * @param {Gallery.Item} item Original gallery item. |
89 * @param {Canvas} canvas Canvas containing new image. | 56 * @param {Canvas} canvas Canvas containing new image. |
90 * @param {boolean} overwrite Whether to overwrite the image to the item or not. | 57 * @param {boolean} overwrite Whether to overwrite the image to the item or not. |
91 * @return {Promise} Promise to be fulfilled with when the operation completes. | 58 * @return {Promise} Promise to be fulfilled with when the operation completes. |
92 */ | 59 */ |
93 GalleryDataModel.prototype.saveItem = function(item, canvas, overwrite) { | 60 GalleryDataModel.prototype.saveItem = function(item, canvas, overwrite) { |
94 var oldEntry = item.getEntry(); | 61 var oldEntry = item.getEntry(); |
95 var oldMetadata = item.getMetadata(); | 62 var oldMetadata = item.getMetadata(); |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
216 loadTimeData: {} | 183 loadTimeData: {} |
217 }; | 184 }; |
218 this.container_ = document.querySelector('.gallery'); | 185 this.container_ = document.querySelector('.gallery'); |
219 this.document_ = document; | 186 this.document_ = document; |
220 this.metadataCache_ = this.context_.metadataCache; | 187 this.metadataCache_ = this.context_.metadataCache; |
221 this.volumeManager_ = volumeManager; | 188 this.volumeManager_ = volumeManager; |
222 this.selectedEntry_ = null; | 189 this.selectedEntry_ = null; |
223 this.metadataCacheObserverId_ = null; | 190 this.metadataCacheObserverId_ = null; |
224 this.onExternallyUnmountedBound_ = this.onExternallyUnmounted_.bind(this); | 191 this.onExternallyUnmountedBound_ = this.onExternallyUnmounted_.bind(this); |
225 | 192 |
226 this.dataModel_ = new GalleryDataModel(); | 193 this.dataModel_ = new GalleryDataModel(this.context_.metadataCache); |
227 this.selectionModel_ = new cr.ui.ListSelectionModel(); | 194 this.selectionModel_ = new cr.ui.ListSelectionModel(); |
228 | 195 |
229 this.initDom_(); | 196 this.initDom_(); |
230 this.initListeners_(); | 197 this.initListeners_(); |
231 } | 198 } |
232 | 199 |
233 /** | 200 /** |
234 * Gallery extends cr.EventTarget. | 201 * Gallery extends cr.EventTarget. |
235 */ | 202 */ |
236 Gallery.prototype.__proto__ = cr.EventTarget.prototype; | 203 Gallery.prototype.__proto__ = cr.EventTarget.prototype; |
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
429 return button; | 396 return button; |
430 }; | 397 }; |
431 | 398 |
432 /** | 399 /** |
433 * Loads the content. | 400 * Loads the content. |
434 * | 401 * |
435 * @param {!Array.<Entry>} entries Array of entries. | 402 * @param {!Array.<Entry>} entries Array of entries. |
436 * @param {!Array.<Entry>} selectedEntries Array of selected entries. | 403 * @param {!Array.<Entry>} selectedEntries Array of selected entries. |
437 */ | 404 */ |
438 Gallery.prototype.load = function(entries, selectedEntries) { | 405 Gallery.prototype.load = function(entries, selectedEntries) { |
439 this.dataModel_.initialize(this.metadataCache_, entries).then(function() { | 406 // Obtains max chank size. |
440 // Apply selection. | 407 var maxChunkSize = 20; |
441 this.selectionModel_.adjustLength(this.dataModel_.length); | 408 var volumeInfo = this.volumeManager_.getVolumeInfo(entries[0]); |
442 var entryIndexesByURLs = {}; | 409 if (volumeInfo && |
443 for (var index = 0; index < entries.length; index++) { | 410 volumeInfo.volumeType === VolumeManagerCommon.VolumeType.MTP) { |
444 entryIndexesByURLs[entries[index].toURL()] = index; | 411 maxChunkSize = 1; |
445 } | 412 } |
446 for (var i = 0; i !== selectedEntries.length; i++) { | |
447 var selectedIndex = entryIndexesByURLs[selectedEntries[i].toURL()]; | |
448 if (selectedIndex !== undefined) | |
449 this.selectionModel_.setIndexSelected(selectedIndex, true); | |
450 else | |
451 console.error('Cannot select ' + selectedEntries[i]); | |
452 } | |
453 if (this.selectionModel_.selectedIndexes.length === 0) | |
454 this.onSelection_(); | |
455 | 413 |
456 // Determine the initial mode. | 414 // Make loading list. |
457 var shouldShowMosaic = selectedEntries.length > 1 || | 415 var entrySet = {}; |
458 (this.context_.pageState && | 416 for (var i = 0; i < entries.length; i++) { |
459 this.context_.pageState.gallery === 'mosaic'); | 417 var entry = entries[i]; |
460 this.setCurrentMode_(shouldShowMosaic ? this.mosaicMode_ : this.slideMode_); | 418 entrySet[entry.toURL()] = { |
| 419 entry: entry, |
| 420 selected: false, |
| 421 index: i |
| 422 }; |
| 423 } |
| 424 for (var i = 0; i < selectedEntries.length; i++) { |
| 425 var entry = selectedEntries[i]; |
| 426 entrySet[entry.toURL()] = { |
| 427 entry: entry, |
| 428 selected: true, |
| 429 index: i |
| 430 }; |
| 431 } |
| 432 var loadingList = []; |
| 433 for (var url in entrySet) { |
| 434 loadingList.push(entrySet[url]); |
| 435 } |
| 436 loadingList = loadingList.sort(function(a, b) { |
| 437 if (a.selected && !b.selected) |
| 438 return -1; |
| 439 else if (!a.selected && b.selected) |
| 440 return 1; |
| 441 else |
| 442 return a.index - b.index; |
| 443 }); |
461 | 444 |
462 // Init mosaic mode. | 445 // Load entries. |
463 var mosaic = this.mosaicMode_.getMosaic(); | 446 // Use the self variable capture-by-closure because it is faster than bind. |
464 mosaic.init(); | 447 var self = this; |
| 448 var loadChunk = function(firstChunk) { |
| 449 // Extract chunk. |
| 450 var chunk = loadingList.splice(0, maxChunkSize); |
| 451 if (!chunk.length) |
| 452 return; |
465 | 453 |
466 // Do the initialization for each mode. | 454 return new Promise(function(fulfill) { |
467 if (shouldShowMosaic) { | 455 // Obtains metadata for chunk. |
468 mosaic.show(); | 456 var entries = chunk.map(function(chunkItem) { |
469 this.inactivityWatcher_.check(); // Show the toolbar. | 457 return chunkItem.entry; |
470 cr.dispatchSimpleEvent(this, 'loaded'); | 458 }); |
471 } else { | 459 self.metadataCache_.get(entries, Gallery.METADATA_TYPE, fulfill); |
472 this.slideMode_.enter( | 460 }).then(function(metadataList) { |
473 null, | 461 if (chunk.length !== metadataList.length) |
474 function() { | 462 return Promise.reject('Failed to load metadata.'); |
475 // Flash the toolbar briefly to show it is there. | 463 |
476 this.inactivityWatcher_.kick(Gallery.FIRST_FADE_TIMEOUT); | 464 // Add items to the model. |
477 }.bind(this), | 465 var items = chunk.map(function(chunkItem, index) { |
478 function() { | 466 var clonedMetadata = MetadataCache.cloneMetadata(metadataList[index]); |
479 cr.dispatchSimpleEvent(this, 'loaded'); | 467 return new Gallery.Item( |
480 }.bind(this)); | 468 chunkItem.entry, |
481 } | 469 clonedMetadata, |
482 }.bind(this)).catch(function(error) { | 470 self.metadataCache_, |
| 471 /* original */ true); |
| 472 }); |
| 473 self.dataModel_.push.apply(self.dataModel_, items); |
| 474 |
| 475 // Apply the selection. |
| 476 var selectionUpdated = false; |
| 477 for (var i = 0; i < chunk.length; i++) { |
| 478 if (!chunk[i].selected) |
| 479 continue; |
| 480 var index = self.dataModel_.indexOf(items[i]); |
| 481 if (index < 0) |
| 482 continue; |
| 483 self.selectionModel_.setIndexSelected(index); |
| 484 selectionUpdated = true; |
| 485 } |
| 486 if (selectionUpdated) |
| 487 self.onSelection_(); |
| 488 |
| 489 // Init modes after the first chunk is loaded. |
| 490 if (firstChunk) { |
| 491 // Determine the initial mode. |
| 492 var shouldShowMosaic = selectedEntries.length > 1 || |
| 493 (self.context_.pageState && |
| 494 self.context_.pageState.gallery === 'mosaic'); |
| 495 self.setCurrentMode_( |
| 496 shouldShowMosaic ? self.mosaicMode_ : self.slideMode_); |
| 497 |
| 498 // Init mosaic mode. |
| 499 var mosaic = self.mosaicMode_.getMosaic(); |
| 500 mosaic.init(); |
| 501 |
| 502 // Do the initialization for each mode. |
| 503 if (shouldShowMosaic) { |
| 504 mosaic.show(); |
| 505 self.inactivityWatcher_.check(); // Show the toolbar. |
| 506 cr.dispatchSimpleEvent(self, 'loaded'); |
| 507 } else { |
| 508 self.slideMode_.enter( |
| 509 null, |
| 510 function() { |
| 511 // Flash the toolbar briefly to show it is there. |
| 512 self.inactivityWatcher_.kick(Gallery.FIRST_FADE_TIMEOUT); |
| 513 }, |
| 514 function() { |
| 515 cr.dispatchSimpleEvent(self, 'loaded'); |
| 516 }); |
| 517 } |
| 518 } |
| 519 |
| 520 // Continue to load chunks. |
| 521 return loadChunk(/* firstChunk */ false); |
| 522 }); |
| 523 }; |
| 524 loadChunk(/* firstChunk */ true).catch(function(error) { |
483 console.error(error.stack || error); | 525 console.error(error.stack || error); |
484 }); | 526 }); |
485 }; | 527 }; |
486 | 528 |
487 /** | 529 /** |
488 * Handles user's 'Close' action. | 530 * Handles user's 'Close' action. |
489 * @private | 531 * @private |
490 */ | 532 */ |
491 Gallery.prototype.onClose_ = function() { | 533 Gallery.prototype.onClose_ = function() { |
492 this.executeWhenReady(this.context_.onClose); | 534 this.executeWhenReady(this.context_.onClose); |
(...skipping 448 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
941 window.loadTimeData.data = backgroundComponents.stringData; | 983 window.loadTimeData.data = backgroundComponents.stringData; |
942 gallery = new Gallery(backgroundComponents.volumeManager); | 984 gallery = new Gallery(backgroundComponents.volumeManager); |
943 }; | 985 }; |
944 | 986 |
945 /** | 987 /** |
946 * Loads entries. | 988 * Loads entries. |
947 */ | 989 */ |
948 window.loadEntries = function(entries, selectedEntries) { | 990 window.loadEntries = function(entries, selectedEntries) { |
949 gallery.load(entries, selectedEntries); | 991 gallery.load(entries, selectedEntries); |
950 }; | 992 }; |
OLD | NEW |