Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(43)

Side by Side Diff: chrome/browser/resources/file_manager/js/photo/gallery.js

Issue 39123003: [Files.app] Split the JavaScript files into subdirectories: common, background, and foreground (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fixed test failure. Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 'use strict';
6
7 /**
8 * Called from the main frame when unloading.
9 * @return {string?} User-visible message on null if it is OK to close.
10 */
11 function beforeunload() { return Gallery.instance.onBeforeUnload() }
12
13 /**
14 * Called from the main frame when unloading.
15 * @param {boolean=} opt_exiting True if the app is exiting.
16 */
17 function unload(opt_exiting) { Gallery.instance.onUnload(opt_exiting) }
18
19 /**
20 * Gallery for viewing and editing image files.
21 *
22 * @param {Object} context Object containing the following:
23 * {function(string)} onNameChange Called every time a selected
24 * item name changes (on rename and on selection change).
25 * {AppWindow} appWindow
26 * {function(string)} onBack
27 * {function()} onClose
28 * {function()} onMaximize
29 * {MetadataCache} metadataCache
30 * {Array.<Object>} shareActions
31 * {string} readonlyDirName Directory name for readonly warning or null.
32 * {DirEntry} saveDirEntry Directory to save to.
33 * {function(string)} displayStringFunction.
34 * @param {VolumeManagerWrapper} volumeManager The VolumeManager instance of
35 * the system.
36 * @class
37 * @constructor
38 */
39 function Gallery(context, volumeManager) {
40 this.container_ = document.querySelector('.gallery');
41 this.document_ = document;
42 this.context_ = context;
43 this.metadataCache_ = context.metadataCache;
44 this.volumeManager_ = volumeManager;
45
46 this.dataModel_ = new cr.ui.ArrayDataModel([]);
47 this.selectionModel_ = new cr.ui.ListSelectionModel();
48 this.displayStringFunction_ = context.displayStringFunction;
49
50 this.initDom_();
51 this.initListeners_();
52 }
53
54 /**
55 * Gallery extends cr.EventTarget.
56 */
57 Gallery.prototype.__proto__ = cr.EventTarget.prototype;
58
59 /**
60 * Create and initialize a Gallery object based on a context.
61 *
62 * @param {Object} context Gallery context.
63 * @param {VolumeManagerWrapper} volumeManager VolumeManager of the system.
64 * @param {Array.<string>} urls Array of urls.
65 * @param {Array.<string>} selectedUrls Array of selected urls.
66 */
67 Gallery.open = function(context, volumeManager, urls, selectedUrls) {
68 Gallery.instance = new Gallery(context, volumeManager);
69 Gallery.instance.load(urls, selectedUrls);
70 };
71
72 /**
73 * Tools fade-out timeout im milliseconds.
74 * @const
75 * @type {number}
76 */
77 Gallery.FADE_TIMEOUT = 3000;
78
79 /**
80 * First time tools fade-out timeout im milliseconds.
81 * @const
82 * @type {number}
83 */
84 Gallery.FIRST_FADE_TIMEOUT = 1000;
85
86 /**
87 * Time until mosaic is initialized in the background. Used to make gallery
88 * in the slide mode load faster. In miiliseconds.
89 * @const
90 * @type {number}
91 */
92 Gallery.MOSAIC_BACKGROUND_INIT_DELAY = 1000;
93
94 /**
95 * Types of metadata Gallery uses (to query the metadata cache).
96 * @const
97 * @type {string}
98 */
99 Gallery.METADATA_TYPE = 'thumbnail|filesystem|media|streaming|drive';
100
101 /**
102 * Initialize listeners.
103 * @private
104 */
105 Gallery.prototype.initListeners_ = function() {
106 this.document_.oncontextmenu = function(e) { e.preventDefault(); };
107 this.keyDownBound_ = this.onKeyDown_.bind(this);
108 this.document_.body.addEventListener('keydown', this.keyDownBound_);
109
110 this.inactivityWatcher_ = new MouseInactivityWatcher(
111 this.container_, Gallery.FADE_TIMEOUT, this.hasActiveTool.bind(this));
112
113 // Search results may contain files from different subdirectories so
114 // the observer is not going to work.
115 if (!this.context_.searchResults && this.context_.curDirEntry) {
116 this.thumbnailObserverId_ = this.metadataCache_.addObserver(
117 this.context_.curDirEntry,
118 MetadataCache.CHILDREN,
119 'thumbnail',
120 this.updateThumbnails_.bind(this));
121 }
122
123 this.volumeManager_.addEventListener('externally-unmounted',
124 this.onExternallyUnmounted_.bind(this));
125 };
126
127 /**
128 * Closes gallery when a volume containing the selected item is unmounted.
129 * @param {Event} event The unmount event.
130 * @private
131 */
132 Gallery.prototype.onExternallyUnmounted_ = function(event) {
133 if (!this.selectedItemFilesystemPath_)
134 return;
135 if (this.selectedItemFilesystemPath_.indexOf(event.mountPath) == 0)
136 this.onBack_();
137 };
138
139 /**
140 * Beforeunload handler.
141 * @return {string?} User-visible message on null if it is OK to close.
142 */
143 Gallery.prototype.onBeforeUnload = function() {
144 return this.slideMode_.onBeforeUnload();
145 };
146
147 /**
148 * Unload the Gallery.
149 * @param {boolean} exiting True if the app is exiting.
150 */
151 Gallery.prototype.onUnload = function(exiting) {
152 if (!this.context_.searchResults) {
153 this.metadataCache_.removeObserver(this.thumbnailObserverId_);
154 }
155 this.slideMode_.onUnload(exiting);
156 };
157
158 /**
159 * Initializes DOM UI
160 * @private
161 */
162 Gallery.prototype.initDom_ = function() {
163 var content = util.createChild(this.container_, 'content');
164 content.addEventListener('click', this.onContentClick_.bind(this));
165
166 this.header_ = util.createChild(this.container_, 'header tool dimmable');
167 this.toolbar_ = util.createChild(this.container_, 'toolbar tool dimmable');
168
169 var backButton = util.createChild(this.container_,
170 'back-button tool dimmable');
171 util.createChild(backButton);
172 backButton.addEventListener('click', this.onBack_.bind(this));
173
174 var preventDefault = function(event) { event.preventDefault(); };
175
176 var maximizeButton = util.createChild(this.header_,
177 'maximize-button tool dimmable',
178 'button');
179 maximizeButton.tabIndex = -1;
180 maximizeButton.addEventListener('click', this.onMaximize_.bind(this));
181 maximizeButton.addEventListener('mousedown', preventDefault);
182
183 var closeButton = util.createChild(this.header_,
184 'close-button tool dimmable',
185 'button');
186 closeButton.tabIndex = -1;
187 closeButton.addEventListener('click', this.onClose_.bind(this));
188 closeButton.addEventListener('mousedown', preventDefault);
189
190 this.filenameSpacer_ = util.createChild(this.toolbar_, 'filename-spacer');
191 this.filenameEdit_ = util.createChild(this.filenameSpacer_,
192 'namebox', 'input');
193
194 this.filenameEdit_.setAttribute('type', 'text');
195 this.filenameEdit_.addEventListener('blur',
196 this.onFilenameEditBlur_.bind(this));
197
198 this.filenameEdit_.addEventListener('focus',
199 this.onFilenameFocus_.bind(this));
200
201 this.filenameEdit_.addEventListener('keydown',
202 this.onFilenameEditKeydown_.bind(this));
203
204 util.createChild(this.toolbar_, 'button-spacer');
205
206 this.prompt_ = new ImageEditor.Prompt(
207 this.container_, this.displayStringFunction_);
208
209 this.modeButton_ = util.createChild(this.toolbar_, 'button mode', 'button');
210 this.modeButton_.addEventListener('click',
211 this.toggleMode_.bind(this, null));
212
213 this.mosaicMode_ = new MosaicMode(content,
214 this.dataModel_,
215 this.selectionModel_,
216 this.metadataCache_,
217 this.toggleMode_.bind(this, null));
218
219 this.slideMode_ = new SlideMode(this.container_,
220 content,
221 this.toolbar_,
222 this.prompt_,
223 this.dataModel_,
224 this.selectionModel_,
225 this.context_,
226 this.toggleMode_.bind(this),
227 this.displayStringFunction_);
228
229 this.slideMode_.addEventListener('image-displayed', function() {
230 cr.dispatchSimpleEvent(this, 'image-displayed');
231 }.bind(this));
232 this.slideMode_.addEventListener('image-saved', function() {
233 cr.dispatchSimpleEvent(this, 'image-saved');
234 }.bind(this));
235
236 var deleteButton = this.createToolbarButton_('delete', 'GALLERY_DELETE');
237 deleteButton.addEventListener('click', this.delete_.bind(this));
238
239 this.shareButton_ = this.createToolbarButton_('share', 'GALLERY_SHARE');
240 this.shareButton_.setAttribute('disabled', '');
241 this.shareButton_.addEventListener('click', this.toggleShare_.bind(this));
242
243 this.shareMenu_ = util.createChild(this.container_, 'share-menu');
244 this.shareMenu_.hidden = true;
245 util.createChild(this.shareMenu_, 'bubble-point');
246
247 this.dataModel_.addEventListener('splice', this.onSplice_.bind(this));
248 this.dataModel_.addEventListener('content', this.onContentChange_.bind(this));
249
250 this.selectionModel_.addEventListener('change', this.onSelection_.bind(this));
251 this.slideMode_.addEventListener('useraction', this.onUserAction_.bind(this));
252 };
253
254 /**
255 * Creates toolbar button.
256 *
257 * @param {string} className Class to add.
258 * @param {string} title Button title.
259 * @return {HTMLElement} Newly created button.
260 * @private
261 */
262 Gallery.prototype.createToolbarButton_ = function(className, title) {
263 var button = util.createChild(this.toolbar_, className, 'button');
264 button.title = this.displayStringFunction_(title);
265 return button;
266 };
267
268 /**
269 * Load the content.
270 *
271 * @param {Array.<string>} urls Array of urls.
272 * @param {Array.<string>} selectedUrls Array of selected urls.
273 */
274 Gallery.prototype.load = function(urls, selectedUrls) {
275 var items = [];
276 for (var index = 0; index < urls.length; ++index) {
277 items.push(new Gallery.Item(urls[index]));
278 }
279 this.dataModel_.push.apply(this.dataModel_, items);
280
281 this.selectionModel_.adjustLength(this.dataModel_.length);
282
283 for (var i = 0; i != selectedUrls.length; i++) {
284 var selectedIndex = urls.indexOf(selectedUrls[i]);
285 if (selectedIndex >= 0)
286 this.selectionModel_.setIndexSelected(selectedIndex, true);
287 else
288 console.error('Cannot select ' + selectedUrls[i]);
289 }
290
291 if (this.selectionModel_.selectedIndexes.length == 0)
292 this.onSelection_();
293
294 var mosaic = this.mosaicMode_ && this.mosaicMode_.getMosaic();
295
296 // Mosaic view should show up if most of the selected files are images.
297 var imagesCount = 0;
298 for (var i = 0; i != selectedUrls.length; i++) {
299 if (FileType.getMediaType(selectedUrls[i]) == 'image')
300 imagesCount++;
301 }
302 var mostlyImages = imagesCount > (selectedUrls.length / 2.0);
303
304 var forcedMosaic = (this.context_.pageState &&
305 this.context_.pageState.gallery == 'mosaic');
306
307 var showMosaic = (mostlyImages && selectedUrls.length > 1) || forcedMosaic;
308 if (mosaic && showMosaic) {
309 this.setCurrentMode_(this.mosaicMode_);
310 mosaic.init();
311 mosaic.show();
312 this.inactivityWatcher_.check(); // Show the toolbar.
313 cr.dispatchSimpleEvent(this, 'loaded');
314 } else {
315 this.setCurrentMode_(this.slideMode_);
316 var maybeLoadMosaic = function() {
317 if (mosaic)
318 mosaic.init();
319 cr.dispatchSimpleEvent(this, 'loaded');
320 }.bind(this);
321 /* TODO: consider nice blow-up animation for the first image */
322 this.slideMode_.enter(null, function() {
323 // Flash the toolbar briefly to show it is there.
324 this.inactivityWatcher_.kick(Gallery.FIRST_FADE_TIMEOUT);
325 }.bind(this),
326 maybeLoadMosaic);
327 }
328 };
329
330 /**
331 * Close the Gallery and go to Files.app.
332 * @private
333 */
334 Gallery.prototype.back_ = function() {
335 if (util.isFullScreen(this.context_.appWindow)) {
336 util.toggleFullScreen(this.context_.appWindow,
337 false); // Leave the full screen mode.
338 }
339 this.context_.onBack(this.getSelectedUrls());
340 };
341
342 /**
343 * Handle user's 'Back' action (Escape or a click on the X icon).
344 * @private
345 */
346 Gallery.prototype.onBack_ = function() {
347 this.executeWhenReady(this.back_.bind(this));
348 };
349
350 /**
351 * Handle user's 'Close' action.
352 * @private
353 */
354 Gallery.prototype.onClose_ = function() {
355 this.executeWhenReady(this.context_.onClose);
356 };
357
358 /**
359 * Handle user's 'Maximize' action (Escape or a click on the X icon).
360 * @private
361 */
362 Gallery.prototype.onMaximize_ = function() {
363 this.executeWhenReady(this.context_.onMaximize);
364 };
365
366 /**
367 * Execute a function when the editor is done with the modifications.
368 * @param {function} callback Function to execute.
369 */
370 Gallery.prototype.executeWhenReady = function(callback) {
371 this.currentMode_.executeWhenReady(callback);
372 };
373
374 /**
375 * @return {Object} File browser private API.
376 */
377 Gallery.getFileBrowserPrivate = function() {
378 return chrome.fileBrowserPrivate || window.top.chrome.fileBrowserPrivate;
379 };
380
381 /**
382 * @return {boolean} True if some tool is currently active.
383 */
384 Gallery.prototype.hasActiveTool = function() {
385 return this.currentMode_.hasActiveTool() ||
386 this.isSharing_() || this.isRenaming_();
387 };
388
389 /**
390 * External user action event handler.
391 * @private
392 */
393 Gallery.prototype.onUserAction_ = function() {
394 this.closeShareMenu_();
395 // Show the toolbar and hide it after the default timeout.
396 this.inactivityWatcher_.kick();
397 };
398
399 /**
400 * Set the current mode, update the UI.
401 * @param {Object} mode Current mode.
402 * @private
403 */
404 Gallery.prototype.setCurrentMode_ = function(mode) {
405 if (mode != this.slideMode_ && mode != this.mosaicMode_)
406 console.error('Invalid Gallery mode');
407
408 this.currentMode_ = mode;
409 this.container_.setAttribute('mode', this.currentMode_.getName());
410 this.updateSelectionAndState_();
411 this.updateButtons_();
412 };
413
414 /**
415 * Mode toggle event handler.
416 * @param {function=} opt_callback Callback.
417 * @param {Event=} opt_event Event that caused this call.
418 * @private
419 */
420 Gallery.prototype.toggleMode_ = function(opt_callback, opt_event) {
421 if (!this.modeButton_)
422 return;
423
424 if (this.changingMode_) // Do not re-enter while changing the mode.
425 return;
426
427 if (opt_event)
428 this.onUserAction_();
429
430 this.changingMode_ = true;
431
432 var onModeChanged = function() {
433 this.changingMode_ = false;
434 if (opt_callback) opt_callback();
435 }.bind(this);
436
437 var tileIndex = Math.max(0, this.selectionModel_.selectedIndex);
438
439 var mosaic = this.mosaicMode_.getMosaic();
440 var tileRect = mosaic.getTileRect(tileIndex);
441
442 if (this.currentMode_ == this.slideMode_) {
443 this.setCurrentMode_(this.mosaicMode_);
444 mosaic.transform(
445 tileRect, this.slideMode_.getSelectedImageRect(), true /* instant */);
446 this.slideMode_.leave(tileRect,
447 function() {
448 // Animate back to normal position.
449 mosaic.transform();
450 mosaic.show();
451 onModeChanged();
452 }.bind(this));
453 } else {
454 this.setCurrentMode_(this.slideMode_);
455 this.slideMode_.enter(tileRect,
456 function() {
457 // Animate to zoomed position.
458 mosaic.transform(tileRect, this.slideMode_.getSelectedImageRect());
459 mosaic.hide();
460 }.bind(this),
461 onModeChanged);
462 }
463 };
464
465 /**
466 * Deletes the selected items.
467 * @private
468 */
469 Gallery.prototype.delete_ = function() {
470 this.onUserAction_();
471
472 // Clone the sorted selected indexes array.
473 var indexesToRemove = this.selectionModel_.selectedIndexes.slice();
474 if (!indexesToRemove.length)
475 return;
476
477 /* TODO(dgozman): Implement Undo delete, Remove the confirmation dialog. */
478
479 var itemsToRemove = this.getSelectedItems();
480 var plural = itemsToRemove.length > 1;
481 var param = plural ? itemsToRemove.length : itemsToRemove[0].getFileName();
482
483 function deleteNext() {
484 if (!itemsToRemove.length)
485 return; // All deleted.
486
487 var url = itemsToRemove.pop().getUrl();
488 webkitResolveLocalFileSystemURL(url,
489 function(entry) {
490 entry.remove(deleteNext,
491 util.flog('Error deleting ' + url, deleteNext));
492 },
493 util.flog('Error resolving ' + url, deleteNext));
494 }
495
496 // Prevent the Gallery from handling Esc and Enter.
497 this.document_.body.removeEventListener('keydown', this.keyDownBound_);
498 var restoreListener = function() {
499 this.document_.body.addEventListener('keydown', this.keyDownBound_);
500 }.bind(this);
501
502 cr.ui.dialogs.BaseDialog.OK_LABEL = this.displayStringFunction_(
503 'GALLERY_OK_LABEL');
504 cr.ui.dialogs.BaseDialog.CANCEL_LABEL =
505 this.displayStringFunction_('GALLERY_CANCEL_LABEL');
506 var confirm = new cr.ui.dialogs.ConfirmDialog(this.container_);
507 confirm.show(
508 this.displayStringFunction_(plural ? 'GALLERY_CONFIRM_DELETE_SOME' :
509 'GALLERY_CONFIRM_DELETE_ONE', param),
510 function() {
511 restoreListener();
512 this.selectionModel_.unselectAll();
513 this.selectionModel_.leadIndex = -1;
514 // Remove items from the data model, starting from the highest index.
515 while (indexesToRemove.length)
516 this.dataModel_.splice(indexesToRemove.pop(), 1);
517 // Delete actual files.
518 deleteNext();
519 }.bind(this),
520 function() {
521 // Restore the listener after a timeout so that ESC is processed.
522 setTimeout(restoreListener, 0);
523 });
524 };
525
526 /**
527 * @return {Array.<Gallery.Item>} Current selection.
528 */
529 Gallery.prototype.getSelectedItems = function() {
530 return this.selectionModel_.selectedIndexes.map(
531 this.dataModel_.item.bind(this.dataModel_));
532 };
533
534 /**
535 * @return {Array.<string>} Array of currently selected urls.
536 */
537 Gallery.prototype.getSelectedUrls = function() {
538 return this.selectionModel_.selectedIndexes.map(function(index) {
539 return this.dataModel_.item(index).getUrl();
540 }.bind(this));
541 };
542
543 /**
544 * @return {Gallery.Item} Current single selection.
545 */
546 Gallery.prototype.getSingleSelectedItem = function() {
547 var items = this.getSelectedItems();
548 if (items.length > 1)
549 throw new Error('Unexpected multiple selection');
550 return items[0];
551 };
552
553 /**
554 * Selection change event handler.
555 * @private
556 */
557 Gallery.prototype.onSelection_ = function() {
558 this.updateSelectionAndState_();
559 this.updateShareMenu_();
560 };
561
562 /**
563 * Data model splice event handler.
564 * @private
565 */
566 Gallery.prototype.onSplice_ = function() {
567 this.selectionModel_.adjustLength(this.dataModel_.length);
568 };
569
570 /**
571 * Content change event handler.
572 * @param {Event} event Event.
573 * @private
574 */
575 Gallery.prototype.onContentChange_ = function(event) {
576 var index = this.dataModel_.indexOf(event.item);
577 if (index != this.selectionModel_.selectedIndex)
578 console.error('Content changed for unselected item');
579 this.updateSelectionAndState_();
580 };
581
582 /**
583 * Keydown handler.
584 *
585 * @param {Event} event Event.
586 * @private
587 */
588 Gallery.prototype.onKeyDown_ = function(event) {
589 var wasSharing = this.isSharing_();
590 this.closeShareMenu_();
591
592 if (this.currentMode_.onKeyDown(event))
593 return;
594
595 switch (util.getKeyModifiers(event) + event.keyIdentifier) {
596 case 'U+0008': // Backspace.
597 // The default handler would call history.back and close the Gallery.
598 event.preventDefault();
599 break;
600
601 case 'U+001B': // Escape
602 // Swallow Esc if it closed the Share menu, otherwise close the Gallery.
603 if (!wasSharing)
604 this.onBack_();
605 break;
606
607 case 'U+004D': // 'm' switches between Slide and Mosaic mode.
608 this.toggleMode_(null, event);
609 break;
610
611 case 'U+0056': // 'v'
612 this.slideMode_.startSlideshow(SlideMode.SLIDESHOW_INTERVAL_FIRST, event);
613 break;
614
615 case 'U+007F': // Delete
616 case 'Shift-U+0033': // Shift+'3' (Delete key might be missing).
617 this.delete_();
618 break;
619 }
620 };
621
622 // Name box and rename support.
623
624 /**
625 * Update the UI related to the selected item and the persistent state.
626 *
627 * @private
628 */
629 Gallery.prototype.updateSelectionAndState_ = function() {
630 var path;
631 var displayName = '';
632
633 var selectedItems = this.getSelectedItems();
634 if (selectedItems.length == 1) {
635 var item = selectedItems[0];
636 path = util.extractFilePath(item.getUrl());
637 var fullName = item.getFileName();
638 window.top.document.title = fullName;
639 displayName = ImageUtil.getFileNameFromFullName(fullName);
640 } else if (selectedItems.length > 1 && this.context_.curDirEntry) {
641 // If the Gallery was opened on search results the search query will not be
642 // recorded in the app state and the relaunch will just open the gallery
643 // in the curDirEntry directory.
644 path = this.context_.curDirEntry.fullPath;
645 window.top.document.title = this.context_.curDirEntry.name;
646 displayName =
647 this.displayStringFunction_('GALLERY_ITEMS_SELECTED',
648 selectedItems.length);
649 }
650
651 window.top.util.updateAppState(path,
652 {gallery: (this.currentMode_ == this.mosaicMode_ ? 'mosaic' : 'slide')});
653
654 // We can't rename files in readonly directory.
655 // We can only rename a single file.
656 this.filenameEdit_.disabled = selectedItems.length != 1 ||
657 this.context_.readonlyDirName;
658
659 this.filenameEdit_.value = displayName;
660
661 // Resolve real filesystem path of the current file.
662 if (this.selectionModel_.selectedIndexes.length) {
663 var selectedIndex = this.selectionModel_.selectedIndex;
664 var selectedItem =
665 this.dataModel_.item(this.selectionModel_.selectedIndex);
666
667 this.selectedItemFilesystemPath_ = null;
668 webkitResolveLocalFileSystemURL(selectedItem.getUrl(),
669 function(entry) {
670 if (this.selectionModel_.selectedIndex != selectedIndex)
671 return;
672 this.selectedItemFilesystemPath_ = entry.fullPath;
673 }.bind(this));
674 }
675 };
676
677 /**
678 * Click event handler on filename edit box
679 * @private
680 */
681 Gallery.prototype.onFilenameFocus_ = function() {
682 ImageUtil.setAttribute(this.filenameSpacer_, 'renaming', true);
683 this.filenameEdit_.originalValue = this.filenameEdit_.value;
684 setTimeout(this.filenameEdit_.select.bind(this.filenameEdit_), 0);
685 this.onUserAction_();
686 };
687
688 /**
689 * Blur event handler on filename edit box.
690 *
691 * @param {Event} event Blur event.
692 * @return {boolean} if default action should be prevented.
693 * @private
694 */
695 Gallery.prototype.onFilenameEditBlur_ = function(event) {
696 if (this.filenameEdit_.value && this.filenameEdit_.value[0] == '.') {
697 this.prompt_.show('file_hidden_name', 5000);
698 this.filenameEdit_.focus();
699 event.stopPropagation();
700 event.preventDefault();
701 return false;
702 }
703
704 var item = this.getSingleSelectedItem();
705 var oldUrl = item.getUrl();
706
707 var onFileExists = function() {
708 this.prompt_.show('file_exists', 3000);
709 this.filenameEdit_.value = name;
710 this.filenameEdit_.focus();
711 }.bind(this);
712
713 var onSuccess = function() {
714 var e = new Event('content');
715 e.item = item;
716 e.oldUrl = oldUrl;
717 e.metadata = null; // Metadata unchanged.
718 this.dataModel_.dispatchEvent(e);
719 }.bind(this);
720
721 if (this.filenameEdit_.value) {
722 this.getSingleSelectedItem().rename(
723 this.filenameEdit_.value, onSuccess, onFileExists);
724 }
725
726 ImageUtil.setAttribute(this.filenameSpacer_, 'renaming', false);
727 this.onUserAction_();
728 };
729
730 /**
731 * Keydown event handler on filename edit box
732 * @private
733 */
734 Gallery.prototype.onFilenameEditKeydown_ = function() {
735 switch (event.keyCode) {
736 case 27: // Escape
737 this.filenameEdit_.value = this.filenameEdit_.originalValue;
738 this.filenameEdit_.blur();
739 break;
740
741 case 13: // Enter
742 this.filenameEdit_.blur();
743 break;
744 }
745 event.stopPropagation();
746 };
747
748 /**
749 * @return {boolean} True if file renaming is currently in progress.
750 * @private
751 */
752 Gallery.prototype.isRenaming_ = function() {
753 return this.filenameSpacer_.hasAttribute('renaming');
754 };
755
756 /**
757 * Content area click handler.
758 * @private
759 */
760 Gallery.prototype.onContentClick_ = function() {
761 this.closeShareMenu_();
762 this.filenameEdit_.blur();
763 };
764
765 // Share button support.
766
767 /**
768 * @return {boolean} True if the Share menu is active.
769 * @private
770 */
771 Gallery.prototype.isSharing_ = function() {
772 return !this.shareMenu_.hidden;
773 };
774
775 /**
776 * Close Share menu if it is open.
777 * @private
778 */
779 Gallery.prototype.closeShareMenu_ = function() {
780 if (this.isSharing_())
781 this.toggleShare_();
782 };
783
784 /**
785 * Share button handler.
786 * @private
787 */
788 Gallery.prototype.toggleShare_ = function() {
789 if (!this.shareButton_.hasAttribute('disabled'))
790 this.shareMenu_.hidden = !this.shareMenu_.hidden;
791 this.inactivityWatcher_.check();
792 };
793
794 /**
795 * Updates available actions list based on the currently selected urls.
796 * @private.
797 */
798 Gallery.prototype.updateShareMenu_ = function() {
799 var urls = this.getSelectedUrls();
800
801 function isShareAction(task) {
802 var taskParts = task.taskId.split('|');
803 return taskParts[0] != chrome.runtime.id;
804 }
805
806 var api = Gallery.getFileBrowserPrivate();
807 var mimeTypes = []; // TODO(kaznacheev) Collect mime types properly.
808
809 var createShareMenu = function(tasks) {
810 var wasHidden = this.shareMenu_.hidden;
811 this.shareMenu_.hidden = true;
812 var items = this.shareMenu_.querySelectorAll('.item');
813 for (var i = 0; i != items.length; i++) {
814 items[i].parentNode.removeChild(items[i]);
815 }
816
817 for (var t = 0; t != tasks.length; t++) {
818 var task = tasks[t];
819 if (!isShareAction(task)) continue;
820
821 var item = util.createChild(this.shareMenu_, 'item');
822 item.textContent = task.title;
823 item.style.backgroundImage = 'url(' + task.iconUrl + ')';
824 item.addEventListener('click', function(taskId) {
825 this.toggleShare_(); // Hide the menu.
826 this.executeWhenReady(api.executeTask.bind(api, taskId, urls));
827 }.bind(this, task.taskId));
828 }
829
830 var empty = this.shareMenu_.querySelector('.item') == null;
831 ImageUtil.setAttribute(this.shareButton_, 'disabled', empty);
832 this.shareMenu_.hidden = wasHidden || empty;
833 }.bind(this);
834
835 // Create or update the share menu with a list of sharing tasks and show
836 // or hide the share button.
837 if (!urls.length)
838 createShareMenu([]); // Empty list of tasks, since there is no selection.
839 else
840 api.getFileTasks(urls, mimeTypes, createShareMenu);
841 };
842
843 /**
844 * Updates thumbnails.
845 * @private
846 */
847 Gallery.prototype.updateThumbnails_ = function() {
848 if (this.currentMode_ == this.slideMode_)
849 this.slideMode_.updateThumbnails();
850
851 if (this.mosaicMode_) {
852 var mosaic = this.mosaicMode_.getMosaic();
853 if (mosaic.isInitialized())
854 mosaic.reload();
855 }
856 };
857
858 /**
859 * Updates buttons.
860 * @private
861 */
862 Gallery.prototype.updateButtons_ = function() {
863 if (this.modeButton_) {
864 var oppositeMode =
865 this.currentMode_ == this.slideMode_ ? this.mosaicMode_ :
866 this.slideMode_;
867 this.modeButton_.title =
868 this.displayStringFunction_(oppositeMode.getTitle());
869 }
870 };
OLDNEW
« no previous file with comments | « chrome/browser/resources/file_manager/js/path_util.js ('k') | chrome/browser/resources/file_manager/js/photo/gallery_item.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698