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 {!VolumeManager} volumeManager The VolumeManager instance of the
19 function Gallery(volumeManager
) {
21 * @type {{appWindow: chrome.app.window.AppWindow, onClose: function(),
22 * onMaximize: function(), onMinimize: function(),
23 * onAppRegionChanged: function(), readonlyDirName: string,
24 * displayStringFunction: function(), loadTimeData: Object,
25 * curDirEntry: Entry, searchResults: *}}
28 * TODO(yawano): curDirEntry and searchResults seem not to be used.
29 * Investigate them and remove them if possible.
32 appWindow
: chrome
.app
.window
.current(),
33 onClose: function() { window
.close(); },
34 onMaximize: function() {
35 var appWindow
= chrome
.app
.window
.current();
36 if (appWindow
.isMaximized())
41 onMinimize: function() { chrome
.app
.window
.current().minimize(); },
42 onAppRegionChanged: function() {},
44 displayStringFunction: function() { return ''; },
49 this.container_
= queryRequiredElement(document
, '.gallery');
50 this.document_
= document
;
51 this.volumeManager_
= volumeManager
;
53 * @private {!MetadataModel}
56 this.metadataModel_
= MetadataModel
.create(volumeManager
);
58 * @private {!ThumbnailModel}
61 this.thumbnailModel_
= new ThumbnailModel(this.metadataModel_
);
62 this.selectedEntry_
= null;
63 this.onExternallyUnmountedBound_
= this.onExternallyUnmounted_
.bind(this);
64 this.initialized_
= false;
66 this.dataModel_
= new GalleryDataModel(this.metadataModel_
);
67 var downloadVolumeInfo
= this.volumeManager_
.getCurrentProfileVolumeInfo(
68 VolumeManagerCommon
.VolumeType
.DOWNLOADS
);
69 downloadVolumeInfo
.resolveDisplayRoot().then(function(entry
) {
70 this.dataModel_
.fallbackSaveDirectory
= entry
;
71 }.bind(this)).catch(function(error
) {
73 'Failed to obtain the fallback directory: ' + (error
.stack
|| error
));
75 this.selectionModel_
= new cr
.ui
.ListSelectionModel();
78 * @type {(SlideMode|MosaicMode)}
81 this.currentMode_
= null;
87 this.changingMode_
= false;
89 // -----------------------------------------------------------------
90 // Initializes the UI.
92 // Initialize the dialog label.
93 cr
.ui
.dialogs
.BaseDialog
.OK_LABEL
= str('GALLERY_OK_LABEL');
94 cr
.ui
.dialogs
.BaseDialog
.CANCEL_LABEL
= str('GALLERY_CANCEL_LABEL');
96 var content
= queryRequiredElement(document
, '#content');
97 content
.addEventListener('click', this.onContentClick_
.bind(this));
99 this.header_
= queryRequiredElement(document
, '#header');
100 this.toolbar_
= queryRequiredElement(document
, '#toolbar');
102 var preventDefault = function(event
) { event
.preventDefault(); };
104 var minimizeButton
= util
.createChild(this.header_
,
105 'minimize-button tool dimmable',
107 minimizeButton
.tabIndex
= -1;
108 minimizeButton
.addEventListener('click', this.onMinimize_
.bind(this));
109 minimizeButton
.addEventListener('mousedown', preventDefault
);
111 var maximizeButton
= util
.createChild(this.header_
,
112 'maximize-button tool dimmable',
114 maximizeButton
.tabIndex
= -1;
115 maximizeButton
.addEventListener('click', this.onMaximize_
.bind(this));
116 maximizeButton
.addEventListener('mousedown', preventDefault
);
118 var closeButton
= util
.createChild(this.header_
,
119 'close-button tool dimmable',
121 closeButton
.tabIndex
= -1;
122 closeButton
.addEventListener('click', this.onClose_
.bind(this));
123 closeButton
.addEventListener('mousedown', preventDefault
);
125 this.filenameSpacer_
= queryRequiredElement(this.toolbar_
,
127 this.filenameEdit_
= util
.createChild(this.filenameSpacer_
,
130 this.filenameEdit_
.setAttribute('type', 'text');
131 this.filenameEdit_
.addEventListener('blur',
132 this.onFilenameEditBlur_
.bind(this));
134 this.filenameEdit_
.addEventListener('focus',
135 this.onFilenameFocus_
.bind(this));
137 this.filenameEdit_
.addEventListener('keydown',
138 this.onFilenameEditKeydown_
.bind(this));
140 var middleSpacer
= queryRequiredElement(this.toolbar_
, '.middle-spacer');
141 var buttonSpacer
= queryRequiredElement(this.toolbar_
, '.button-spacer');
143 this.prompt_
= new ImageEditor
.Prompt(this.container_
, strf
);
145 this.errorBanner_
= new ErrorBanner(this.container_
);
147 this.modeButton_
= queryRequiredElement(this.toolbar_
, 'button.mode');
148 this.modeButton_
.addEventListener('click',
149 this.toggleMode_
.bind(this, undefined));
151 this.mosaicMode_
= new MosaicMode(content
,
154 this.selectionModel_
,
156 this.toggleMode_
.bind(this, undefined));
158 this.slideMode_
= new SlideMode(this.container_
,
164 this.selectionModel_
,
166 this.thumbnailModel_
,
169 this.toggleMode_
.bind(this),
172 this.slideMode_
.addEventListener('image-displayed', function() {
173 cr
.dispatchSimpleEvent(this, 'image-displayed');
175 this.slideMode_
.addEventListener('image-saved', function() {
176 cr
.dispatchSimpleEvent(this, 'image-saved');
179 this.deleteButton_
= this.initToolbarButton_('delete', 'GALLERY_DELETE');
180 this.deleteButton_
.addEventListener('click', this.delete_
.bind(this));
182 this.shareButton_
= this.initToolbarButton_('share', 'GALLERY_SHARE');
183 this.shareButton_
.addEventListener(
184 'click', this.onShareButtonClick_
.bind(this));
186 this.dataModel_
.addEventListener('splice', this.onSplice_
.bind(this));
187 this.dataModel_
.addEventListener('content', this.onContentChange_
.bind(this));
189 this.selectionModel_
.addEventListener('change', this.onSelection_
.bind(this));
190 this.slideMode_
.addEventListener('useraction', this.onUserAction_
.bind(this));
192 this.shareDialog_
= new ShareDialog(this.container_
);
194 // -----------------------------------------------------------------
195 // Initialize listeners.
197 this.keyDownBound_
= this.onKeyDown_
.bind(this);
198 this.document_
.body
.addEventListener('keydown', this.keyDownBound_
);
200 this.inactivityWatcher_
= new MouseInactivityWatcher(
201 this.container_
, Gallery
.FADE_TIMEOUT
, this.hasActiveTool
.bind(this));
203 // TODO(hirono): Add observer to handle thumbnail update.
204 this.volumeManager_
.addEventListener(
205 'externally-unmounted', this.onExternallyUnmountedBound_
);
206 // The 'pagehide' event is called when the app window is closed.
207 window
.addEventListener('pagehide', this.onPageHide_
.bind(this));
211 * Gallery extends cr.EventTarget.
213 Gallery
.prototype.__proto__
= cr
.EventTarget
.prototype;
216 * Tools fade-out timeout in milliseconds.
220 Gallery
.FADE_TIMEOUT
= 2000;
223 * First time tools fade-out timeout in milliseconds.
227 Gallery
.FIRST_FADE_TIMEOUT
= 1000;
230 * Time until mosaic is initialized in the background. Used to make gallery
231 * in the slide mode load faster. In milliseconds.
235 Gallery
.MOSAIC_BACKGROUND_INIT_DELAY
= 1000;
238 * Types of metadata Gallery uses (to query the metadata cache).
240 * @type {!Array<string>}
242 Gallery
.PREFETCH_PROPERTY_NAMES
=
243 ['imageWidth', 'imageHeight', 'size', 'present'];
246 * Closes gallery when a volume containing the selected item is unmounted.
247 * @param {!Event} event The unmount event.
250 Gallery
.prototype.onExternallyUnmounted_ = function(event
) {
251 if (!this.selectedEntry_
)
254 if (this.volumeManager_
.getVolumeInfo(this.selectedEntry_
) ===
261 * Unloads the Gallery.
264 Gallery
.prototype.onPageHide_ = function() {
265 this.volumeManager_
.removeEventListener(
266 'externally-unmounted', this.onExternallyUnmountedBound_
);
270 * Initializes a toolbar button.
272 * @param {string} className Class to add.
273 * @param {string} title Button title.
274 * @return {!HTMLElement} Newly created button.
277 Gallery
.prototype.initToolbarButton_ = function(className
, title
) {
278 var button
= queryRequiredElement(this.toolbar_
, 'button.' + className
);
279 button
.title
= str(title
);
286 * @param {!Array.<!Entry>} selectedEntries Array of selected entries.
288 Gallery
.prototype.load = function(selectedEntries
) {
289 GalleryUtil
.createEntrySet(selectedEntries
).then(function(allEntries
) {
290 this.loadInternal_(allEntries
, selectedEntries
);
297 * @param {!Array.<!Entry>} entries Array of entries.
298 * @param {!Array.<!Entry>} selectedEntries Array of selected entries.
301 Gallery
.prototype.loadInternal_ = function(entries
, selectedEntries
) {
302 // Obtains max chank size.
303 var maxChunkSize
= 20;
304 var volumeInfo
= this.volumeManager_
.getVolumeInfo(entries
[0]);
306 if (volumeInfo
.volumeType
=== VolumeManagerCommon
.VolumeType
.MTP
)
308 if (volumeInfo
.isReadOnly
)
309 this.context_
.readonlyDirName
= volumeInfo
.label
;
312 // Make loading list.
314 for (var i
= 0; i
< entries
.length
; i
++) {
315 var entry
= entries
[i
];
316 entrySet
[entry
.toURL()] = {
322 for (var i
= 0; i
< selectedEntries
.length
; i
++) {
323 var entry
= selectedEntries
[i
];
324 entrySet
[entry
.toURL()] = {
330 var loadingList
= [];
331 for (var url
in entrySet
) {
332 loadingList
.push(entrySet
[url
]);
334 loadingList
= loadingList
.sort(function(a
, b
) {
335 if (a
.selected
&& !b
.selected
)
337 else if (!a
.selected
&& b
.selected
)
340 return a
.index
- b
.index
;
343 if (loadingList
.length
=== 0) {
344 this.dataModel_
.splice(0, this.dataModel_
.length
);
349 // Use the self variable capture-by-closure because it is faster than bind.
351 var thumbnailModel
= new ThumbnailModel(this.metadataModel_
);
352 var loadChunk = function(firstChunk
) {
354 var chunk
= loadingList
.splice(0, maxChunkSize
);
357 var entries
= chunk
.map(function(chunkItem
) {
358 return chunkItem
.entry
;
360 var metadataPromise
= self
.metadataModel_
.get(
361 entries
, Gallery
.PREFETCH_PROPERTY_NAMES
);
362 var thumbnailPromise
= thumbnailModel
.get(entries
);
363 return Promise
.all([metadataPromise
, thumbnailPromise
]).then(
364 function(metadataLists
) {
365 // Remove all the previous items if it's the first chunk.
366 // Do it here because prevent a flicker between removing all the items
367 // and adding new ones.
369 self
.dataModel_
.splice(0, self
.dataModel_
.length
);
370 self
.updateThumbnails_(); // Remove the caches.
373 // Add items to the model.
375 chunk
.forEach(function(chunkItem
, index
) {
376 var locationInfo
= self
.volumeManager_
.getLocationInfo(chunkItem
.entry
);
377 if (!locationInfo
) // Skip the item, since gone.
379 items
.push(new Gallery
.Item(
382 metadataLists
[0][index
],
383 metadataLists
[1][index
],
384 /* original */ true));
386 self
.dataModel_
.push
.apply(self
.dataModel_
, items
);
388 // Apply the selection.
389 var selectionUpdated
= false;
390 for (var i
= 0; i
< chunk
.length
; i
++) {
391 if (!chunk
[i
].selected
)
393 var index
= self
.dataModel_
.indexOf(items
[i
]);
396 self
.selectionModel_
.setIndexSelected(index
, true);
397 selectionUpdated
= true;
399 if (selectionUpdated
)
402 // Init modes after the first chunk is loaded.
403 if (firstChunk
&& !self
.initialized_
) {
404 // Determine the initial mode.
405 var shouldShowMosaic
= selectedEntries
.length
> 1 ||
406 (self
.context_
.pageState
&&
407 self
.context_
.pageState
.gallery
=== 'mosaic');
408 self
.setCurrentMode_(
409 shouldShowMosaic
? self
.mosaicMode_
: self
.slideMode_
);
412 var mosaic
= self
.mosaicMode_
.getMosaic();
415 // Do the initialization for each mode.
416 if (shouldShowMosaic
) {
418 self
.inactivityWatcher_
.check(); // Show the toolbar.
419 cr
.dispatchSimpleEvent(self
, 'loaded');
421 self
.slideMode_
.enter(
424 // Flash the toolbar briefly to show it is there.
425 self
.inactivityWatcher_
.kick(Gallery
.FIRST_FADE_TIMEOUT
);
428 cr
.dispatchSimpleEvent(self
, 'loaded');
431 self
.initialized_
= true;
434 // Continue to load chunks.
435 return loadChunk(/* firstChunk */ false);
438 loadChunk(/* firstChunk */ true).catch(function(error
) {
439 console
.error(error
.stack
|| error
);
444 * Handles user's 'Close' action.
447 Gallery
.prototype.onClose_ = function() {
448 this.executeWhenReady(this.context_
.onClose
);
452 * Handles user's 'Maximize' action (Escape or a click on the X icon).
455 Gallery
.prototype.onMaximize_ = function() {
456 this.executeWhenReady(this.context_
.onMaximize
);
460 * Handles user's 'Maximize' action (Escape or a click on the X icon).
463 Gallery
.prototype.onMinimize_ = function() {
464 this.executeWhenReady(this.context_
.onMinimize
);
468 * Executes a function when the editor is done with the modifications.
469 * @param {function()} callback Function to execute.
471 Gallery
.prototype.executeWhenReady = function(callback
) {
472 this.currentMode_
.executeWhenReady(callback
);
476 * @return {!Object} File manager private API.
478 Gallery
.getFileManagerPrivate = function() {
479 return chrome
.fileManagerPrivate
|| window
.top
.chrome
.fileManagerPrivate
;
483 * @return {boolean} True if some tool is currently active.
485 Gallery
.prototype.hasActiveTool = function() {
486 return (this.currentMode_
&& this.currentMode_
.hasActiveTool()) ||
491 * External user action event handler.
494 Gallery
.prototype.onUserAction_ = function() {
495 // Show the toolbar and hide it after the default timeout.
496 this.inactivityWatcher_
.kick();
500 * Sets the current mode, update the UI.
501 * @param {!(SlideMode|MosaicMode)} mode Current mode.
504 Gallery
.prototype.setCurrentMode_ = function(mode
) {
505 if (mode
!== this.slideMode_
&& mode
!== this.mosaicMode_
)
506 console
.error('Invalid Gallery mode');
508 this.currentMode_
= mode
;
509 this.container_
.setAttribute('mode', this.currentMode_
.getName());
510 this.updateSelectionAndState_();
511 this.updateButtons_();
515 * Mode toggle event handler.
516 * @param {function()=} opt_callback Callback.
517 * @param {Event=} opt_event Event that caused this call.
520 Gallery
.prototype.toggleMode_ = function(opt_callback
, opt_event
) {
521 if (!this.modeButton_
)
524 if (this.changingMode_
) // Do not re-enter while changing the mode.
528 this.onUserAction_();
530 this.changingMode_
= true;
532 var onModeChanged = function() {
533 this.changingMode_
= false;
534 if (opt_callback
) opt_callback();
537 var tileIndex
= Math
.max(0, this.selectionModel_
.selectedIndex
);
539 var mosaic
= this.mosaicMode_
.getMosaic();
540 var tileRect
= mosaic
.getTileRect(tileIndex
);
542 if (this.currentMode_
=== this.slideMode_
) {
543 this.setCurrentMode_(this.mosaicMode_
);
545 tileRect
, this.slideMode_
.getSelectedImageRect(), true /* instant */);
546 this.slideMode_
.leave(
549 // Animate back to normal position.
550 mosaic
.transform(null, null);
555 this.setCurrentMode_(this.slideMode_
);
556 this.slideMode_
.enter(
559 // Animate to zoomed position.
560 mosaic
.transform(tileRect
, this.slideMode_
.getSelectedImageRect());
568 * Deletes the selected items.
571 Gallery
.prototype.delete_ = function() {
572 this.onUserAction_();
574 // Clone the sorted selected indexes array.
575 var indexesToRemove
= this.selectionModel_
.selectedIndexes
.slice();
576 if (!indexesToRemove
.length
)
579 /* TODO(dgozman): Implement Undo delete, Remove the confirmation dialog. */
581 var itemsToRemove
= this.getSelectedItems();
582 var plural
= itemsToRemove
.length
> 1;
583 var param
= plural
? itemsToRemove
.length
: itemsToRemove
[0].getFileName();
585 function deleteNext() {
586 if (!itemsToRemove
.length
)
587 return; // All deleted.
589 var entry
= itemsToRemove
.pop().getEntry();
590 entry
.remove(deleteNext
, function() {
591 console
.error('Error deleting: ' + entry
.name
);
596 // Prevent the Gallery from handling Esc and Enter.
597 this.document_
.body
.removeEventListener('keydown', this.keyDownBound_
);
598 var restoreListener = function() {
599 this.document_
.body
.addEventListener('keydown', this.keyDownBound_
);
603 var confirm
= new cr
.ui
.dialogs
.ConfirmDialog(this.container_
);
604 confirm
.setOkLabel(str('DELETE_BUTTON_LABEL'));
605 confirm
.show(strf(plural
?
606 'GALLERY_CONFIRM_DELETE_SOME' : 'GALLERY_CONFIRM_DELETE_ONE', param
),
609 this.selectionModel_
.unselectAll();
610 this.selectionModel_
.leadIndex
= -1;
611 // Remove items from the data model, starting from the highest index.
612 while (indexesToRemove
.length
)
613 this.dataModel_
.splice(indexesToRemove
.pop(), 1);
614 // Delete actual files.
618 // Restore the listener after a timeout so that ESC is processed.
619 setTimeout(restoreListener
, 0);
625 * @return {!Array.<Gallery.Item>} Current selection.
627 Gallery
.prototype.getSelectedItems = function() {
628 return this.selectionModel_
.selectedIndexes
.map(
629 this.dataModel_
.item
.bind(this.dataModel_
));
633 * @return {!Array.<Entry>} Array of currently selected entries.
635 Gallery
.prototype.getSelectedEntries = function() {
636 return this.selectionModel_
.selectedIndexes
.map(function(index
) {
637 return this.dataModel_
.item(index
).getEntry();
642 * @return {?Gallery.Item} Current single selection.
644 Gallery
.prototype.getSingleSelectedItem = function() {
645 var items
= this.getSelectedItems();
646 if (items
.length
> 1) {
647 console
.error('Unexpected multiple selection');
654 * Selection change event handler.
657 Gallery
.prototype.onSelection_ = function() {
658 this.updateSelectionAndState_();
662 * Data model splice event handler.
665 Gallery
.prototype.onSplice_ = function() {
666 this.selectionModel_
.adjustLength(this.dataModel_
.length
);
667 this.selectionModel_
.selectedIndexes
=
668 this.selectionModel_
.selectedIndexes
.filter(function(index
) {
669 return 0 <= index
&& index
< this.dataModel_
.length
;
674 * Content change event handler.
675 * @param {!Event} event Event.
678 Gallery
.prototype.onContentChange_ = function(event
) {
679 var index
= this.dataModel_
.indexOf(event
.item
);
680 if (index
!== this.selectionModel_
.selectedIndex
)
681 console
.error('Content changed for unselected item');
682 this.updateSelectionAndState_();
688 * @param {!Event} event Event.
691 Gallery
.prototype.onKeyDown_ = function(event
) {
692 if (this.currentMode_
.onKeyDown(event
))
695 switch (util
.getKeyModifiers(event
) + event
.keyIdentifier
) {
696 case 'U+0008': // Backspace.
697 // The default handler would call history.back and close the Gallery.
698 event
.preventDefault();
701 case 'U+004D': // 'm' switches between Slide and Mosaic mode.
702 this.toggleMode_(undefined, event
);
705 case 'U+0056': // 'v'
706 case 'MediaPlayPause':
707 this.slideMode_
.startSlideshow(SlideMode
.SLIDESHOW_INTERVAL_FIRST
, event
);
710 case 'U+007F': // Delete
711 case 'Shift-U+0033': // Shift+'3' (Delete key might be missing).
712 case 'U+0044': // 'd'
716 case 'U+001B': // Escape
722 // Name box and rename support.
725 * Updates the UI related to the selected item and the persistent state.
729 Gallery
.prototype.updateSelectionAndState_ = function() {
730 var numSelectedItems
= this.selectionModel_
.selectedIndexes
.length
;
731 var selectedEntryURL
= null;
733 // If it's selecting something, update the variable values.
734 if (numSelectedItems
) {
735 // Delete button is available when all images are NOT readOnly.
736 this.deleteButton_
.disabled
= !this.selectionModel_
.selectedIndexes
738 return !this.dataModel_
.item(i
).getLocationInfo().isReadOnly
;
741 // Obtains selected item.
743 this.dataModel_
.item(this.selectionModel_
.selectedIndex
);
744 this.selectedEntry_
= selectedItem
.getEntry();
745 selectedEntryURL
= this.selectedEntry_
.toURL();
748 selectedItem
.touch();
749 this.dataModel_
.evictCache();
751 // Update the title and the display name.
752 if (numSelectedItems
=== 1) {
753 document
.title
= this.selectedEntry_
.name
;
754 this.filenameEdit_
.disabled
= selectedItem
.getLocationInfo().isReadOnly
;
755 this.filenameEdit_
.value
=
756 ImageUtil
.getDisplayNameFromName(this.selectedEntry_
.name
);
757 this.shareButton_
.hidden
= !selectedItem
.getLocationInfo().isDriveBased
;
759 if (this.context_
.curDirEntry
) {
760 // If the Gallery was opened on search results the search query will not
761 // be recorded in the app state and the relaunch will just open the
762 // gallery in the curDirEntry directory.
763 document
.title
= this.context_
.curDirEntry
.name
;
767 this.filenameEdit_
.disabled
= true;
768 this.filenameEdit_
.value
=
769 strf('GALLERY_ITEMS_SELECTED', numSelectedItems
);
770 this.shareButton_
.hidden
= true;
774 this.filenameEdit_
.disabled
= true;
775 this.deleteButton_
.disabled
= true;
776 this.filenameEdit_
.value
= '';
777 this.shareButton_
.hidden
= true;
781 null, // Keep the current directory.
782 selectedEntryURL
, // Update the selection.
783 {gallery
: (this.currentMode_
=== this.mosaicMode_
? 'mosaic' : 'slide')});
787 * Click event handler on filename edit box
790 Gallery
.prototype.onFilenameFocus_ = function() {
791 ImageUtil
.setAttribute(this.filenameSpacer_
, 'renaming', true);
792 this.filenameEdit_
.originalValue
= this.filenameEdit_
.value
;
793 setTimeout(this.filenameEdit_
.select
.bind(this.filenameEdit_
), 0);
794 this.onUserAction_();
798 * Blur event handler on filename edit box.
800 * @param {!Event} event Blur event.
803 Gallery
.prototype.onFilenameEditBlur_ = function(event
) {
804 var item
= this.getSingleSelectedItem();
806 var oldEntry
= item
.getEntry();
808 item
.rename(this.filenameEdit_
.value
).then(function() {
809 var event
= new Event('content');
811 event
.oldEntry
= oldEntry
;
812 event
.thumbnailChanged
= false;
813 this.dataModel_
.dispatchEvent(event
);
814 }.bind(this), function(error
) {
815 if (error
=== 'NOT_CHANGED')
816 return Promise
.resolve();
817 this.filenameEdit_
.value
=
818 ImageUtil
.getDisplayNameFromName(item
.getEntry().name
);
819 this.filenameEdit_
.focus();
820 if (typeof error
=== 'string')
821 this.prompt_
.showStringAt('center', error
, 5000);
823 return Promise
.reject(error
);
824 }.bind(this)).catch(function(error
) {
825 console
.error(error
.stack
|| error
);
829 ImageUtil
.setAttribute(this.filenameSpacer_
, 'renaming', false);
830 this.onUserAction_();
834 * Keydown event handler on filename edit box
835 * @param {!Event} event A keyboard event.
838 Gallery
.prototype.onFilenameEditKeydown_ = function(event
) {
839 event
= assertInstanceof(event
, KeyboardEvent
);
840 switch (event
.keyCode
) {
842 this.filenameEdit_
.value
= this.filenameEdit_
.originalValue
;
843 this.filenameEdit_
.blur();
847 this.filenameEdit_
.blur();
850 event
.stopPropagation();
854 * @return {boolean} True if file renaming is currently in progress.
857 Gallery
.prototype.isRenaming_ = function() {
858 return this.filenameSpacer_
.hasAttribute('renaming');
862 * Content area click handler.
865 Gallery
.prototype.onContentClick_ = function() {
866 this.filenameEdit_
.blur();
870 * Share button handler.
873 Gallery
.prototype.onShareButtonClick_ = function() {
874 var item
= this.getSingleSelectedItem();
877 this.shareDialog_
.showEntry(item
.getEntry(), function() {});
881 * Updates thumbnails.
884 Gallery
.prototype.updateThumbnails_ = function() {
885 if (this.currentMode_
=== this.slideMode_
)
886 this.slideMode_
.updateThumbnails();
888 if (this.mosaicMode_
) {
889 var mosaic
= this.mosaicMode_
.getMosaic();
890 if (mosaic
.isInitialized())
899 Gallery
.prototype.updateButtons_ = function() {
900 if (this.modeButton_
) {
902 this.currentMode_
=== this.slideMode_
? this.mosaicMode_
:
904 this.modeButton_
.title
= str(oppositeMode
.getTitle());
909 * Enters the debug mode.
911 Gallery
.prototype.debugMe = function() {
912 this.mosaicMode_
.debugMe();
922 * (Re-)loads entries.
925 initializePromise
.then(function() {
926 util
.URLsToEntries(window
.appState
.urls
, function(entries
) {
927 gallery
.load(entries
);
933 * Promise to initialize the load time data.
936 var loadTimeDataPromise
= new Promise(function(fulfill
, reject
) {
937 chrome
.fileManagerPrivate
.getStrings(function(strings
) {
938 window
.loadTimeData
.data
= strings
;
939 i18nTemplate
.process(document
, loadTimeData
);
945 * Promise to initialize volume manager.
948 var volumeManagerPromise
= new Promise(function(fulfill
, reject
) {
949 var volumeManager
= new VolumeManagerWrapper(
950 VolumeManagerWrapper
.NonNativeVolumeStatus
.ENABLED
);
951 volumeManager
.ensureInitialized(fulfill
.bind(null, volumeManager
));
955 * Promise to initialize both the volume manager and the load time data.
958 var initializePromise
=
959 Promise
.all([loadTimeDataPromise
, volumeManagerPromise
]).
960 then(function(args
) {
961 var volumeManager
= args
[1];
962 gallery
= new Gallery(volumeManager
);
966 initializePromise
.then(reload
);
969 * Enteres the debug mode.
971 window
.debugMe = function() {
972 initializePromise
.then(function() {