Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / ui / file_manager / file_manager / foreground / js / file_manager_commands.js
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 /**
6  * TODO(dzvorygin): Here we use this hack, since 'hidden' is standard
7  * attribute and we can't use it's setter as usual.
8  * @param {boolean} value New value of hidden property.
9  */
10 cr.ui.Command.prototype.setHidden = function(value) {
11   this.__lookupSetter__('hidden').call(this, value);
12 };
13
14 /**
15  * A command.
16  * @interface
17  */
18 var Command = function() {};
19
20 /**
21  * Handles the execute event.
22  * @param {!Event} event Command event.
23  * @param {!FileManager} fileManager FileManager.
24  */
25 Command.prototype.execute = function(event, fileManager) {};
26
27 /**
28  * Handles the can execute event.
29  * @param {!Event} event Can execute event.
30  * @param {!FileManager} fileManager FileManager.
31  */
32 Command.prototype.canExecute = function(event, fileManager) {};
33
34 /**
35  * Utility for commands.
36  */
37 var CommandUtil = {};
38
39 /**
40  * Extracts entry on which command event was dispatched.
41  *
42  * @param {EventTarget} element Element which is the command event's target.
43  * @return {Entry} Entry of the found node.
44  */
45 CommandUtil.getCommandEntry = function(element) {
46   if (element instanceof DirectoryTree) {
47     // element is a DirectoryTree.
48     return element.selectedItem ? element.selectedItem.entry : null;
49   } else if (element instanceof DirectoryItem ||
50              element instanceof VolumeItem ||
51              element instanceof ShortcutItem) {
52     // element are sub items in DirectoryTree.
53     return element.entry;
54   } else if (element instanceof cr.ui.List) {
55     // element is a normal List (eg. the file list on the right panel).
56     var entry = element.selectedItem;
57     // Check if it is Entry or not by checking for toURL().
58     return entry && 'toURL' in entry ? entry : null;
59   } else {
60     return null;
61   }
62 };
63
64 /**
65  * Obtains an entry from the give navigation model item.
66  * @param {!NavigationModelItem} item Navigation model item.
67  * @return {Entry} Related entry.
68  * @private
69  */
70 CommandUtil.getEntryFromNavigationModelItem_ = function(item) {
71   if (item.isVolume)
72     return item.volumeInfo.displayRoot;
73   if (item.isShortcut)
74     return item.entry;
75   return null;
76 };
77
78 /**
79  * Checks if command can be executed on drive.
80  * @param {!Event} event Command event to mark.
81  * @param {!FileManager} fileManager FileManager to use.
82  */
83 CommandUtil.canExecuteEnabledOnDriveOnly = function(event, fileManager) {
84   event.canExecute = fileManager.isOnDrive();
85 };
86
87 /**
88  * Sets the command as visible only when the current volume is drive and it's
89  * running as a normal app, not as a modal dialog.
90  * @param {!Event} event Command event to mark.
91  * @param {!FileManager} fileManager FileManager to use.
92  */
93 CommandUtil.canExecuteVisibleOnDriveInNormalAppModeOnly =
94     function(event, fileManager) {
95   var enabled = fileManager.isOnDrive() &&
96       !DialogType.isModal(fileManager.dialogType);
97   event.canExecute = enabled;
98   event.command.setHidden(!enabled);
99 };
100
101 /**
102  * Sets as the command as always enabled.
103  * @param {!Event} event Command event to mark.
104  */
105 CommandUtil.canExecuteAlways = function(event) {
106   event.canExecute = true;
107 };
108
109 /**
110  * Returns a single selected/passed entry or null.
111  * @param {!Event} event Command event.
112  * @param {!FileManager} fileManager FileManager to use.
113  * @return {FileEntry} The entry or null.
114  */
115 CommandUtil.getSingleEntry = function(event, fileManager) {
116   if (event.target.entry) {
117     return event.target.entry;
118   }
119   var selection = fileManager.getSelection();
120   if (selection.totalCount == 1) {
121     return selection.entries[0];
122   }
123   return null;
124 };
125
126 /**
127  * Obtains target entries that can be pinned from the selection.
128  * If directories are included in the selection, it just returns an empty
129  * array to avoid confusing because pinning directory is not supported
130  * currently.
131  *
132  * @return {Array.<Entry>} Target entries.
133  */
134 CommandUtil.getPinTargetEntries = function() {
135   var hasDirectory = false;
136   var results = fileManager.getSelection().entries.filter(function(entry) {
137     hasDirectory = hasDirectory || entry.isDirectory;
138     if (!entry || hasDirectory)
139       return false;
140     var metadata = fileManager.metadataCache_.getCached(entry, 'external');
141     if (!metadata || metadata.hosted)
142       return false;
143     entry.pinned = metadata.pinned;
144     return true;
145   });
146   return hasDirectory ? [] : results;
147 };
148
149 /**
150  * Sets the default handler for the commandId and prevents handling
151  * the keydown events for this command. Not doing that breaks relationship
152  * of original keyboard event and the command. WebKit would handle it
153  * differently in some cases.
154  * @param {Node} node to register command handler on.
155  * @param {string} commandId Command id to respond to.
156  */
157 CommandUtil.forceDefaultHandler = function(node, commandId) {
158   var doc = node.ownerDocument;
159   var command = doc.querySelector('command[id="' + commandId + '"]');
160   node.addEventListener('keydown', function(e) {
161     if (command.matchesEvent(e)) {
162       // Prevent cr.ui.CommandManager of handling it and leave it
163       // for the default handler.
164       e.stopPropagation();
165     }
166   });
167   node.addEventListener('command', function(event) {
168     if (event.command.id !== commandId)
169       return;
170     document.execCommand(event.command.id);
171     event.cancelBubble = true;
172   });
173   node.addEventListener('canExecute', function(event) {
174     if (event.command.id === commandId)
175       event.canExecute = document.queryCommandEnabled(event.command.id);
176   });
177 };
178
179 /**
180  * Default command.
181  * @type {Command}
182  */
183 CommandUtil.defaultCommand = /** @type {Command} */ ({
184   /**
185    * @param {!Event} event Command event.
186    * @param {!FileManager} fileManager FileManager to use.
187    */
188   execute: function(event, fileManager) {
189     fileManager.document.execCommand(event.command.id);
190   },
191   /**
192    * @param {!Event} event Command event.
193    * @param {!FileManager} fileManager FileManager to use.
194    */
195   canExecute: function(event, fileManager) {
196     event.canExecute = fileManager.document.queryCommandEnabled(
197         event.command.id);
198   }
199 });
200
201 /**
202  * Creates the volume switch command with index.
203  * @param {number} index Volume index from 1 to 9.
204  * @return {Command} Volume switch command.
205  */
206 CommandUtil.createVolumeSwitchCommand = function(index) {
207   return /** @type {Command} */ ({
208     /**
209      * @param {!Event} event Command event.
210      * @param {!FileManager} fileManager FileManager to use.
211      */
212     execute: function(event, fileManager) {
213       fileManager.directoryTree.selectByIndex(index - 1);
214     },
215     /**
216      * @param {!Event} event Command event.
217      * @param {!FileManager} fileManager FileManager to use.
218      */
219     canExecute: function(event, fileManager) {
220       event.canExecute = index > 0 &&
221           index <= fileManager.directoryTree.items.length;
222     }
223   });
224 };
225
226 /**
227  * Returns a directory entry when only one entry is selected and it is
228  * directory. Otherwise, returns null.
229  * @param {FileSelection} selection Instance of FileSelection.
230  * @return {?DirectoryEntry} Directory entry which is selected alone.
231  */
232 CommandUtil.getOnlyOneSelectedDirectory = function(selection) {
233   if (!selection)
234     return null;
235   if (selection.totalCount !== 1)
236     return null;
237   if (!selection.entries[0].isDirectory)
238     return null;
239   return selection.entries[0];
240 };
241
242 /**
243  * Handle of the command events.
244  * @param {!FileManager} fileManager FileManager.
245  * @constructor
246  */
247 var CommandHandler = function(fileManager) {
248   /**
249    * FileManager.
250    * @type {!FileManager}
251    * @private
252    */
253   this.fileManager_ = fileManager;
254
255   /**
256    * Command elements.
257    * @type {Object.<string, cr.ui.Command>}
258    * @private
259    */
260   this.commands_ = {};
261
262   Object.seal(this);
263
264   // Decorate command tags in the document.
265   var commands = fileManager.document.querySelectorAll('command');
266   for (var i = 0; i < commands.length; i++) {
267     cr.ui.Command.decorate(commands[i]);
268     this.commands_[commands[i].id] = commands[i];
269   }
270
271   // Register events.
272   fileManager.document.addEventListener('command', this.onCommand_.bind(this));
273   fileManager.document.addEventListener('canExecute',
274                                         this.onCanExecute_.bind(this));
275 };
276
277 /**
278  * Updates the availability of all commands.
279  */
280 CommandHandler.prototype.updateAvailability = function() {
281   for (var id in this.commands_) {
282     this.commands_[id].canExecuteChange();
283   }
284 };
285
286 /**
287  * Checks if the handler should ignore the current event, eg. since there is
288  * a popup dialog currently opened.
289  *
290  * @return {boolean} True if the event should be ignored, false otherwise.
291  * @private
292  */
293 CommandHandler.prototype.shouldIgnoreEvents_ = function() {
294   // Do not handle commands, when a dialog is shown.
295   if (this.fileManager_.document.querySelector('.cr-dialog-container.shown'))
296     return true;
297
298   return false;  // Do not ignore.
299 };
300
301 /**
302  * Handles command events.
303  * @param {!Event} event Command event.
304  * @private
305  */
306 CommandHandler.prototype.onCommand_ = function(event) {
307   if (this.shouldIgnoreEvents_())
308     return;
309   var handler = CommandHandler.COMMANDS_[event.command.id];
310   handler.execute.call(/** @type {Command} */ (this), event, this.fileManager_);
311 };
312
313 /**
314  * Handles canExecute events.
315  * @param {!Event} event Can execute event.
316  * @private
317  */
318 CommandHandler.prototype.onCanExecute_ = function(event) {
319   if (this.shouldIgnoreEvents_())
320     return;
321   var handler = CommandHandler.COMMANDS_[event.command.id];
322   handler.canExecute.call(/** @type {Command} */ (this), event,
323                           this.fileManager_);
324 };
325
326 /**
327  * Commands.
328  * @type {Object.<string, Command>}
329  * @const
330  * @private
331  */
332 CommandHandler.COMMANDS_ = {};
333
334 /**
335  * Unmounts external drive.
336  * @type {Command}
337  */
338 CommandHandler.COMMANDS_['unmount'] = /** @type {Command} */ ({
339   /**
340    * @param {!Event} event Command event.
341    * @param {!FileManager} fileManager The file manager instance.
342    */
343   execute: function(event, fileManager) {
344     var root = CommandUtil.getCommandEntry(event.target);
345     if (!root) {
346       console.warn('unmount command executed on an element which does not ' +
347                    'have corresponding entry.');
348       return;
349     }
350     var errorCallback = function() {
351       fileManager.alert.showHtml('', str('UNMOUNT_FAILED'));
352     };
353     var volumeInfo = fileManager.volumeManager.getVolumeInfo(root);
354     if (!volumeInfo) {
355       errorCallback();
356       return;
357     }
358     fileManager.volumeManager_.unmount(
359         volumeInfo,
360         function() {},
361         errorCallback);
362   },
363   /**
364    * @param {!Event} event Command event.
365    * @this {CommandHandler}
366    */
367   canExecute: function(event, fileManager) {
368     var root = CommandUtil.getCommandEntry(event.target);
369     if (!root)
370       return;
371     var locationInfo = this.fileManager_.volumeManager.getLocationInfo(root);
372     var rootType =
373         locationInfo && locationInfo.isRootEntry && locationInfo.rootType;
374
375     event.canExecute = (rootType == VolumeManagerCommon.RootType.ARCHIVE ||
376                         rootType == VolumeManagerCommon.RootType.REMOVABLE ||
377                         rootType == VolumeManagerCommon.RootType.PROVIDED);
378     event.command.setHidden(!event.canExecute);
379
380     switch (rootType) {
381       case VolumeManagerCommon.RootType.ARCHIVE:
382       case VolumeManagerCommon.RootType.PROVIDED:
383         event.command.label = str('CLOSE_VOLUME_BUTTON_LABEL');
384         break;
385       case VolumeManagerCommon.RootType.REMOVABLE:
386         event.command.label = str('UNMOUNT_DEVICE_BUTTON_LABEL');
387         break;
388     }
389   }
390 });
391
392 /**
393  * Formats external drive.
394  * @type {Command}
395  */
396 CommandHandler.COMMANDS_['format'] = /** @type {Command} */ ({
397   /**
398    * @param {!Event} event Command event.
399    * @param {!FileManager} fileManager The file manager instance.
400    */
401   execute: function(event, fileManager) {
402     var directoryModel = fileManager.directoryModel;
403     var root = CommandUtil.getCommandEntry(event.target);
404     // If an entry is not found from the event target, use the current
405     // directory. This can happen for the format button for unsupported and
406     // unrecognized volumes.
407     if (!root)
408       root = directoryModel.getCurrentDirEntry();
409
410     var volumeInfo = fileManager.volumeManager.getVolumeInfo(root);
411     if (volumeInfo) {
412       fileManager.confirm.show(
413           loadTimeData.getString('FORMATTING_WARNING'),
414           chrome.fileManagerPrivate.formatVolume.bind(null,
415                                                       volumeInfo.volumeId));
416     }
417   },
418   /**
419    * @param {!Event} event Command event.
420    * @param {!FileManager} fileManager The file manager instance.
421    */
422   canExecute: function(event, fileManager) {
423     var directoryModel = fileManager.directoryModel;
424     var root = CommandUtil.getCommandEntry(event.target);
425     // |root| is null for unrecognized volumes. Regard such volumes as writable
426     // so that the format command is enabled.
427     var isReadOnly = root && fileManager.isOnReadonlyDirectory();
428     // See the comment in execute() for why doing this.
429     if (!root)
430       root = directoryModel.getCurrentDirEntry();
431     var location = root && fileManager.volumeManager.getLocationInfo(root);
432     var removable = location && location.rootType ===
433         VolumeManagerCommon.RootType.REMOVABLE;
434     event.canExecute = removable && !isReadOnly;
435     event.command.setHidden(!removable);
436   }
437 });
438
439 /**
440  * Initiates new folder creation.
441  * @type {Command}
442  */
443 CommandHandler.COMMANDS_['new-folder'] = /** @type {Command} */ ({
444   /**
445    * @param {!Event} event Command event.
446    * @param {!FileManager} fileManager FileManager to use.
447    */
448   execute: function(event, fileManager) {
449     fileManager.createNewFolder();
450   },
451   /**
452    * @param {!Event} event Command event.
453    * @param {!FileManager} fileManager FileManager to use.
454    */
455   canExecute: function(event, fileManager) {
456     var directoryModel = fileManager.directoryModel;
457     event.canExecute = !fileManager.isOnReadonlyDirectory() &&
458                        !fileManager.namingController.isRenamingInProgress() &&
459                        !directoryModel.isSearching() &&
460                        !directoryModel.isScanning();
461   }
462 });
463
464 /**
465  * Initiates new window creation.
466  * @type {Command}
467  */
468 CommandHandler.COMMANDS_['new-window'] = /** @type {Command} */ ({
469   /**
470    * @param {!Event} event Command event.
471    * @param {!FileManager} fileManager FileManager to use.
472    */
473   execute: function(event, fileManager) {
474     chrome.fileManagerPrivate.getProfiles(
475         function(profiles, currentId, displayedId) {
476           fileManager.backgroundPage.launchFileManager({
477             currentDirectoryURL: fileManager.getCurrentDirectoryEntry() &&
478                 fileManager.getCurrentDirectoryEntry().toURL(),
479             displayedId: currentId !== displayedId ? displayedId : undefined
480           });
481         });
482   },
483   /**
484    * @param {!Event} event Command event.
485    * @param {!FileManager} fileManager FileManager to use.
486    */
487   canExecute: function(event, fileManager) {
488     event.canExecute =
489         fileManager.getCurrentDirectoryEntry() &&
490         (fileManager.dialogType === DialogType.FULL_PAGE);
491   }
492 });
493
494 /**
495  * Toggles drive sync settings.
496  * @type {Command}
497  */
498 CommandHandler.COMMANDS_['drive-sync-settings'] = /** @type {Command} */ ({
499   /**
500    * @param {!Event} event Command event.
501    * @param {!FileManager} fileManager FileManager to use.
502    */
503   execute: function(event, fileManager) {
504     fileManager.toggleDriveSyncSettings();
505   },
506   /**
507    * @param {!Event} event Command event.
508    * @param {!FileManager} fileManager FileManager to use.
509    */
510   canExecute: function(event, fileManager) {
511     event.canExecute = fileManager.shouldShowDriveSettings() &&
512         fileManager.volumeManager.getDriveConnectionState().
513         hasCellularNetworkAccess;
514     event.command.setHidden(!event.canExecute);
515   }
516 });
517
518 /**
519  * Toggles drive hosted settings.
520  * @type {Command}
521  */
522 CommandHandler.COMMANDS_['drive-hosted-settings'] = /** @type {Command} */ ({
523   /**
524    * @param {!Event} event Command event.
525    * @param {!FileManager} fileManager FileManager to use.
526    */
527   execute: function(event, fileManager) {
528     fileManager.toggleDriveHostedSettings();
529   },
530   /**
531    * @param {!Event} event Command event.
532    * @param {!FileManager} fileManager FileManager to use.
533    */
534   canExecute: function(event, fileManager) {
535     event.canExecute = fileManager.shouldShowDriveSettings();
536     event.command.setHidden(!event.canExecute);
537   }
538 });
539
540 /**
541  * Deletes selected files.
542  * @type {Command}
543  */
544 CommandHandler.COMMANDS_['delete'] = /** @type {Command} */ ({
545   /**
546    * @param {!Event} event Command event.
547    * @param {!FileManager} fileManager FileManager to use.
548    */
549   execute: function(event, fileManager) {
550     var entries = fileManager.getSelection().entries;
551     var message = entries.length == 1 ?
552         strf('GALLERY_CONFIRM_DELETE_ONE', entries[0].name) :
553         strf('GALLERY_CONFIRM_DELETE_SOME', entries.length);
554     fileManager.ui.deleteConfirmDialog.show(message, function() {
555       fileManager.fileOperationManager.deleteEntries(entries);
556     });
557   },
558   /**
559    * @param {!Event} event Command event.
560    * @param {!FileManager} fileManager FileManager to use.
561    */
562   canExecute: function(event, fileManager) {
563     var selection = fileManager.getSelection();
564     event.canExecute = !fileManager.isOnReadonlyDirectory() &&
565                        selection &&
566                        selection.totalCount > 0;
567   }
568 });
569
570 /**
571  * Pastes files from clipboard.
572  * @type {Command}
573  */
574 CommandHandler.COMMANDS_['paste'] = /** @type {Command} */ ({
575   /**
576    * @param {!Event} event Command event.
577    * @param {!FileManager} fileManager FileManager to use.
578    */
579   execute: function(event, fileManager) {
580     fileManager.document.execCommand(event.command.id);
581   },
582   /**
583    * @param {!Event} event Command event.
584    * @param {!FileManager} fileManager FileManager to use.
585    */
586   canExecute: function(event, fileManager) {
587     var fileTransferController = fileManager.fileTransferController;
588     event.canExecute = (fileTransferController &&
589         fileTransferController.queryPasteCommandEnabled());
590     // Hide this command if only one folder is selected.
591     event.command.setHidden(!!CommandUtil.getOnlyOneSelectedDirectory(
592         fileManager.getSelection()));
593   }
594 });
595
596 /**
597  * Pastes files from clipboard into the selected folder.
598  * @type {Command}
599  */
600 CommandHandler.COMMANDS_['paste-into-folder'] = /** @type {Command} */ ({
601   /**
602    * @param {!Event} event Command event.
603    * @param {!FileManager} fileManager FileManager to use.
604    */
605   execute: function(event, fileManager) {
606     var selection = fileManager.getSelection();
607     var dest = CommandUtil.getOnlyOneSelectedDirectory(selection);
608     if (!dest) return;
609
610     // This handler tweaks the Event object for 'paste' event so that
611     // the FileTransferController can distinguish this 'paste-into-folder'
612     // command and know the destination directory.
613     var handler = function(inEvent) {
614       inEvent.destDirectory = dest;
615     };
616     fileManager.document.addEventListener('paste', handler, true);
617     fileManager.document.execCommand('paste');
618     fileManager.document.removeEventListener('paste', handler, true);
619   },
620   /**
621    * @param {!Event} event Command event.
622    * @param {!FileManager} fileManager FileManager to use.
623    */
624   canExecute: function(event, fileManager) {
625     var fileTransferController = fileManager.fileTransferController;
626     event.canExecute = (fileTransferController &&
627         fileTransferController.queryPasteCommandEnabled());
628     // Hide this command unless only one folder is selected.
629     event.command.setHidden(!CommandUtil.getOnlyOneSelectedDirectory(
630         fileManager.getSelection()));
631   }
632 });
633
634 CommandHandler.COMMANDS_['cut'] = CommandUtil.defaultCommand;
635 CommandHandler.COMMANDS_['copy'] = CommandUtil.defaultCommand;
636
637 /**
638  * Initiates file renaming.
639  * @type {Command}
640  */
641 CommandHandler.COMMANDS_['rename'] = /** @type {Command} */ ({
642   /**
643    * @param {!Event} event Command event.
644    * @param {!FileManager} fileManager FileManager to use.
645    */
646   execute: function(event, fileManager) {
647     fileManager.namingController.initiateRename();
648   },
649   /**
650    * @param {!Event} event Command event.
651    * @param {!FileManager} fileManager FileManager to use.
652    */
653   canExecute: function(event, fileManager) {
654     var selection = fileManager.getSelection();
655     event.canExecute = !fileManager.namingController.isRenamingInProgress() &&
656                        !fileManager.isOnReadonlyDirectory() &&
657                        selection &&
658                        selection.totalCount == 1;
659   }
660 });
661
662 /**
663  * Opens drive help.
664  * @type {Command}
665  */
666 CommandHandler.COMMANDS_['volume-help'] = /** @type {Command} */ ({
667   /**
668    * @param {!Event} event Command event.
669    * @param {!FileManager} fileManager FileManager to use.
670    */
671   execute: function(event, fileManager) {
672     if (fileManager.isOnDrive())
673       util.visitURL(str('GOOGLE_DRIVE_HELP_URL'));
674     else
675       util.visitURL(str('FILES_APP_HELP_URL'));
676   },
677   /**
678    * @param {!Event} event Command event.
679    * @param {!FileManager} fileManager FileManager to use.
680    */
681   canExecute: function(event, fileManager) {
682     // Hides the help menu in modal dialog mode. It does not make much sense
683     // because after all, users cannot view the help without closing, and
684     // besides that the help page is about Files.app as an app, not about the
685     // dialog mode itself. It can also lead to hard-to-fix bug crbug.com/339089.
686     var hideHelp = DialogType.isModal(fileManager.dialogType);
687     event.canExecute = !hideHelp;
688     event.command.setHidden(hideHelp);
689     fileManager.document_.getElementById('help-separator').hidden = hideHelp;
690   }
691 });
692
693 /**
694  * Opens drive buy-more-space url.
695  * @type {Command}
696  */
697 CommandHandler.COMMANDS_['drive-buy-more-space'] = /** @type {Command} */ ({
698   /**
699    * @param {!Event} event Command event.
700    * @param {!FileManager} fileManager FileManager to use.
701    */
702   execute: function(event, fileManager) {
703     util.visitURL(str('GOOGLE_DRIVE_BUY_STORAGE_URL'));
704   },
705   canExecute: CommandUtil.canExecuteVisibleOnDriveInNormalAppModeOnly
706 });
707
708 /**
709  * Opens drive.google.com.
710  * @type {Command}
711  */
712 CommandHandler.COMMANDS_['drive-go-to-drive'] = /** @type {Command} */ ({
713   /**
714    * @param {!Event} event Command event.
715    * @param {!FileManager} fileManager FileManager to use.
716    */
717   execute: function(event, fileManager) {
718     util.visitURL(str('GOOGLE_DRIVE_ROOT_URL'));
719   },
720   canExecute: CommandUtil.canExecuteVisibleOnDriveInNormalAppModeOnly
721 });
722
723 /**
724  * Displays open with dialog for current selection.
725  * @type {Command}
726  */
727 CommandHandler.COMMANDS_['open-with'] = /** @type {Command} */ ({
728   /**
729    * @param {!Event} event Command event.
730    * @param {!FileManager} fileManager FileManager to use.
731    */
732   execute: function(event, fileManager) {
733     var tasks = fileManager.getSelection().tasks;
734     if (tasks) {
735       tasks.showTaskPicker(fileManager.defaultTaskPicker,
736           str('OPEN_WITH_BUTTON_LABEL'),
737           '',
738           function(task) {
739             tasks.execute(task.taskId);
740           },
741           false);
742     }
743   },
744   /**
745    * @param {!Event} event Command event.
746    * @param {!FileManager} fileManager FileManager to use.
747    */
748   canExecute: function(event, fileManager) {
749     var tasks = fileManager.getSelection().tasks;
750     event.canExecute = tasks && tasks.size() > 1;
751   }
752 });
753
754 /**
755  * Focuses search input box.
756  * @type {Command}
757  */
758 CommandHandler.COMMANDS_['search'] = /** @type {Command} */ ({
759   /**
760    * @param {!Event} event Command event.
761    * @param {!FileManager} fileManager FileManager to use.
762    */
763   execute: function(event, fileManager) {
764     var element = fileManager.document.querySelector('#search-box input');
765     element.focus();
766     element.select();
767   },
768   /**
769    * @param {!Event} event Command event.
770    * @param {!FileManager} fileManager FileManager to use.
771    */
772   canExecute: function(event, fileManager) {
773     event.canExecute = !fileManager.namingController.isRenamingInProgress();
774   }
775 });
776
777 /**
778  * Activates the n-th volume.
779  * @type {Command}
780  */
781 CommandHandler.COMMANDS_['volume-switch-1'] =
782     CommandUtil.createVolumeSwitchCommand(1);
783 CommandHandler.COMMANDS_['volume-switch-2'] =
784     CommandUtil.createVolumeSwitchCommand(2);
785 CommandHandler.COMMANDS_['volume-switch-3'] =
786     CommandUtil.createVolumeSwitchCommand(3);
787 CommandHandler.COMMANDS_['volume-switch-4'] =
788     CommandUtil.createVolumeSwitchCommand(4);
789 CommandHandler.COMMANDS_['volume-switch-5'] =
790     CommandUtil.createVolumeSwitchCommand(5);
791 CommandHandler.COMMANDS_['volume-switch-6'] =
792     CommandUtil.createVolumeSwitchCommand(6);
793 CommandHandler.COMMANDS_['volume-switch-7'] =
794     CommandUtil.createVolumeSwitchCommand(7);
795 CommandHandler.COMMANDS_['volume-switch-8'] =
796     CommandUtil.createVolumeSwitchCommand(8);
797 CommandHandler.COMMANDS_['volume-switch-9'] =
798     CommandUtil.createVolumeSwitchCommand(9);
799
800 /**
801  * Flips 'available offline' flag on the file.
802  * @type {Command}
803  */
804 CommandHandler.COMMANDS_['toggle-pinned'] = /** @type {Command} */ ({
805   /**
806    * @param {!Event} event
807    * @param {!FileManager} fileManager
808    */
809   execute: function(event, fileManager) {
810     var pin = !event.command.checked;
811     event.command.checked = pin;
812     var entries = CommandUtil.getPinTargetEntries();
813     if (entries.length == 0)
814       return;
815     var currentEntry;
816     var error = false;
817     var steps = {
818       // Pick an entry and pin it.
819       start: function() {
820         // Check if all the entries are pinned or not.
821         if (entries.length == 0)
822           return;
823         currentEntry = entries.shift();
824         chrome.fileManagerPrivate.pinDriveFile(
825             currentEntry.toURL(),
826             pin,
827             steps.entryPinned);
828       },
829
830       // Check the result of pinning
831       entryPinned: function() {
832         // Convert to boolean.
833         error = !!chrome.runtime.lastError;
834         if (error && pin) {
835           fileManager.metadataCache_.getOne(
836               currentEntry, 'filesystem', steps.showError);
837         }
838         fileManager.metadataCache_.clear(currentEntry, 'external');
839         fileManager.metadataCache_.getOne(
840             currentEntry, 'external', steps.updateUI.bind(this));
841       },
842
843       // Update the user interface according to the cache state.
844       updateUI: function(drive /* not used */) {
845         fileManager.ui.listContainer.currentView.updateListItemsMetadata(
846             'external', [currentEntry]);
847         if (!error)
848           steps.start();
849       },
850
851       // Show the error
852       showError: function(filesystem) {
853         fileManager.alert.showHtml(str('DRIVE_OUT_OF_SPACE_HEADER'),
854                                    strf('DRIVE_OUT_OF_SPACE_MESSAGE',
855                                         unescape(currentEntry.name),
856                                         util.bytesToString(filesystem.size)));
857       }
858     };
859     steps.start();
860
861     var driveSyncHandler =
862         fileManager.backgroundPage.background.driveSyncHandler;
863     if (pin && driveSyncHandler.isSyncSuppressed())
864       driveSyncHandler.showDisabledMobileSyncNotification();
865   },
866
867   /**
868    * @param {!Event} event Command event.
869    * @param {!FileManager} fileManager FileManager to use.
870    */
871   canExecute: function(event, fileManager) {
872     var entries = CommandUtil.getPinTargetEntries();
873     var checked = true;
874     for (var i = 0; i < entries.length; i++) {
875       checked = checked && entries[i].pinned;
876     }
877     if (entries.length > 0) {
878       event.canExecute = true;
879       event.command.setHidden(false);
880       event.command.checked = checked;
881     } else {
882       event.canExecute = false;
883       event.command.setHidden(true);
884     }
885   }
886 });
887
888 /**
889  * Creates zip file for current selection.
890  * @type {Command}
891  */
892 CommandHandler.COMMANDS_['zip-selection'] = /** @type {Command} */ ({
893   /**
894    * @param {!Event} event Command event.
895    * @param {!FileManager} fileManager FileManager to use.
896    */
897   execute: function(event, fileManager) {
898     var dirEntry = fileManager.getCurrentDirectoryEntry();
899     if (!dirEntry)
900       return;
901     var selectionEntries = fileManager.getSelection().entries;
902     fileManager.fileOperationManager_.zipSelection(
903         dirEntry, selectionEntries);
904   },
905   /**
906    * @param {!Event} event Command event.
907    * @param {!FileManager} fileManager FileManager to use.
908    */
909   canExecute: function(event, fileManager) {
910     var dirEntry = fileManager.getCurrentDirectoryEntry();
911     var selection = fileManager.getSelection();
912     event.canExecute =
913         dirEntry &&
914         !fileManager.isOnReadonlyDirectory() &&
915         !fileManager.isOnDrive() &&
916         selection && selection.totalCount > 0;
917   }
918 });
919
920 /**
921  * Shows the share dialog for the current selection (single only).
922  * @type {Command}
923  */
924 CommandHandler.COMMANDS_['share'] = /** @type {Command} */ ({
925   /**
926    * @param {!Event} event Command event.
927    * @param {!FileManager} fileManager FileManager to use.
928    */
929   execute: function(event, fileManager) {
930     fileManager.shareSelection();
931   },
932   /**
933    * @param {!Event} event Command event.
934    * @param {!FileManager} fileManager FileManager to use.
935    */
936   canExecute: function(event, fileManager) {
937     var selection = fileManager.getSelection();
938     var isDriveOffline =
939         fileManager.volumeManager.getDriveConnectionState().type ===
940             VolumeManagerCommon.DriveConnectionType.OFFLINE;
941     event.canExecute = fileManager.isOnDrive() &&
942         !isDriveOffline &&
943         selection && selection.totalCount == 1;
944     event.command.setHidden(!fileManager.isOnDrive());
945   }
946 });
947
948 /**
949  * Creates a shortcut of the selected folder (single only).
950  * @type {Command}
951  */
952 CommandHandler.COMMANDS_['create-folder-shortcut'] = /** @type {Command} */ ({
953   /**
954    * @param {!Event} event Command event.
955    * @param {!FileManager} fileManager The file manager instance.
956    */
957   execute: function(event, fileManager) {
958     var entry = CommandUtil.getCommandEntry(event.target);
959     if (!entry) {
960       console.warn('create-folder-shortcut command executed on an element ' +
961                    'which does not have corresponding entry.');
962       return;
963     }
964     fileManager.createFolderShortcut(entry);
965   },
966
967   /**
968    * @param {!Event} event Command event.
969    * @param {!FileManager} fileManager The file manager instance.
970    */
971   canExecute: function(event, fileManager) {
972     var entry = CommandUtil.getCommandEntry(event.target);
973     var folderShortcutExists = entry &&
974                                fileManager.folderShortcutExists(entry);
975
976     var onlyOneFolderSelected = true;
977     // Only on list, user can select multiple files. The command is enabled only
978     // when a single file is selected.
979     if (event.target instanceof cr.ui.List) {
980       var items = event.target.selectedItems;
981       onlyOneFolderSelected = (items.length == 1 && items[0].isDirectory);
982     }
983
984     var location = entry && fileManager.volumeManager.getLocationInfo(entry);
985     var eligible = location && location.isEligibleForFolderShortcut;
986     event.canExecute =
987         eligible && onlyOneFolderSelected && !folderShortcutExists;
988     event.command.setHidden(!eligible || !onlyOneFolderSelected);
989   }
990 });
991
992 /**
993  * Removes the folder shortcut.
994  * @type {Command}
995  */
996 CommandHandler.COMMANDS_['remove-folder-shortcut'] = /** @type {Command} */ ({
997   /**
998    * @param {!Event} event Command event.
999    * @param {!FileManager} fileManager The file manager instance.
1000    */
1001   execute: function(event, fileManager) {
1002     var entry = CommandUtil.getCommandEntry(event.target);
1003     if (!entry) {
1004       console.warn('remove-folder-shortcut command executed on an element ' +
1005                    'which does not have corresponding entry.');
1006       return;
1007     }
1008     fileManager.removeFolderShortcut(entry);
1009   },
1010
1011   /**
1012    * @param {!Event} event Command event.
1013    * @param {!FileManager} fileManager The file manager instance.
1014    */
1015   canExecute: function(event, fileManager) {
1016     var entry = CommandUtil.getCommandEntry(event.target);
1017     var location = entry && fileManager.volumeManager.getLocationInfo(entry);
1018
1019     var eligible = location && location.isEligibleForFolderShortcut;
1020     var isShortcut = entry && fileManager.folderShortcutExists(entry);
1021     event.canExecute = isShortcut && eligible;
1022     event.command.setHidden(!event.canExecute);
1023   }
1024 });
1025
1026 /**
1027  * Zoom in to the Files.app.
1028  * @type {Command}
1029  */
1030 CommandHandler.COMMANDS_['zoom-in'] = /** @type {Command} */ ({
1031   /**
1032    * @param {!Event} event Command event.
1033    * @param {!FileManager} fileManager FileManager to use.
1034    */
1035   execute: function(event, fileManager) {
1036     chrome.fileManagerPrivate.zoom('in');
1037   },
1038   canExecute: CommandUtil.canExecuteAlways
1039 });
1040
1041 /**
1042  * Zoom out from the Files.app.
1043  * @type {Command}
1044  */
1045 CommandHandler.COMMANDS_['zoom-out'] = /** @type {Command} */ ({
1046   /**
1047    * @param {!Event} event Command event.
1048    * @param {!FileManager} fileManager FileManager to use.
1049    */
1050   execute: function(event, fileManager) {
1051     chrome.fileManagerPrivate.zoom('out');
1052   },
1053   canExecute: CommandUtil.canExecuteAlways
1054 });
1055
1056 /**
1057  * Reset the zoom factor.
1058  * @type {Command}
1059  */
1060 CommandHandler.COMMANDS_['zoom-reset'] = /** @type {Command} */ ({
1061   /**
1062    * @param {!Event} event Command event.
1063    * @param {!FileManager} fileManager FileManager to use.
1064    */
1065   execute: function(event, fileManager) {
1066     chrome.fileManagerPrivate.zoom('reset');
1067   },
1068   canExecute: CommandUtil.canExecuteAlways
1069 });
1070
1071 /**
1072  * Open inspector for foreground page.
1073  * @type {Command}
1074  */
1075 CommandHandler.COMMANDS_['inspect-normal'] = /** @type {Command} */ ({
1076   /**
1077    * @param {!Event} event Command event.
1078    * @param {!FileManager} fileManager FileManager to use.
1079    */
1080   execute: function(event, fileManager) {
1081     chrome.fileManagerPrivate.openInspector('normal');
1082   },
1083   canExecute: CommandUtil.canExecuteAlways
1084 });
1085
1086 /**
1087  * Open inspector for foreground page and bring focus to the console.
1088  * @type {Command}
1089  */
1090 CommandHandler.COMMANDS_['inspect-console'] = /** @type {Command} */ ({
1091   /**
1092    * @param {!Event} event Command event.
1093    * @param {!FileManager} fileManager FileManager to use.
1094    */
1095   execute: function(event, fileManager) {
1096     chrome.fileManagerPrivate.openInspector('console');
1097   },
1098   canExecute: CommandUtil.canExecuteAlways
1099 });
1100
1101 /**
1102  * Open inspector for foreground page in inspect element mode.
1103  * @type {Command}
1104  */
1105 CommandHandler.COMMANDS_['inspect-element'] = /** @type {Command} */ ({
1106   /**
1107    * @param {!Event} event Command event.
1108    * @param {!FileManager} fileManager FileManager to use.
1109    */
1110   execute: function(event, fileManager) {
1111     chrome.fileManagerPrivate.openInspector('element');
1112   },
1113   canExecute: CommandUtil.canExecuteAlways
1114 });
1115
1116 /**
1117  * Open inspector for background page.
1118  * @type {Command}
1119  */
1120 CommandHandler.COMMANDS_['inspect-background'] = /** @type {Command} */ ({
1121   /**
1122    * @param {!Event} event Command event.
1123    * @param {!FileManager} fileManager FileManager to use.
1124    */
1125   execute: function(event, fileManager) {
1126     chrome.fileManagerPrivate.openInspector('background');
1127   },
1128   canExecute: CommandUtil.canExecuteAlways
1129 });