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
3 // found in the LICENSE file.
6 * Overrided metadata worker's path.
9 ContentMetadataProvider.WORKER_SCRIPT = '/js/metadata_worker.js';
12 * Gallery for viewing and editing image files.
14 * @param {!VolumeManagerWrapper} volumeManager
18 function Gallery(volumeManager) {
20 * @type {{appWindow: chrome.app.window.AppWindow, onClose: function(),
21 * onMaximize: function(), onMinimize: function(),
22 * onAppRegionChanged: function(), readonlyDirName: string,
23 * displayStringFunction: function(), loadTimeData: Object,
24 * curDirEntry: Entry, searchResults: *}}
27 * TODO(yawano): curDirEntry and searchResults seem not to be used.
28 * Investigate them and remove them if possible.
31 appWindow: chrome.app.window.current(),
32 onClose: function() { window.close(); },
33 onMaximize: function() {
34 var appWindow = chrome.app.window.current();
35 if (appWindow.isMaximized())
40 onMinimize: function() { chrome.app.window.current().minimize(); },
41 onAppRegionChanged: function() {},
43 displayStringFunction: function() { return ''; },
48 this.container_ = queryRequiredElement(document, '.gallery');
49 this.document_ = document;
50 this.volumeManager_ = volumeManager;
52 * @private {!MetadataModel}
55 this.metadataModel_ = MetadataModel.create(volumeManager);
57 * @private {!ThumbnailModel}
60 this.thumbnailModel_ = new ThumbnailModel(this.metadataModel_);
61 this.selectedEntry_ = null;
62 this.onExternallyUnmountedBound_ = this.onExternallyUnmounted_.bind(this);
63 this.initialized_ = false;
65 this.dataModel_ = new GalleryDataModel(this.metadataModel_);
66 var downloadVolumeInfo = this.volumeManager_.getCurrentProfileVolumeInfo(
67 VolumeManagerCommon.VolumeType.DOWNLOADS);
68 downloadVolumeInfo.resolveDisplayRoot().then(function(entry) {
69 this.dataModel_.fallbackSaveDirectory = entry;
70 }.bind(this)).catch(function(error) {
72 'Failed to obtain the fallback directory: ' + (error.stack || error));
74 this.selectionModel_ = new cr.ui.ListSelectionModel();
77 * @type {(SlideMode|MosaicMode)}
80 this.currentMode_ = null;
86 this.changingMode_ = false;
88 // -----------------------------------------------------------------
89 // Initializes the UI.
91 // Initialize the dialog label.
92 cr.ui.dialogs.BaseDialog.OK_LABEL = str('GALLERY_OK_LABEL');
93 cr.ui.dialogs.BaseDialog.CANCEL_LABEL = str('GALLERY_CANCEL_LABEL');
95 var content = queryRequiredElement(document, '#content');
96 content.addEventListener('click', this.onContentClick_.bind(this));
98 this.header_ = queryRequiredElement(document, '#header');
99 this.toolbar_ = queryRequiredElement(document, '#toolbar');
101 var preventDefault = function(event) { event.preventDefault(); };
103 var minimizeButton = util.createChild(this.header_,
104 'minimize-button tool dimmable',
106 minimizeButton.tabIndex = -1;
107 minimizeButton.addEventListener('click', this.onMinimize_.bind(this));
108 minimizeButton.addEventListener('mousedown', preventDefault);
110 var maximizeButton = util.createChild(this.header_,
111 'maximize-button tool dimmable',
113 maximizeButton.tabIndex = -1;
114 maximizeButton.addEventListener('click', this.onMaximize_.bind(this));
115 maximizeButton.addEventListener('mousedown', preventDefault);
117 var closeButton = util.createChild(this.header_,
118 'close-button tool dimmable',
120 closeButton.tabIndex = -1;
121 closeButton.addEventListener('click', this.onClose_.bind(this));
122 closeButton.addEventListener('mousedown', preventDefault);
124 this.filenameSpacer_ = queryRequiredElement(this.toolbar_,
126 this.filenameEdit_ = util.createChild(this.filenameSpacer_,
129 this.filenameEdit_.setAttribute('type', 'text');
130 this.filenameEdit_.addEventListener('blur',
131 this.onFilenameEditBlur_.bind(this));
133 this.filenameEdit_.addEventListener('focus',
134 this.onFilenameFocus_.bind(this));
136 this.filenameEdit_.addEventListener('keydown',
137 this.onFilenameEditKeydown_.bind(this));
139 var middleSpacer = queryRequiredElement(this.toolbar_, '.middle-spacer');
140 var buttonSpacer = queryRequiredElement(this.toolbar_, '.button-spacer');
142 this.prompt_ = new ImageEditor.Prompt(this.container_, strf);
144 this.errorBanner_ = new ErrorBanner(this.container_);
146 this.modeButton_ = queryRequiredElement(this.toolbar_, 'button.mode');
147 this.modeButton_.addEventListener('click',
148 this.toggleMode_.bind(this, undefined));
150 this.mosaicMode_ = new MosaicMode(content,
153 this.selectionModel_,
155 this.toggleMode_.bind(this, undefined));
157 this.slideMode_ = new SlideMode(this.container_,
163 this.selectionModel_,
165 this.thumbnailModel_,
168 this.toggleMode_.bind(this),
171 this.slideMode_.addEventListener('image-displayed', function() {
172 cr.dispatchSimpleEvent(this, 'image-displayed');
175 this.deleteButton_ = this.initToolbarButton_('delete', 'GALLERY_DELETE');
176 this.deleteButton_.addEventListener('click', this.delete_.bind(this));
178 this.shareButton_ = this.initToolbarButton_('share', 'GALLERY_SHARE');
179 this.shareButton_.addEventListener(
180 'click', this.onShareButtonClick_.bind(this));
182 this.dataModel_.addEventListener('splice', this.onSplice_.bind(this));
183 this.dataModel_.addEventListener('content', this.onContentChange_.bind(this));
185 this.selectionModel_.addEventListener('change', this.onSelection_.bind(this));
186 this.slideMode_.addEventListener('useraction', this.onUserAction_.bind(this));
188 this.shareDialog_ = new ShareDialog(this.container_);
190 // -----------------------------------------------------------------
191 // Initialize listeners.
193 this.keyDownBound_ = this.onKeyDown_.bind(this);
194 this.document_.body.addEventListener('keydown', this.keyDownBound_);
196 this.inactivityWatcher_ = new MouseInactivityWatcher(
197 this.container_, Gallery.FADE_TIMEOUT, this.hasActiveTool.bind(this));
199 // TODO(hirono): Add observer to handle thumbnail update.
200 this.volumeManager_.addEventListener(
201 'externally-unmounted', this.onExternallyUnmountedBound_);
202 // The 'pagehide' event is called when the app window is closed.
203 window.addEventListener('pagehide', this.onPageHide_.bind(this));
207 * Gallery extends cr.EventTarget.
209 Gallery.prototype.__proto__ = cr.EventTarget.prototype;
212 * Tools fade-out timeout in milliseconds.
216 Gallery.FADE_TIMEOUT = 2000;
219 * First time tools fade-out timeout in milliseconds.
223 Gallery.FIRST_FADE_TIMEOUT = 1000;
226 * Time until mosaic is initialized in the background. Used to make gallery
227 * in the slide mode load faster. In milliseconds.
231 Gallery.MOSAIC_BACKGROUND_INIT_DELAY = 1000;
234 * Types of metadata Gallery uses (to query the metadata cache).
236 * @type {!Array<string>}
238 Gallery.PREFETCH_PROPERTY_NAMES =
239 ['imageWidth', 'imageHeight', 'size', 'present'];
242 * Closes gallery when a volume containing the selected item is unmounted.
243 * @param {!Event} event The unmount event.
246 Gallery.prototype.onExternallyUnmounted_ = function(event) {
247 if (!this.selectedEntry_)
250 if (this.volumeManager_.getVolumeInfo(this.selectedEntry_) ===
257 * Unloads the Gallery.
260 Gallery.prototype.onPageHide_ = function() {
261 this.volumeManager_.removeEventListener(
262 'externally-unmounted', this.onExternallyUnmountedBound_);
263 this.volumeManager_.dispose();
267 * Initializes a toolbar button.
269 * @param {string} className Class to add.
270 * @param {string} title Button title.
271 * @return {!HTMLElement} Newly created button.
274 Gallery.prototype.initToolbarButton_ = function(className, title) {
275 var button = queryRequiredElement(this.toolbar_, 'button.' + className);
276 button.title = str(title);
283 * @param {!Array.<!Entry>} selectedEntries Array of selected entries.
285 Gallery.prototype.load = function(selectedEntries) {
286 GalleryUtil.createEntrySet(selectedEntries).then(function(allEntries) {
287 this.loadInternal_(allEntries, selectedEntries);
294 * @param {!Array.<!Entry>} entries Array of entries.
295 * @param {!Array.<!Entry>} selectedEntries Array of selected entries.
298 Gallery.prototype.loadInternal_ = function(entries, selectedEntries) {
299 // Obtains max chank size.
300 var maxChunkSize = 20;
301 var volumeInfo = this.volumeManager_.getVolumeInfo(entries[0]);
303 if (volumeInfo.volumeType === VolumeManagerCommon.VolumeType.MTP)
305 if (volumeInfo.isReadOnly)
306 this.context_.readonlyDirName = volumeInfo.label;
309 // Make loading list.
311 for (var i = 0; i < entries.length; i++) {
312 var entry = entries[i];
313 entrySet[entry.toURL()] = {
319 for (var i = 0; i < selectedEntries.length; i++) {
320 var entry = selectedEntries[i];
321 entrySet[entry.toURL()] = {
327 var loadingList = [];
328 for (var url in entrySet) {
329 loadingList.push(entrySet[url]);
331 loadingList = loadingList.sort(function(a, b) {
332 if (a.selected && !b.selected)
334 else if (!a.selected && b.selected)
337 return a.index - b.index;
340 if (loadingList.length === 0) {
341 this.dataModel_.splice(0, this.dataModel_.length);
346 // Use the self variable capture-by-closure because it is faster than bind.
348 var thumbnailModel = new ThumbnailModel(this.metadataModel_);
349 var loadChunk = function(firstChunk) {
351 var chunk = loadingList.splice(0, maxChunkSize);
354 var entries = chunk.map(function(chunkItem) {
355 return chunkItem.entry;
357 var metadataPromise = self.metadataModel_.get(
358 entries, Gallery.PREFETCH_PROPERTY_NAMES);
359 var thumbnailPromise = thumbnailModel.get(entries);
360 return Promise.all([metadataPromise, thumbnailPromise]).then(
361 function(metadataLists) {
362 // Remove all the previous items if it's the first chunk.
363 // Do it here because prevent a flicker between removing all the items
364 // and adding new ones.
366 self.dataModel_.splice(0, self.dataModel_.length);
367 self.updateThumbnails_(); // Remove the caches.
370 // Add items to the model.
372 chunk.forEach(function(chunkItem, index) {
373 var locationInfo = self.volumeManager_.getLocationInfo(chunkItem.entry);
374 if (!locationInfo) // Skip the item, since gone.
376 items.push(new Gallery.Item(
379 metadataLists[0][index],
380 metadataLists[1][index],
381 /* original */ true));
383 self.dataModel_.push.apply(self.dataModel_, items);
385 // Apply the selection.
386 var selectionUpdated = false;
387 for (var i = 0; i < chunk.length; i++) {
388 if (!chunk[i].selected)
390 var index = self.dataModel_.indexOf(items[i]);
393 self.selectionModel_.setIndexSelected(index, true);
394 selectionUpdated = true;
396 if (selectionUpdated)
399 // Init modes after the first chunk is loaded.
400 if (firstChunk && !self.initialized_) {
401 // Determine the initial mode.
402 var shouldShowMosaic = selectedEntries.length > 1 ||
403 (self.context_.pageState &&
404 self.context_.pageState.gallery === 'mosaic');
405 self.setCurrentMode_(
406 shouldShowMosaic ? self.mosaicMode_ : self.slideMode_);
409 var mosaic = self.mosaicMode_.getMosaic();
412 // Do the initialization for each mode.
413 if (shouldShowMosaic) {
415 self.inactivityWatcher_.check(); // Show the toolbar.
416 cr.dispatchSimpleEvent(self, 'loaded');
418 self.slideMode_.enter(
421 // Flash the toolbar briefly to show it is there.
422 self.inactivityWatcher_.kick(Gallery.FIRST_FADE_TIMEOUT);
425 cr.dispatchSimpleEvent(self, 'loaded');
428 self.initialized_ = true;
431 // Continue to load chunks.
432 return loadChunk(/* firstChunk */ false);
435 loadChunk(/* firstChunk */ true).catch(function(error) {
436 console.error(error.stack || error);
441 * Handles user's 'Close' action.
444 Gallery.prototype.onClose_ = function() {
445 this.executeWhenReady(this.context_.onClose);
449 * Handles user's 'Maximize' action (Escape or a click on the X icon).
452 Gallery.prototype.onMaximize_ = function() {
453 this.executeWhenReady(this.context_.onMaximize);
457 * Handles user's 'Maximize' action (Escape or a click on the X icon).
460 Gallery.prototype.onMinimize_ = function() {
461 this.executeWhenReady(this.context_.onMinimize);
465 * Executes a function when the editor is done with the modifications.
466 * @param {function()} callback Function to execute.
468 Gallery.prototype.executeWhenReady = function(callback) {
469 this.currentMode_.executeWhenReady(callback);
473 * @return {!Object} File manager private API.
475 Gallery.getFileManagerPrivate = function() {
476 return chrome.fileManagerPrivate || window.top.chrome.fileManagerPrivate;
480 * @return {boolean} True if some tool is currently active.
482 Gallery.prototype.hasActiveTool = function() {
483 return (this.currentMode_ && this.currentMode_.hasActiveTool()) ||
488 * External user action event handler.
491 Gallery.prototype.onUserAction_ = function() {
492 // Show the toolbar and hide it after the default timeout.
493 this.inactivityWatcher_.kick();
497 * Sets the current mode, update the UI.
498 * @param {!(SlideMode|MosaicMode)} mode Current mode.
501 Gallery.prototype.setCurrentMode_ = function(mode) {
502 if (mode !== this.slideMode_ && mode !== this.mosaicMode_)
503 console.error('Invalid Gallery mode');
505 this.currentMode_ = mode;
506 this.container_.setAttribute('mode', this.currentMode_.getName());
507 this.updateSelectionAndState_();
508 this.updateButtons_();
512 * Mode toggle event handler.
513 * @param {function()=} opt_callback Callback.
514 * @param {Event=} opt_event Event that caused this call.
517 Gallery.prototype.toggleMode_ = function(opt_callback, opt_event) {
518 if (!this.modeButton_)
521 if (this.changingMode_) // Do not re-enter while changing the mode.
525 this.onUserAction_();
527 this.changingMode_ = true;
529 var onModeChanged = function() {
530 this.changingMode_ = false;
531 if (opt_callback) opt_callback();
534 var tileIndex = Math.max(0, this.selectionModel_.selectedIndex);
536 var mosaic = this.mosaicMode_.getMosaic();
537 var tileRect = mosaic.getTileRect(tileIndex);
539 if (this.currentMode_ === this.slideMode_) {
540 this.setCurrentMode_(this.mosaicMode_);
542 tileRect, this.slideMode_.getSelectedImageRect(), true /* instant */);
543 this.slideMode_.leave(
546 // Animate back to normal position.
547 mosaic.transform(null, null);
552 this.setCurrentMode_(this.slideMode_);
553 this.slideMode_.enter(
556 // Animate to zoomed position.
557 mosaic.transform(tileRect, this.slideMode_.getSelectedImageRect());
565 * Deletes the selected items.
568 Gallery.prototype.delete_ = function() {
569 this.onUserAction_();
571 // Clone the sorted selected indexes array.
572 var indexesToRemove = this.selectionModel_.selectedIndexes.slice();
573 if (!indexesToRemove.length)
576 /* TODO(dgozman): Implement Undo delete, Remove the confirmation dialog. */
578 var itemsToRemove = this.getSelectedItems();
579 var plural = itemsToRemove.length > 1;
580 var param = plural ? itemsToRemove.length : itemsToRemove[0].getFileName();
582 function deleteNext() {
583 if (!itemsToRemove.length)
584 return; // All deleted.
586 var entry = itemsToRemove.pop().getEntry();
587 entry.remove(deleteNext, function() {
588 console.error('Error deleting: ' + entry.name);
593 // Prevent the Gallery from handling Esc and Enter.
594 this.document_.body.removeEventListener('keydown', this.keyDownBound_);
595 var restoreListener = function() {
596 this.document_.body.addEventListener('keydown', this.keyDownBound_);
600 var confirm = new cr.ui.dialogs.ConfirmDialog(this.container_);
601 confirm.setOkLabel(str('DELETE_BUTTON_LABEL'));
602 confirm.show(strf(plural ?
603 'GALLERY_CONFIRM_DELETE_SOME' : 'GALLERY_CONFIRM_DELETE_ONE', param),
606 this.selectionModel_.unselectAll();
607 this.selectionModel_.leadIndex = -1;
608 // Remove items from the data model, starting from the highest index.
609 while (indexesToRemove.length)
610 this.dataModel_.splice(indexesToRemove.pop(), 1);
611 // Delete actual files.
615 // Restore the listener after a timeout so that ESC is processed.
616 setTimeout(restoreListener, 0);
622 * @return {!Array.<Gallery.Item>} Current selection.
624 Gallery.prototype.getSelectedItems = function() {
625 return this.selectionModel_.selectedIndexes.map(
626 this.dataModel_.item.bind(this.dataModel_));
630 * @return {!Array.<Entry>} Array of currently selected entries.
632 Gallery.prototype.getSelectedEntries = function() {
633 return this.selectionModel_.selectedIndexes.map(function(index) {
634 return this.dataModel_.item(index).getEntry();
639 * @return {?Gallery.Item} Current single selection.
641 Gallery.prototype.getSingleSelectedItem = function() {
642 var items = this.getSelectedItems();
643 if (items.length > 1) {
644 console.error('Unexpected multiple selection');
651 * Selection change event handler.
654 Gallery.prototype.onSelection_ = function() {
655 this.updateSelectionAndState_();
659 * Data model splice event handler.
662 Gallery.prototype.onSplice_ = function() {
663 this.selectionModel_.adjustLength(this.dataModel_.length);
664 this.selectionModel_.selectedIndexes =
665 this.selectionModel_.selectedIndexes.filter(function(index) {
666 return 0 <= index && index < this.dataModel_.length;
671 * Content change event handler.
672 * @param {!Event} event Event.
675 Gallery.prototype.onContentChange_ = function(event) {
676 var index = this.dataModel_.indexOf(event.item);
677 if (index !== this.selectionModel_.selectedIndex)
678 console.error('Content changed for unselected item');
679 this.updateSelectionAndState_();
685 * @param {!Event} event Event.
688 Gallery.prototype.onKeyDown_ = function(event) {
689 if (this.currentMode_.onKeyDown(event))
692 switch (util.getKeyModifiers(event) + event.keyIdentifier) {
693 case 'U+0008': // Backspace.
694 // The default handler would call history.back and close the Gallery.
695 event.preventDefault();
698 case 'U+004D': // 'm' switches between Slide and Mosaic mode.
699 this.toggleMode_(undefined, event);
702 case 'U+0056': // 'v'
703 case 'MediaPlayPause':
704 this.slideMode_.startSlideshow(SlideMode.SLIDESHOW_INTERVAL_FIRST, event);
707 case 'U+007F': // Delete
708 case 'Shift-U+0033': // Shift+'3' (Delete key might be missing).
709 case 'U+0044': // 'd'
713 case 'U+001B': // Escape
719 // Name box and rename support.
722 * Updates the UI related to the selected item and the persistent state.
726 Gallery.prototype.updateSelectionAndState_ = function() {
727 var numSelectedItems = this.selectionModel_.selectedIndexes.length;
728 var selectedEntryURL = null;
730 // If it's selecting something, update the variable values.
731 if (numSelectedItems) {
732 // Delete button is available when all images are NOT readOnly.
733 this.deleteButton_.disabled = !this.selectionModel_.selectedIndexes
735 return !this.dataModel_.item(i).getLocationInfo().isReadOnly;
738 // Obtains selected item.
740 this.dataModel_.item(this.selectionModel_.selectedIndex);
741 this.selectedEntry_ = selectedItem.getEntry();
742 selectedEntryURL = this.selectedEntry_.toURL();
745 selectedItem.touch();
746 this.dataModel_.evictCache();
748 // Update the title and the display name.
749 if (numSelectedItems === 1) {
750 document.title = this.selectedEntry_.name;
751 this.filenameEdit_.disabled = selectedItem.getLocationInfo().isReadOnly;
752 this.filenameEdit_.value =
753 ImageUtil.getDisplayNameFromName(this.selectedEntry_.name);
754 this.shareButton_.hidden = !selectedItem.getLocationInfo().isDriveBased;
756 if (this.context_.curDirEntry) {
757 // If the Gallery was opened on search results the search query will not
758 // be recorded in the app state and the relaunch will just open the
759 // gallery in the curDirEntry directory.
760 document.title = this.context_.curDirEntry.name;
764 this.filenameEdit_.disabled = true;
765 this.filenameEdit_.value =
766 strf('GALLERY_ITEMS_SELECTED', numSelectedItems);
767 this.shareButton_.hidden = true;
771 this.filenameEdit_.disabled = true;
772 this.deleteButton_.disabled = true;
773 this.filenameEdit_.value = '';
774 this.shareButton_.hidden = true;
778 null, // Keep the current directory.
779 selectedEntryURL, // Update the selection.
780 {gallery: (this.currentMode_ === this.mosaicMode_ ? 'mosaic' : 'slide')});
784 * Click event handler on filename edit box
787 Gallery.prototype.onFilenameFocus_ = function() {
788 ImageUtil.setAttribute(this.filenameSpacer_, 'renaming', true);
789 this.filenameEdit_.originalValue = this.filenameEdit_.value;
790 setTimeout(this.filenameEdit_.select.bind(this.filenameEdit_), 0);
791 this.onUserAction_();
795 * Blur event handler on filename edit box.
797 * @param {!Event} event Blur event.
800 Gallery.prototype.onFilenameEditBlur_ = function(event) {
801 var item = this.getSingleSelectedItem();
803 var oldEntry = item.getEntry();
805 item.rename(this.filenameEdit_.value).then(function() {
806 var event = new Event('content');
808 event.oldEntry = oldEntry;
809 event.thumbnailChanged = false;
810 this.dataModel_.dispatchEvent(event);
811 }.bind(this), function(error) {
812 if (error === 'NOT_CHANGED')
813 return Promise.resolve();
814 this.filenameEdit_.value =
815 ImageUtil.getDisplayNameFromName(item.getEntry().name);
816 this.filenameEdit_.focus();
817 if (typeof error === 'string')
818 this.prompt_.showStringAt('center', error, 5000);
820 return Promise.reject(error);
821 }.bind(this)).catch(function(error) {
822 console.error(error.stack || error);
826 ImageUtil.setAttribute(this.filenameSpacer_, 'renaming', false);
827 this.onUserAction_();
831 * Keydown event handler on filename edit box
832 * @param {!Event} event A keyboard event.
835 Gallery.prototype.onFilenameEditKeydown_ = function(event) {
836 event = assertInstanceof(event, KeyboardEvent);
837 switch (event.keyCode) {
839 this.filenameEdit_.value = this.filenameEdit_.originalValue;
840 this.filenameEdit_.blur();
844 this.filenameEdit_.blur();
847 event.stopPropagation();
851 * @return {boolean} True if file renaming is currently in progress.
854 Gallery.prototype.isRenaming_ = function() {
855 return this.filenameSpacer_.hasAttribute('renaming');
859 * Content area click handler.
862 Gallery.prototype.onContentClick_ = function() {
863 this.filenameEdit_.blur();
867 * Share button handler.
870 Gallery.prototype.onShareButtonClick_ = function() {
871 var item = this.getSingleSelectedItem();
874 this.shareDialog_.showEntry(item.getEntry(), function() {});
878 * Updates thumbnails.
881 Gallery.prototype.updateThumbnails_ = function() {
882 if (this.currentMode_ === this.slideMode_)
883 this.slideMode_.updateThumbnails();
885 if (this.mosaicMode_) {
886 var mosaic = this.mosaicMode_.getMosaic();
887 if (mosaic.isInitialized())
896 Gallery.prototype.updateButtons_ = function() {
897 if (this.modeButton_) {
899 this.currentMode_ === this.slideMode_ ? this.mosaicMode_ :
901 this.modeButton_.title = str(oppositeMode.getTitle());
906 * Enters the debug mode.
908 Gallery.prototype.debugMe = function() {
909 this.mosaicMode_.debugMe();
919 * (Re-)loads entries.
922 initializePromise.then(function() {
923 util.URLsToEntries(window.appState.urls, function(entries) {
924 gallery.load(entries);
930 * Promise to initialize the load time data.
933 var loadTimeDataPromise = new Promise(function(fulfill, reject) {
934 chrome.fileManagerPrivate.getStrings(function(strings) {
935 window.loadTimeData.data = strings;
936 i18nTemplate.process(document, loadTimeData);
942 * Promise to initialize volume manager.
945 var volumeManagerPromise = new Promise(function(fulfill, reject) {
946 var volumeManager = new VolumeManagerWrapper(
947 VolumeManagerWrapper.NonNativeVolumeStatus.ENABLED);
948 volumeManager.ensureInitialized(fulfill.bind(null, volumeManager));
952 * Promise to initialize both the volume manager and the load time data.
955 var initializePromise =
956 Promise.all([loadTimeDataPromise, volumeManagerPromise]).
957 then(function(args) {
958 var volumeManager = args[1];
959 gallery = new Gallery(volumeManager);
963 initializePromise.then(reload);
966 * Enteres the debug mode.
968 window.debugMe = function() {
969 initializePromise.then(function() {