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

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

Issue 8554003: [filebrowser] Add left panel with roots. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years 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
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // Setting the src of an img to an empty string can crash the browser, so we 5 // Setting the src of an img to an empty string can crash the browser, so we
6 // use an empty 1x1 gif instead. 6 // use an empty 1x1 gif instead.
7 const EMPTY_IMAGE_URI = 'data:image/gif;base64,' 7 const EMPTY_IMAGE_URI = 'data:image/gif;base64,'
8 + 'R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw%3D%3D'; 8 + 'R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw%3D%3D';
9 9
10 // If directory files changes too often, don't rescan directory more than once 10 // If directory files changes too often, don't rescan directory more than once
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
54 metrics.recordEnum('Create', this.dialogType_, 54 metrics.recordEnum('Create', this.dialogType_,
55 [FileManager.DialogType.SELECT_FOLDER, 55 [FileManager.DialogType.SELECT_FOLDER,
56 FileManager.DialogType.SELECT_SAVEAS_FILE, 56 FileManager.DialogType.SELECT_SAVEAS_FILE,
57 FileManager.DialogType.SELECT_OPEN_FILE, 57 FileManager.DialogType.SELECT_OPEN_FILE,
58 FileManager.DialogType.SELECT_OPEN_MULTI_FILE, 58 FileManager.DialogType.SELECT_OPEN_MULTI_FILE,
59 FileManager.DialogType.FULL_PAGE]); 59 FileManager.DialogType.FULL_PAGE]);
60 60
61 // TODO(dgozman): This will be changed to LocaleInfo. 61 // TODO(dgozman): This will be changed to LocaleInfo.
62 this.locale_ = new v8Locale(navigator.language); 62 this.locale_ = new v8Locale(navigator.language);
63 63
64 this.resolveRoots_(); 64 this.initFileSystem_();
65 this.initDom_(); 65 this.initDom_();
66 this.initDialogType_(); 66 this.initDialogType_();
67 this.dialogDom_.style.opacity = '1'; 67 this.dialogDom_.style.opacity = '1';
68 } 68 }
69 69
70 FileManager.prototype = { 70 FileManager.prototype = {
71 __proto__: cr.EventTarget.prototype 71 __proto__: cr.EventTarget.prototype
72 }; 72 };
73 73
74 // Anonymous "namespace". 74 // Anonymous "namespace".
(...skipping 418 matching lines...) Expand 10 before | Expand all | Expand 10 after
493 chrome.fileBrowserPrivate.getStrings(function(strings) { 493 chrome.fileBrowserPrivate.getStrings(function(strings) {
494 localStrings = new LocalStrings(strings); 494 localStrings = new LocalStrings(strings);
495 if (callback) 495 if (callback)
496 callback(); 496 callback();
497 }); 497 });
498 }; 498 };
499 499
500 // Instance methods. 500 // Instance methods.
501 501
502 /** 502 /**
503 * Request file system and get root entries asynchronously. Invokes init_ 503 * Request local file system, resolve roots and init_ after that.
504 * when have finished. 504 * @private
505 */ 505 */
506 FileManager.prototype.resolveRoots_ = function(callback) { 506 FileManager.prototype.initFileSystem_ = function() {
507 var rootPaths = ['Downloads', 'removable', 'archive']; 507 util.installFileErrorToString();
508 metrics.startInterval('Load.FileSystem');
508 509
509 metrics.startInterval('Load.FileSystem');
510 var self = this; 510 var self = this;
511 511
512 // The list of active mount points to distinct them from other directories. 512 // The list of active mount points to distinct them from other directories.
513 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { 513 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) {
514 self.mountPoints_ = mountPoints; 514 self.mountPoints_ = mountPoints;
515 onDone(); 515 onDone();
516 }); 516 });
517 517
518 function onDone() { 518 function onDone() {
519 if (self.mountPoints_ && self.rootEntries_) 519 if (self.mountPoints_ && self.rootEntries_)
520 self.init_(); 520 self.init_();
521 } 521 }
522 522
523 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) { 523 chrome.fileBrowserPrivate.requestLocalFileSystem(function(filesystem) {
524 self.filesystem_ = filesystem;
525 util.installFileErrorToString();
526
527 metrics.recordInterval('Load.FileSystem'); 524 metrics.recordInterval('Load.FileSystem');
528 525
529 var rootEntries = []; 526 self.filesystem_ = filesystem;
530 527 self.resolveRoots_(function(rootEntries) {
531 function onAllRootsFound() {
532 metrics.recordInterval('Load.Roots');
533 self.rootEntries_ = rootEntries; 528 self.rootEntries_ = rootEntries;
534 onDone(); 529 onDone();
535 } 530 });
536
537 function onPathError(path, err) {
538 console.error('Error locating root path: ' + path + ': ' + err);
539 }
540
541 function onEntryFound(entry) {
542 if (entry) {
543 rootEntries.push(entry);
544 } else {
545 onAllRootsFound();
546 }
547 }
548
549 metrics.startInterval('Load.Roots');
550 if (filesystem.name.match(/^chrome-extension_\S+:external/i)) {
551 // We've been handed the local filesystem, whose root directory
552 // cannot be enumerated.
553 util.getDirectories(filesystem.root, {create: false}, rootPaths,
554 onEntryFound, onPathError);
555 } else {
556 util.forEachDirEntry(filesystem.root, onEntryFound);
557 }
558 }); 531 });
559 }; 532 };
560 533
561 /** 534 /**
535 * Get root entries asynchronously. Invokes callback
536 * when have finished.
537 */
538 FileManager.prototype.resolveRoots_ = function(callback) {
539 var rootPaths = [DOWNLOADS_DIRECTORY, ARCHIVE_DIRECTORY,
540 REMOVABLE_DIRECTORY].map(function(s) { return s.substring(1); });
541 var rootEntries = [];
542
543 // The number of entries left to enumerate to get all roots.
544 // When equals to zero, we are done.
545 var entriesToEnumerate = 0;
546 // Entries may be enumerated faster than next one appears, so we have this
547 // guard to not finish too early.
548 var allEntriesFound = false;
549 var done = false;
550
551 function onDone() {
552 if (done) return;
553 done = true;
554 metrics.recordInterval('Load.Roots');
555 callback(rootEntries);
556 }
557
558 function onPathError(path, err) {
559 console.error('Error locating root path: ' + path + ': ' + err);
560 }
561
562 function onRootFound(root) {
563 if (root) {
564 rootEntries.push(root);
565 } else {
566 entriesToEnumerate--;
567 if (entriesToEnumerate == 0 && allEntriesFound)
568 onDone();
569 }
570 }
571
572 function onEntryFound(entry) {
573 if (entry) {
574 entriesToEnumerate++;
575 var path = entry.fullPath;
576 if (path == ARCHIVE_DIRECTORY || path == REMOVABLE_DIRECTORY) {
577 // All removable devices and mounted archives are considered
578 // roots, and are shown in the sidebar.
579 util.forEachDirEntry(entry, onRootFound);
580 } else {
581 onRootFound(entry);
582 onRootFound(null);
583 }
584 } else {
585 allEntriesFound = true;
586 if (entriesToEnumerate == 0)
587 onDone();
588 }
589 }
590
591 metrics.startInterval('Load.Roots');
592 if (this.filesystem_.name.match(/^chrome-extension_\S+:external/i)) {
593 // We've been handed the local filesystem, whose root directory
594 // cannot be enumerated.
595 util.getDirectories(this.filesystem_.root, {create: false}, rootPaths,
596 onEntryFound, onPathError);
597 } else {
598 util.forEachDirEntry(this.filesystem_.root, onEntryFound);
599 }
600 };
601
602 /**
562 * Continue initializing the file manager after resolving roots. 603 * Continue initializing the file manager after resolving roots.
563 */ 604 */
564 FileManager.prototype.init_ = function() { 605 FileManager.prototype.init_ = function() {
565 metrics.startInterval('Load.DOM'); 606 metrics.startInterval('Load.DOM');
607 this.initCommands_();
566 608
567 // TODO(rginda): 6/22/11: Remove this test when createDateTimeFormat is 609 // TODO(rginda): 6/22/11: Remove this test when createDateTimeFormat is
568 // available in all chrome trunk builds. 610 // available in all chrome trunk builds.
569 if ('createDateTimeFormat' in this.locale_) { 611 if ('createDateTimeFormat' in this.locale_) {
570 this.shortDateFormatter_ = 612 this.shortDateFormatter_ =
571 this.locale_.createDateTimeFormat({'dateType': 'medium'}); 613 this.locale_.createDateTimeFormat({'dateType': 'medium'});
572 } else { 614 } else {
573 this.shortDateFormatter_ = { 615 this.shortDateFormatter_ = {
574 format: function(d) { 616 format: function(d) {
575 return (d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getFullYear(); 617 return (d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getFullYear();
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
613 window.addEventListener('unload', this.onUnload_.bind(this)); 655 window.addEventListener('unload', this.onUnload_.bind(this));
614 656
615 this.addEventListener('directory-changed', 657 this.addEventListener('directory-changed',
616 this.onDirectoryChanged_.bind(this)); 658 this.onDirectoryChanged_.bind(this));
617 this.addEventListener('selection-summarized', 659 this.addEventListener('selection-summarized',
618 this.onSelectionSummarized_.bind(this)); 660 this.onSelectionSummarized_.bind(this));
619 661
620 // The list of archives requested to mount. We will show contents once 662 // The list of archives requested to mount. We will show contents once
621 // archive is mounted, but only for mounts from within this filebrowser tab. 663 // archive is mounted, but only for mounts from within this filebrowser tab.
622 this.mountRequests_ = []; 664 this.mountRequests_ = [];
665 this.unmountRequests_ = [];
623 chrome.fileBrowserPrivate.onMountCompleted.addListener( 666 chrome.fileBrowserPrivate.onMountCompleted.addListener(
624 this.onMountCompleted_.bind(this)); 667 this.onMountCompleted_.bind(this));
625 668
626 chrome.fileBrowserPrivate.onFileChanged.addListener( 669 chrome.fileBrowserPrivate.onFileChanged.addListener(
627 this.onFileChanged_.bind(this)); 670 this.onFileChanged_.bind(this));
628 671
629 var self = this; 672 var self = this;
630 673
631 // The list of callbacks to be invoked during the directory rescan after 674 // The list of callbacks to be invoked during the directory rescan after
632 // all paste tasks are complete. 675 // all paste tasks are complete.
633 this.pasteSuccessCallbacks_ = []; 676 this.pasteSuccessCallbacks_ = [];
634 677
635 this.initCommands_();
636
637 this.setupCurrentDirectory_(); 678 this.setupCurrentDirectory_();
638 679
639 this.summarizeSelection_(); 680 this.summarizeSelection_();
640 681
641 this.dataModel_.sort('cachedMtime_', 'desc'); 682 this.dataModel_.sort('cachedMtime_', 'desc');
642 683
643 this.refocus(); 684 this.refocus();
644 685
645 this.createMetadataProvider_(); 686 this.createMetadataProvider_();
646 metrics.recordInterval('Load.DOM'); 687 metrics.recordInterval('Load.DOM');
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
685 // Cache nodes we'll be manipulating. 726 // Cache nodes we'll be manipulating.
686 this.previewThumbnails_ = 727 this.previewThumbnails_ =
687 this.dialogDom_.querySelector('.preview-thumbnails'); 728 this.dialogDom_.querySelector('.preview-thumbnails');
688 this.previewPanel_ = this.dialogDom_.querySelector('.preview-panel'); 729 this.previewPanel_ = this.dialogDom_.querySelector('.preview-panel');
689 this.previewFilename_ = this.dialogDom_.querySelector('.preview-filename'); 730 this.previewFilename_ = this.dialogDom_.querySelector('.preview-filename');
690 this.previewSummary_ = this.dialogDom_.querySelector('.preview-summary'); 731 this.previewSummary_ = this.dialogDom_.querySelector('.preview-summary');
691 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input'); 732 this.filenameInput_ = this.dialogDom_.querySelector('.filename-input');
692 this.taskButtons_ = this.dialogDom_.querySelector('.task-buttons'); 733 this.taskButtons_ = this.dialogDom_.querySelector('.task-buttons');
693 this.okButton_ = this.dialogDom_.querySelector('.ok'); 734 this.okButton_ = this.dialogDom_.querySelector('.ok');
694 this.cancelButton_ = this.dialogDom_.querySelector('.cancel'); 735 this.cancelButton_ = this.dialogDom_.querySelector('.cancel');
695 this.newFolderButton_ = this.dialogDom_.querySelector('.new-folder');
696 this.deleteButton_ = this.dialogDom_.querySelector('.delete-button'); 736 this.deleteButton_ = this.dialogDom_.querySelector('.delete-button');
697 737
698 this.downloadsWarning_ = 738 this.downloadsWarning_ =
699 this.dialogDom_.querySelector('.downloads-warning'); 739 this.dialogDom_.querySelector('.downloads-warning');
700 var html = util.htmlUnescape(str('DOWNLOADS_DIRECTORY_WARNING')); 740 var html = util.htmlUnescape(str('DOWNLOADS_DIRECTORY_WARNING'));
701 this.downloadsWarning_.lastElementChild.innerHTML = html; 741 this.downloadsWarning_.lastElementChild.innerHTML = html;
702 var link = this.downloadsWarning_.querySelector('a'); 742 var link = this.downloadsWarning_.querySelector('a');
703 link.addEventListener('click', this.onDownloadsWarningClick_.bind(this)); 743 link.addEventListener('click', this.onDownloadsWarningClick_.bind(this));
704 744
705 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this)); 745 this.document_.addEventListener('keydown', this.onKeyDown_.bind(this));
(...skipping 10 matching lines...) Expand all
716 'keyup', this.onFilenameInputKeyUp_.bind(this)); 756 'keyup', this.onFilenameInputKeyUp_.bind(this));
717 this.filenameInput_.addEventListener( 757 this.filenameInput_.addEventListener(
718 'focus', this.onFilenameInputFocus_.bind(this)); 758 'focus', this.onFilenameInputFocus_.bind(this));
719 759
720 var listContainer = this.dialogDom_.querySelector('.list-container'); 760 var listContainer = this.dialogDom_.querySelector('.list-container');
721 listContainer.addEventListener('keydown', this.onListKeyDown_.bind(this)); 761 listContainer.addEventListener('keydown', this.onListKeyDown_.bind(this));
722 listContainer.addEventListener('keypress', this.onListKeyPress_.bind(this)); 762 listContainer.addEventListener('keypress', this.onListKeyPress_.bind(this));
723 this.okButton_.addEventListener('click', this.onOk_.bind(this)); 763 this.okButton_.addEventListener('click', this.onOk_.bind(this));
724 this.cancelButton_.addEventListener('click', this.onCancel_.bind(this)); 764 this.cancelButton_.addEventListener('click', this.onCancel_.bind(this));
725 765
726 this.dialogDom_.querySelector('button.new-folder').addEventListener( 766 this.dialogDom_.querySelector('div.open-sidebar').addEventListener(
727 'click', this.onNewFolderButtonClick_.bind(this)); 767 'click', this.onToggleSidebar_.bind(this));
768 this.dialogDom_.querySelector('div.close-sidebar').addEventListener(
769 'click', this.onToggleSidebar_.bind(this));
770 this.dialogContainer_ = this.dialogDom_.querySelector('.dialog-container');
728 771
729 this.dialogDom_.querySelector('button.detail-view').addEventListener( 772 this.dialogDom_.querySelector('button.detail-view').addEventListener(
730 'click', this.onDetailViewButtonClick_.bind(this)); 773 'click', this.onDetailViewButtonClick_.bind(this));
731 this.dialogDom_.querySelector('button.thumbnail-view').addEventListener( 774 this.dialogDom_.querySelector('button.thumbnail-view').addEventListener(
732 'click', this.onThumbnailViewButtonClick_.bind(this)); 775 'click', this.onThumbnailViewButtonClick_.bind(this));
733 776
734 this.dialogDom_.ownerDocument.defaultView.addEventListener( 777 this.dialogDom_.ownerDocument.defaultView.addEventListener(
735 'resize', this.onResize_.bind(this)); 778 'resize', this.onResize_.bind(this));
736 779
737 var ary = this.dialogDom_.querySelectorAll('[visibleif]'); 780 var ary = this.dialogDom_.querySelectorAll('[visibleif]');
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
773 this.selectionModelClass_ = cr.ui.ListSingleSelectionModel; 816 this.selectionModelClass_ = cr.ui.ListSingleSelectionModel;
774 } else { 817 } else {
775 this.selectionModelClass_ = cr.ui.ListSelectionModel; 818 this.selectionModelClass_ = cr.ui.ListSelectionModel;
776 } 819 }
777 820
778 this.dataModel_.addEventListener('splice', 821 this.dataModel_.addEventListener('splice',
779 this.onDataModelSplice_.bind(this)); 822 this.onDataModelSplice_.bind(this));
780 823
781 this.initTable_(); 824 this.initTable_();
782 this.initGrid_(); 825 this.initGrid_();
826 this.initRootsList_();
783 827
784 this.setListType(FileManager.ListType.DETAIL); 828 this.setListType(FileManager.ListType.DETAIL);
785 829
786 this.onResize_(); 830 this.onResize_();
787 831
788 this.textSearchState_ = {text: '', date: new Date()}; 832 this.textSearchState_ = {text: '', date: new Date()};
789 }; 833 };
790 834
835 FileManager.prototype.initRootsList_ = function() {
836 this.rootsList_ = this.dialogDom_.querySelector('.roots-list');
837 cr.ui.List.decorate(this.rootsList_);
838
839 var self = this;
840 this.rootsList_.itemConstructor = function(entry) {
841 return self.renderRoot_(entry);
842 };
843
844 this.rootsList_.selectionModel = new cr.ui.ListSingleSelectionModel();
845 this.rootsList_.selectionModel.addEventListener(
846 'change', this.onRootsSelectionChanged_.bind(this));
847
848 // TODO(dgozman): add "Add a drive" item.
849 this.rootsList_.dataModel = new cr.ui.ArrayDataModel(this.rootEntries_);
850 };
851
852 FileManager.prototype.updateRoots_ = function(opt_changeDirectoryTo) {
853 var self = this;
854 this.resolveRoots_(function(rootEntries) {
855 self.rootEntries_ = rootEntries;
856
857 var dataModel = self.rootsList_.dataModel;
858 var args = [0, dataModel.length].concat(rootEntries);
859 dataModel.splice.apply(dataModel, args);
860
861 self.updateRootsListSelection_();
862
863 if (opt_changeDirectoryTo)
864 self.changeDirectory(opt_changeDirectoryTo);
865 });
866 };
867
791 /** 868 /**
792 * Get the icon type for a given Entry. 869 * Get the icon type for a given Entry.
793 * 870 *
794 * @param {Entry} entry An Entry subclass (FileEntry or DirectoryEntry). 871 * @param {Entry} entry An Entry subclass (FileEntry or DirectoryEntry).
795 * @return {string} 872 * @return {string}
796 */ 873 */
797 FileManager.prototype.getIconType = function(entry) { 874 FileManager.prototype.getIconType = function(entry) {
798 if (!('cachedIconType_' in entry)) 875 if (!('cachedIconType_' in entry))
799 entry.cachedIconType_ = this.computeIconType_(entry); 876 entry.cachedIconType_ = this.computeIconType_(entry);
800 return entry.cachedIconType_; 877 return entry.cachedIconType_;
801 }; 878 };
802 879
803 /** 880 /**
804 * Extract extension from the file name and cat it to to lower case. 881 * Extract extension from the file name and cat it to to lower case.
805 * 882 *
806 * @param {string} name. 883 * @param {string} name.
807 * @return {strin} 884 * @return {strin}
808 */ 885 */
809 function getFileExtension(name) { 886 function getFileExtension(name) {
810 var extIndex = name.lastIndexOf('.'); 887 var extIndex = name.lastIndexOf('.');
811 if (extIndex < 0) 888 if (extIndex < 0)
812 return ''; 889 return '';
813 890
814 return name.substr(extIndex + 1).toLowerCase(); 891 return name.substr(extIndex + 1).toLowerCase();
815 } 892 }
816 893
817 FileManager.prototype.computeIconType_ = function(entry) { 894 FileManager.prototype.computeIconType_ = function(entry) {
895 // TODO(dgozman): refactor this to use proper icons in left panel,
896 // and do not depend on mountPoints.
818 var deviceNumber = this.getDeviceNumber(entry); 897 var deviceNumber = this.getDeviceNumber(entry);
819 if (deviceNumber != undefined) { 898 if (deviceNumber != undefined) {
820 if (this.mountPoints_[deviceNumber].mountCondition == '') 899 if (this.mountPoints_[deviceNumber].mountCondition == '')
821 return 'device'; 900 return 'device';
822 var mountCondition = this.mountPoints_[deviceNumber].mountCondition; 901 var mountCondition = this.mountPoints_[deviceNumber].mountCondition;
823 if (mountCondition == 'unknown_filesystem' || 902 if (mountCondition == 'unknown_filesystem' ||
824 mountCondition == 'unsupported_filesystem') 903 mountCondition == 'unsupported_filesystem')
825 return 'unreadable'; 904 return 'unreadable';
826 } 905 }
827 if (entry.isDirectory) 906 if (entry.isDirectory)
(...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after
1109 !isSystemDirEntry(this.currentDirEntry_)); 1188 !isSystemDirEntry(this.currentDirEntry_));
1110 1189
1111 case 'delete': 1190 case 'delete':
1112 return (// Initialized to the point where we have a current directory 1191 return (// Initialized to the point where we have a current directory
1113 this.currentDirEntry_ && 1192 this.currentDirEntry_ &&
1114 // Rename not in progress. 1193 // Rename not in progress.
1115 !this.renameInput_.currentEntry && 1194 !this.renameInput_.currentEntry &&
1116 !isSystemDirEntry(this.currentDirEntry_)) && 1195 !isSystemDirEntry(this.currentDirEntry_)) &&
1117 this.selection && 1196 this.selection &&
1118 this.selection.totalCount > 0; 1197 this.selection.totalCount > 0;
1198
1199 case 'newfolder':
1200 return this.currentDirEntry_ &&
1201 (this.dialogType_ == 'saveas-file' ||
1202 this.dialogType_ == 'full-page');
1119 } 1203 }
1120 }; 1204 };
1121 1205
1122 FileManager.prototype.updateCommonActionButtons_ = function() { 1206 FileManager.prototype.updateCommonActionButtons_ = function() {
1123 if (this.deleteButton_) 1207 if (this.deleteButton_)
1124 this.deleteButton_.disabled = !this.canExecute_('delete'); 1208 this.deleteButton_.disabled = !this.canExecute_('delete');
1125 }; 1209 };
1126 1210
1127 FileManager.prototype.setListType = function(type) { 1211 FileManager.prototype.setListType = function(type) {
1128 if (type && type == this.listType_) 1212 if (type && type == this.listType_)
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after
1312 case 'rename': 1396 case 'rename':
1313 var index = this.currentList_.selectionModel.selectedIndex; 1397 var index = this.currentList_.selectionModel.selectedIndex;
1314 var item = this.currentList_.getListItemByIndex(index); 1398 var item = this.currentList_.getListItemByIndex(index);
1315 if (item) 1399 if (item)
1316 this.initiateRename_(item); 1400 this.initiateRename_(item);
1317 return; 1401 return;
1318 1402
1319 case 'delete': 1403 case 'delete':
1320 this.deleteEntries(this.selection.entries); 1404 this.deleteEntries(this.selection.entries);
1321 return; 1405 return;
1406
1407 case 'newfolder':
1408 this.onNewFolderCommand_(event);
1409 return;
1322 } 1410 }
1323 }; 1411 };
1324 1412
1325 /** 1413 /**
1326 * Respond to the back and forward buttons. 1414 * Respond to the back and forward buttons.
1327 */ 1415 */
1328 FileManager.prototype.onPopState_ = function(event) { 1416 FileManager.prototype.onPopState_ = function(event) {
1329 // TODO(serya): We should restore selected items here. 1417 // TODO(serya): We should restore selected items here.
1330 if (this.rootEntries_) 1418 if (this.rootEntries_)
1331 this.setupCurrentDirectory_(); 1419 this.setupCurrentDirectory_();
1332 }; 1420 };
1333 1421
1334 FileManager.prototype.requestResize_ = function(timeout) { 1422 FileManager.prototype.requestResize_ = function(timeout) {
1335 var self = this; 1423 var self = this;
1336 setTimeout(function() { self.onResize_() }, timeout || 0); 1424 setTimeout(function() { self.onResize_() }, timeout || 0);
1337 }; 1425 };
1338 1426
1339 /** 1427 /**
1340 * Resize details and thumb views to fit the new window size. 1428 * Resize details and thumb views to fit the new window size.
1341 */ 1429 */
1342 FileManager.prototype.onResize_ = function() { 1430 FileManager.prototype.onResize_ = function() {
1343 this.table_.style.height = this.grid_.style.height = 1431 this.table_.style.height = this.grid_.style.height =
1344 this.grid_.parentNode.clientHeight + 'px'; 1432 this.grid_.parentNode.clientHeight + 'px';
1345 this.table_.style.width = this.grid_.style.width =
1346 this.grid_.parentNode.clientWidth + 'px';
1347
1348 this.table_.list_.style.width = this.table_.parentNode.clientWidth + 'px';
1349 this.table_.list_.style.height = (this.table_.clientHeight - 1 - 1433 this.table_.list_.style.height = (this.table_.clientHeight - 1 -
1350 this.table_.header_.clientHeight) + 'px'; 1434 this.table_.header_.clientHeight) + 'px';
1351 1435
1352 if (this.listType_ == FileManager.ListType.THUMBNAIL) { 1436 if (this.listType_ == FileManager.ListType.THUMBNAIL) {
1353 var self = this; 1437 var self = this;
1354 setTimeout(function() { 1438 setTimeout(function() {
1355 self.grid_.columns = 0; 1439 self.grid_.columns = 0;
1356 self.grid_.redraw(); 1440 self.grid_.redraw();
1357 }, 0); 1441 }, 0);
1358 } else { 1442 } else {
1359 this.currentList_.redraw(); 1443 this.currentList_.redraw();
1360 } 1444 }
1445
1446 this.rootsList_.style.height =
1447 this.rootsList_.parentNode.clientHeight + 'px';
1448 this.rootsList_.redraw();
1361 }; 1449 };
1362 1450
1363 FileManager.prototype.resolvePath = function( 1451 FileManager.prototype.resolvePath = function(
1364 path, resultCallback, errorCallback) { 1452 path, resultCallback, errorCallback) {
1365 return util.resolvePath(this.filesystem_.root, path, resultCallback, 1453 return util.resolvePath(this.filesystem_.root, path, resultCallback,
1366 errorCallback); 1454 errorCallback);
1367 }; 1455 };
1368 1456
1369 /** 1457 /**
1370 * Restores current directory and may be a selected item after page load (or 1458 * Restores current directory and may be a selected item after page load (or
(...skipping 13 matching lines...) Expand all
1384 this.setupPath_(this.params_.defaultPath); 1472 this.setupPath_(this.params_.defaultPath);
1385 } else { 1473 } else {
1386 this.setupDefaultPath_(); 1474 this.setupDefaultPath_();
1387 } 1475 }
1388 }; 1476 };
1389 1477
1390 FileManager.prototype.setupDefaultPath_ = function() { 1478 FileManager.prototype.setupDefaultPath_ = function() {
1391 // No preset given, find a good place to start. 1479 // No preset given, find a good place to start.
1392 // Check for removable devices, if there are none, go to Downloads. 1480 // Check for removable devices, if there are none, go to Downloads.
1393 var removableDirectoryEntry = this.rootEntries_.filter(function(rootEntry) { 1481 var removableDirectoryEntry = this.rootEntries_.filter(function(rootEntry) {
1394 return rootEntry.fullPath == REMOVABLE_DIRECTORY; 1482 return isParentPath(REMOVABLE_DIRECTORY, rootEntry.fullPath);
1395 })[0]; 1483 })[0];
1396 if (!removableDirectoryEntry) { 1484 var path = removableDirectoryEntry && removableDirectoryEntry.fullPath ||
1397 this.changeDirectory(DOWNLOADS_DIRECTORY, CD_NO_HISTORY); 1485 DOWNLOADS_DIRECTORY;
1398 return; 1486 this.changeDirectory(path, CD_NO_HISTORY);
1399 }
1400
1401 var foundRemovable = false;
1402 util.forEachDirEntry(removableDirectoryEntry, function(result) {
1403 if (result) {
1404 foundRemovable = true;
1405 } else { // Done enumerating, and we know the answer.
1406 this.changeDirectory(foundRemovable ? '/' : DOWNLOADS_DIRECTORY,
1407 CD_NO_HISTORY);
1408 }
1409 }.bind(this));
1410 }; 1487 };
1411 1488
1412 FileManager.prototype.setupPath_ = function(path) { 1489 FileManager.prototype.setupPath_ = function(path) {
1413 // Split the dirname from the basename. 1490 // Split the dirname from the basename.
1414 var ary = path.match(/^(?:(.*)\/)?([^\/]*)$/); 1491 var ary = path.match(/^(?:(.*)\/)?([^\/]*)$/);
1415 if (!ary) { 1492 if (!ary) {
1416 console.warn('Unable to split default path: ' + path); 1493 console.warn('Unable to split default path: ' + path);
1417 self.changeDirectory('/', CD_NO_HISTORY); 1494 self.changeDirectory('/', CD_NO_HISTORY);
1418 return; 1495 return;
1419 } 1496 }
(...skipping 18 matching lines...) Expand all
1438 1515
1439 // Leaf is an existing file, cd to its parent directory and select it. 1516 // Leaf is an existing file, cd to its parent directory and select it.
1440 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY, function() { 1517 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY, function() {
1441 self.selectEntry(leafEntry.name); 1518 self.selectEntry(leafEntry.name);
1442 }); 1519 });
1443 } 1520 }
1444 1521
1445 function onLeafError(err) { 1522 function onLeafError(err) {
1446 // Set filename first so OK button will update in changeDirectoryEntry. 1523 // Set filename first so OK button will update in changeDirectoryEntry.
1447 self.filenameInput_.value = leafName; 1524 self.filenameInput_.value = leafName;
1525 self.selectDefaultPathInFilenameInput_();
1448 if (err = FileError.NOT_FOUND_ERR) { 1526 if (err = FileError.NOT_FOUND_ERR) {
1449 // Leaf does not exist, it's just a suggested file name. 1527 // Leaf does not exist, it's just a suggested file name.
1450 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY); 1528 self.changeDirectoryEntry(baseDirEntry, CD_NO_HISTORY);
1451 } else { 1529 } else {
1452 console.log('Unexpected error resolving default leaf: ' + err); 1530 console.log('Unexpected error resolving default leaf: ' + err);
1453 self.changeDirectoryEntry('/', CD_NO_HISTORY); 1531 self.changeDirectoryEntry('/', CD_NO_HISTORY);
1454 } 1532 }
1455 } 1533 }
1456 1534
1457 self.resolvePath(path, onLeafFound, onLeafError); 1535 self.resolvePath(path, onLeafFound, onLeafError);
1458 } 1536 }
1459 1537
1460 function onBaseError(err) { 1538 function onBaseError(err) {
1461 // Set filename first so OK button will update in changeDirectory. 1539 // Set filename first so OK button will update in changeDirectory.
1462 self.filenameInput_.value = leafName; 1540 self.filenameInput_.value = leafName;
1541 self.selectDefaultPathInFilenameInput_();
1463 console.log('Unexpected error resolving default base "' + 1542 console.log('Unexpected error resolving default base "' +
1464 baseName + '": ' + err); 1543 baseName + '": ' + err);
1465 self.changeDirectory('/', CD_NO_HISTORY); 1544 self.changeDirectory('/', CD_NO_HISTORY);
1466 } 1545 }
1467 1546
1468 if (baseName) { 1547 if (baseName) {
1469 this.filesystem_.root.getDirectory( 1548 this.filesystem_.root.getDirectory(
1470 baseName, {create: false}, onBaseFound, onBaseError); 1549 baseName, {create: false}, onBaseFound, onBaseError);
1471 } else { 1550 } else {
1472 onBaseFound(this.filesystem_.root); 1551 onBaseFound(this.filesystem_.root);
(...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after
1708 * @param {cr.ui.Table} table The table doing the rendering. 1787 * @param {cr.ui.Table} table The table doing the rendering.
1709 */ 1788 */
1710 FileManager.prototype.renderIconType_ = function(entry, columnId, table) { 1789 FileManager.prototype.renderIconType_ = function(entry, columnId, table) {
1711 var icon = this.document_.createElement('div'); 1790 var icon = this.document_.createElement('div');
1712 icon.className = 'detail-icon'; 1791 icon.className = 'detail-icon';
1713 this.getIconType(entry); 1792 this.getIconType(entry);
1714 icon.setAttribute('iconType', entry.cachedIconType_); 1793 icon.setAttribute('iconType', entry.cachedIconType_);
1715 return icon; 1794 return icon;
1716 }; 1795 };
1717 1796
1718 FileManager.prototype.getLabelForRootPath_ = function(path) { 1797 /**
1719 // This hack lets us localize the top level directories. 1798 * Return the localized name for the root.
1720 if (path == 'Downloads') 1799 * @param {string} path The full path of the root (starting with slash).
1721 return str('DOWNLOADS_DIRECTORY_LABEL'); 1800 * @return {string} The localized name.
1801 */
1802 FileManager.prototype.getRootLabel_ = function(path) {
1803 if (path == DOWNLOADS_DIRECTORY)
1804 return str('CHROMEBOOK_DIRECTORY_LABEL');
1722 1805
1723 if (path == 'archive') 1806 if (path == ARCHIVE_DIRECTORY)
1724 return str('ARCHIVE_DIRECTORY_LABEL'); 1807 return str('ARCHIVE_DIRECTORY_LABEL');
1808 if (isParentPath(ARCHIVE_DIRECTORY, path))
1809 return path.substring(ARCHIVE_DIRECTORY.length + 1);
1725 1810
1726 if (path == 'removable') 1811 if (path == REMOVABLE_DIRECTORY)
1727 return str('REMOVABLE_DIRECTORY_LABEL'); 1812 return str('REMOVABLE_DIRECTORY_LABEL');
1813 if (isParentPath(REMOVABLE_DIRECTORY, path))
1814 return path.substring(REMOVABLE_DIRECTORY.length + 1);
1728 1815
1729 return path || str('ROOT_DIRECTORY_LABEL'); 1816 return path;
1817 };
1818
1819 FileManager.prototype.getRootIconUrl_ = function(path, opt_small) {
1820 var iconUrl = opt_small ? 'images/chromebook_28x28.png' :
1821 'images/chromebook_24x24.png';
1822 if (isParentPath(REMOVABLE_DIRECTORY, path))
1823 iconUrl = 'images/filetype_device.png';
1824 else if (isParentPath(ARCHIVE_DIRECTORY, path))
1825 iconUrl = 'images/icon_mount_archive_16x16.png';
1826 return chrome.extension.getURL(iconUrl);
1827 };
1828
1829 FileManager.prototype.renderRoot_ = function(entry) {
1830 var li = this.document_.createElement('li');
1831 li.className = 'root-item';
1832
1833 var icon = this.document_.createElement('img');
1834 icon.src = this.getRootIconUrl_(entry.fullPath, false);
1835 li.appendChild(icon);
1836
1837 var div = this.document_.createElement('div');
1838 div.className = 'text';
1839 div.textContent = this.getRootLabel_(entry.fullPath);
1840 li.appendChild(div);
1841
1842 if (isParentPath(REMOVABLE_DIRECTORY, entry.fullPath) ||
1843 isParentPath(ARCHIVE_DIRECTORY, entry.fullPath)) {
1844 var spacer = this.document_.createElement('div');
1845 spacer.className = 'spacer';
1846 li.appendChild(spacer);
1847
1848 var eject = this.document_.createElement('img');
1849 eject.className = 'root-eject';
1850 eject.setAttribute('src', chrome.extension.getURL('images/eject.png'));
1851 eject.addEventListener('click', this.onEjectClick_.bind(this, entry));
1852 li.appendChild(eject);
1853 }
1854
1855 cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR);
1856 cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR);
1857 return li;
1858 };
1859
1860 /**
1861 * Handler for eject button clicked.
1862 * @param {Entry} entry Entry to eject.
1863 * @param {Event} event The event.
1864 */
1865 FileManager.prototype.onEjectClick_ = function(entry, event) {
1866 this.unmountRequests_.push(entry.toURL());
1867 chrome.fileBrowserPrivate.removeMount(entry.fullPath);
1730 }; 1868 };
1731 1869
1732 /** 1870 /**
1733 * Render the Name column of the detail table. 1871 * Render the Name column of the detail table.
1734 * 1872 *
1735 * Invoked by cr.ui.Table when a file needs to be rendered. 1873 * Invoked by cr.ui.Table when a file needs to be rendered.
1736 * 1874 *
1737 * @param {Entry} entry The Entry object to render. 1875 * @param {Entry} entry The Entry object to render.
1738 * @param {string} columnId The id of the column to be rendered. 1876 * @param {string} columnId The id of the column to be rendered.
1739 * @param {cr.ui.Table} table The table doing the rendering. 1877 * @param {cr.ui.Table} table The table doing the rendering.
(...skipping 387 matching lines...) Expand 10 before | Expand all | Expand 10 after
2127 task.title = str('GALLERY'); 2265 task.title = str('GALLERY');
2128 task.allTasks = tasksList; 2266 task.allTasks = tasksList;
2129 this.galleryTask_ = task; 2267 this.galleryTask_ = task;
2130 } 2268 }
2131 } 2269 }
2132 this.renderTaskButton_(task); 2270 this.renderTaskButton_(task);
2133 } 2271 }
2134 2272
2135 // These are done in separate functions, as the checks require 2273 // These are done in separate functions, as the checks require
2136 // asynchronous function calls. 2274 // asynchronous function calls.
2137 this.maybeRenderUnmountTask_(selection);
2138 this.maybeRenderFormattingTask_(selection); 2275 this.maybeRenderFormattingTask_(selection);
2139 }; 2276 };
2140 2277
2141 FileManager.prototype.renderTaskButton_ = function(task) { 2278 FileManager.prototype.renderTaskButton_ = function(task) {
2142 var button = this.document_.createElement('button'); 2279 var button = this.document_.createElement('button');
2143 button.addEventListener('click', 2280 button.addEventListener('click',
2144 this.onTaskButtonClicked_.bind(this, task)); 2281 this.onTaskButtonClicked_.bind(this, task));
2145 button.className = 'task-button'; 2282 button.className = 'task-button';
2146 2283
2147 var img = this.document_.createElement('img'); 2284 var img = this.document_.createElement('img');
2148 img.src = task.iconUrl; 2285 img.src = task.iconUrl;
2149 2286
2150 button.appendChild(img); 2287 button.appendChild(img);
2151 var label = this.document_.createElement('div'); 2288 var label = this.document_.createElement('div');
2152 label.appendChild(this.document_.createTextNode(task.title)) 2289 label.appendChild(this.document_.createTextNode(task.title))
2153 button.appendChild(label); 2290 button.appendChild(label);
2154 2291
2155 this.taskButtons_.appendChild(button); 2292 this.taskButtons_.appendChild(button);
2156 }; 2293 };
2157 2294
2158 /** 2295 /**
2159 * Checks whether unmount task should be displayed and if the answer is
2160 * affirmative renders it.
2161 * @param {Object} selection Selected files object.
2162 */
2163 FileManager.prototype.maybeRenderUnmountTask_ = function(selection) {
2164 for (var index = 0; index < selection.urls.length; ++index) {
2165 // Each url should be a mount point.
2166 var path = selection.entries[index].fullPath;
2167 var found = false;
2168 for (var i = 0; i < this.mountPoints_.length; i++) {
2169 var mountPath = this.mountPoints_[i].mountPath;
2170 if (mountPath[0] != '/') {
2171 mountPath = '/' + mountPath;
2172 }
2173 if (mountPath == path && this.mountPoints_[i].mountType == 'file') {
2174 found = true;
2175 break;
2176 }
2177 }
2178 if (!found)
2179 return;
2180 }
2181 this.renderTaskButton_({
2182 taskId: this.getExtensionId_() + '|unmount-archive',
2183 iconUrl:
2184 chrome.extension.getURL('images/icon_unmount_archive_16x16.png'),
2185 title: str('UNMOUNT_ARCHIVE'),
2186 internal: true
2187 });
2188 };
2189
2190 /**
2191 * Checks whether formatting task should be displayed and if the answer is 2296 * Checks whether formatting task should be displayed and if the answer is
2192 * affirmative renders it. Includes asynchronous calls, so it's splitted into 2297 * affirmative renders it. Includes asynchronous calls, so it's splitted into
2193 * three parts. 2298 * three parts.
2194 * @param {Object} selection Selected files object. 2299 * @param {Object} selection Selected files object.
2195 */ 2300 */
2196 FileManager.prototype.maybeRenderFormattingTask_ = function(selection) { 2301 FileManager.prototype.maybeRenderFormattingTask_ = function(selection) {
2197 // Not to make unnecessary getMountPoints() call we doublecheck if there is 2302 // Not to make unnecessary getMountPoints() call we doublecheck if there is
2198 // only one selected entry. 2303 // only one selected entry.
2199 if (selection.entries.length != 1) 2304 if (selection.entries.length != 1)
2200 return; 2305 return;
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
2261 chrome.fileBrowserPrivate.executeTask(task.taskId, urls); 2366 chrome.fileBrowserPrivate.executeTask(task.taskId, urls);
2262 }; 2367 };
2263 2368
2264 /** 2369 /**
2265 * Event handler called when some volume was mounted or unmouted. 2370 * Event handler called when some volume was mounted or unmouted.
2266 */ 2371 */
2267 FileManager.prototype.onMountCompleted_ = function(event) { 2372 FileManager.prototype.onMountCompleted_ = function(event) {
2268 var self = this; 2373 var self = this;
2269 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { 2374 chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) {
2270 self.mountPoints_ = mountPoints; 2375 self.mountPoints_ = mountPoints;
2376 var changeDirectoryTo = null;
2377
2271 if (event.eventType == 'mount') { 2378 if (event.eventType == 'mount') {
2272 for (var index = 0; index < self.mountRequests_.length; ++index) { 2379 // Mount request finished - remove it.
2273 if (self.mountRequests_[index] == event.sourceUrl) { 2380 var index = self.mountRequests_.indexOf(event.sourceUrl);
2274 self.mountRequests_.splice(index, 1); 2381 if (index != -1) {
2275 if (event.status == 'success') { 2382 self.mountRequests_.splice(index, 1);
2276 self.changeDirectory(event.mountPath); 2383 // Go to mounted directory, if request was initiated from this tab.
2277 } else { 2384 if (event.status == 'success')
2278 // Report mount error. 2385 changeDirectoryTo = event.mountPath;
2279 if (event.mountType == 'file') {
2280 var fileName = event.sourceUrl.substr(
2281 event.sourceUrl.lastIndexOf('/') + 1);
2282 self.alert.show(strf('ARCHIVE_MOUNT_FAILED', fileName,
2283 event.status));
2284 }
2285 }
2286 return;
2287 }
2288 } 2386 }
2289 } 2387 }
2290 2388
2389 if (event.eventType == 'unmount') {
2390 // Unmount request finished - remove it.
2391 var index = self.unmountRequests_.indexOf(event.sourceUrl);
2392 if (index != -1)
2393 self.unmountRequests_.splice(index, 1);
2394 }
2395
2396 if (event.eventType == 'mount' && event.status != 'success' &&
2397 event.mountType == 'file') {
2398 // Report mount error.
2399 var fileName = event.sourceUrl.substr(
2400 event.sourceUrl.lastIndexOf('/') + 1);
2401 self.alert.show(strf('ARCHIVE_MOUNT_FAILED', fileName,
2402 event.status));
2403 }
2404
2405 if (event.eventType == 'unmount' && event.status != 'success') {
2406 // Report unmount error.
2407 // TODO(dgozman): introduce string and show alert here.
2408 }
2409
2291 if (event.eventType == 'unmount' && event.status == 'success' && 2410 if (event.eventType == 'unmount' && event.status == 'success' &&
2292 self.currentDirEntry_ && 2411 self.currentDirEntry_ &&
2293 isParentPath(event.mountPath, self.currentDirEntry_.fullPath)) { 2412 isParentPath(event.mountPath, self.currentDirEntry_.fullPath)) {
2294 self.changeDirectory(getParentPath(event.mountPath)); 2413 changeDirectoryTo = getParentPath(event.mountPath);
2295 return;
2296 } 2414 }
2297 2415
2298 var rescanDirectoryNeeded = (event.status == 'success'); 2416 // In the case of success, roots are changed and should be rescanned.
2299 for (var i = 0; i < mountPoints.length; i++) { 2417 if (event.status == 'success')
2300 if (event.sourceUrl == mountPoints[i].sourceUrl && 2418 self.updateRoots_(changeDirectoryTo);
2301 mountPoints[i].mountCondition != '') {
2302 rescanDirectoryNeeded = true;
2303 }
2304 }
2305 // TODO(dgozman): rescan directory, only if it contains mounted points,
2306 // when mounts location will be decided.
2307 if (rescanDirectoryNeeded)
2308 self.rescanDirectory_(null, 300);
2309 }); 2419 });
2310 }; 2420 };
2311 2421
2312 /** 2422 /**
2313 * Event handler called when some internal task should be executed. 2423 * Event handler called when some internal task should be executed.
2314 */ 2424 */
2315 FileManager.prototype.onFileTaskExecute_ = function(id, details) { 2425 FileManager.prototype.onFileTaskExecute_ = function(id, details) {
2316 var urls = details.urls; 2426 var urls = details.urls;
2317 if (id == 'play' || id == 'enqueue') { 2427 if (id == 'play' || id == 'enqueue') {
2318 chrome.fileBrowserPrivate.viewFiles(urls, id); 2428 chrome.fileBrowserPrivate.viewFiles(urls, id);
2319 } else if (id == 'mount-archive') { 2429 } else if (id == 'mount-archive') {
2320 for (var index = 0; index < urls.length; ++index) { 2430 for (var index = 0; index < urls.length; ++index) {
2321 this.mountRequests_.push(urls[index]); 2431 this.mountRequests_.push(urls[index]);
2322 chrome.fileBrowserPrivate.addMount(urls[index], 'file', {}); 2432 chrome.fileBrowserPrivate.addMount(urls[index], 'file', {});
2323 } 2433 }
2324 } else if (id == 'unmount-archive') {
2325 for (var index = 0; index < urls.length; ++index) {
2326 chrome.fileBrowserPrivate.removeMount(urls[index]);
2327 }
2328 } else if (id == 'format-device') { 2434 } else if (id == 'format-device') {
2329 this.confirm.show(str('FORMATTING_WARNING'), function() { 2435 this.confirm.show(str('FORMATTING_WARNING'), function() {
2330 chrome.fileBrowserPrivate.formatDevice(urls[0]); 2436 chrome.fileBrowserPrivate.formatDevice(urls[0]);
2331 }); 2437 });
2332 } else if (id == 'gallery') { 2438 } else if (id == 'gallery') {
2333 // Pass to gallery all possible tasks except the gallery itself. 2439 // Pass to gallery all possible tasks except the gallery itself.
2334 var noGallery = []; 2440 var noGallery = [];
2335 for (var index = 0; index < details.task.allTasks.length; index++) { 2441 for (var index = 0; index < details.task.allTasks.length; index++) {
2336 var task = details.task.allTasks[index]; 2442 var task = details.task.allTasks[index];
2337 if (task.taskId != this.getExtensionId_() + '|gallery') { 2443 if (task.taskId != this.getExtensionId_() + '|gallery') {
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
2395 self.metadataProvider_, 2501 self.metadataProvider_,
2396 shareActions, 2502 shareActions,
2397 str); 2503 str);
2398 }; 2504 };
2399 2505
2400 galleryFrame.src = 'js/image_editor/gallery.html'; 2506 galleryFrame.src = 'js/image_editor/gallery.html';
2401 this.dialogDom_.appendChild(galleryFrame); 2507 this.dialogDom_.appendChild(galleryFrame);
2402 galleryFrame.focus(); 2508 galleryFrame.focus();
2403 }; 2509 };
2404 2510
2511 FileManager.prototype.getRootForPath_ = function(path) {
2512 for (var index = 0; index < this.rootEntries_.length; index++) {
2513 if (isParentPath(this.rootEntries_[index].fullPath, path)) {
2514 return index;
2515 }
2516 }
2517 return -1;
2518 };
2519
2405 /** 2520 /**
2406 * Update the breadcrumb display to reflect the current directory. 2521 * Update the breadcrumb display to reflect the current directory.
2407 */ 2522 */
2408 FileManager.prototype.updateBreadcrumbs_ = function() { 2523 FileManager.prototype.updateBreadcrumbs_ = function() {
2409 var bc = this.dialogDom_.querySelector('.breadcrumbs'); 2524 var bc = this.dialogDom_.querySelector('.breadcrumbs');
2410 removeChildren(bc); 2525 removeChildren(bc);
2411 2526
2412 var fullPath = this.currentDirEntry_.fullPath.replace(/\/$/, ''); 2527 var fullPath = this.currentDirEntry_.fullPath;
2413 var pathNames = fullPath.split('/'); 2528 var rootIndex = this.getRootForPath_(fullPath);
2414 var path = ''; 2529 if (rootIndex == -1) {
2530 console.error('Not root for: ' + fullPath);
2531 return;
2532 }
2533 var root = this.rootEntries_[rootIndex];
2534
2535 var icon = this.document_.createElement('img');
2536 icon.className = 'breadcrumb-icon';
2537 icon.setAttribute('src', this.getRootIconUrl_(root.fullPath, true));
2538 bc.appendChild(icon);
2539
2540 var rootPath = root.fullPath;
2541 var relativePath = fullPath.substring(rootPath.length);
2542 var pathNames = relativePath.replace(/\/$/, '').split('/');
2543 if (pathNames[0] == '')
2544 pathNames.splice(0, 1);
2545
2546 // We need a first breadcrumb for root, so placing last name from
2547 // rootPath as first name of relativePath.
2548 var rootPathNames = rootPath.replace(/\/$/, '').split('/');
2549 pathNames.splice(0, 0, rootPathNames[rootPathNames.length - 1]);
2550 rootPathNames.splice(rootPathNames.length - 1, 1);
2551 var path = rootPathNames.join('/') + '/';
2415 2552
2416 for (var i = 0; i < pathNames.length; i++) { 2553 for (var i = 0; i < pathNames.length; i++) {
2417 var pathName = pathNames[i]; 2554 var pathName = pathNames[i];
2418 path += pathName + '/'; 2555 path += pathName;
2419 2556
2420 var div = this.document_.createElement('div'); 2557 var div = this.document_.createElement('div');
2421 div.className = 'breadcrumb-path'; 2558 div.className = 'breadcrumb-path';
2422 if (i <= 1) { 2559 div.textContent = i == 0 ? this.getRootLabel_(path) : pathName;
2423 // i == 0: root directory itself, i == 1: the files it contains.
2424 div.textContent = this.getLabelForRootPath_(pathName);
2425 } else {
2426 div.textContent = pathName;
2427 }
2428 2560
2561 path = path + '/';
2429 div.path = path; 2562 div.path = path;
2430 div.addEventListener('click', this.onBreadcrumbClick_.bind(this)); 2563 div.addEventListener('click', this.onBreadcrumbClick_.bind(this));
2431 2564
2432 bc.appendChild(div); 2565 bc.appendChild(div);
2433 2566
2434 if (i == pathNames.length - 1) { 2567 if (i == pathNames.length - 1) {
2435 div.classList.add('breadcrumb-last'); 2568 div.classList.add('breadcrumb-last');
2436 } else { 2569 } else {
2437 var spacer = this.document_.createElement('div'); 2570 var spacer = this.document_.createElement('div');
2438 spacer.className = 'breadcrumb-spacer'; 2571 spacer.className = 'breadcrumb-spacer';
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
2522 2655
2523 FileManager.prototype.selectEntry = function(name) { 2656 FileManager.prototype.selectEntry = function(name) {
2524 for (var i = 0; i < this.dataModel_.length; i++) { 2657 for (var i = 0; i < this.dataModel_.length; i++) {
2525 if (this.dataModel_.item(i).name == name) { 2658 if (this.dataModel_.item(i).name == name) {
2526 this.selectIndex(i); 2659 this.selectIndex(i);
2527 return; 2660 return;
2528 } 2661 }
2529 } 2662 }
2530 }; 2663 };
2531 2664
2665 FileManager.prototype.updateRootsListSelection_ = function() {
2666 if (!this.currentDirEntry_) return;
2667 var index = this.getRootForPath_(this.currentDirEntry_.fullPath);
2668 if (index == -1) {
2669 this.rootsList_.selectionModel.selectedIndex = 0;
2670 } else {
2671 if (this.rootsList_.selectionModel.selectedIndex != index)
2672 this.rootsList_.selectionModel.selectedIndex = index;
2673 }
2674 };
2675
2532 FileManager.prototype.selectIndex = function(index) { 2676 FileManager.prototype.selectIndex = function(index) {
2533 this.focusCurrentList_(); 2677 this.focusCurrentList_();
2534 if (index >= this.dataModel_.length) 2678 if (index >= this.dataModel_.length)
2535 return; 2679 return;
2536 this.currentList_.selectionModel.selectedIndex = index; 2680 this.currentList_.selectionModel.selectedIndex = index;
2537 this.currentList_.scrollIndexIntoView(index); 2681 this.currentList_.scrollIndexIntoView(index);
2538 }; 2682 };
2539 2683
2540 /** 2684 /**
2541 * Add the file/directory with given name to the current selection. 2685 * Add the file/directory with given name to the current selection.
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
2652 */ 2796 */
2653 FileManager.prototype.changeDirectoryEntry = function(dirEntry, 2797 FileManager.prototype.changeDirectoryEntry = function(dirEntry,
2654 opt_saveHistory, 2798 opt_saveHistory,
2655 opt_action) { 2799 opt_action) {
2656 if (typeof opt_saveHistory == 'undefined') { 2800 if (typeof opt_saveHistory == 'undefined') {
2657 opt_saveHistory = true; 2801 opt_saveHistory = true;
2658 } else { 2802 } else {
2659 opt_saveHistory = !!opt_saveHistory; 2803 opt_saveHistory = !!opt_saveHistory;
2660 } 2804 }
2661 2805
2806 // Some directories are above roots, so we instead show the first root.
2807 // There may be request to change directory above the roots. For example,
2808 // when usb-dirve is removed, we try to change to the parent directory,
2809 // which is REMOVABLE_DIRECTORY.
2810 if (!dirEntry || dirEntry.fullPath == '/' ||
2811 dirEntry.fullPath == REMOVABLE_DIRECTORY ||
2812 dirEntry.fullPath == ARCHIVE_DIRECTORY) {
2813 dirEntry = this.rootEntries_[0] || dirEntry;
2814 }
2815
2662 var action = opt_action || 2816 var action = opt_action ||
2663 (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE ? 2817 (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE ?
2664 undefined : this.selectIndex.bind(this, 0)); 2818 undefined : this.selectIndex.bind(this, 0));
2665 2819
2666 var location = document.location.origin + document.location.pathname + '#' + 2820 var location = document.location.origin + document.location.pathname + '#' +
2667 encodeURI(dirEntry.fullPath); 2821 encodeURI(dirEntry.fullPath);
2668 if (opt_saveHistory) { 2822 if (opt_saveHistory) {
2669 history.pushState(undefined, dirEntry.fullPath, location); 2823 history.pushState(undefined, dirEntry.fullPath, location);
2670 } else if (window.location.hash != location) { 2824 } else if (window.location.hash != location) {
2671 // If the user typed URL manually that is not canonical it would be fixed 2825 // If the user typed URL manually that is not canonical it would be fixed
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after
2886 // then the default action of this click event fires and toggles the 3040 // then the default action of this click event fires and toggles the
2887 // checkbox back off. 3041 // checkbox back off.
2888 // 3042 //
2889 // Since we're going to force checkboxes into the correct state for any 3043 // Since we're going to force checkboxes into the correct state for any
2890 // multi-selection, we can prevent this shift click from toggling the 3044 // multi-selection, we can prevent this shift click from toggling the
2891 // checkbox and avoid the trouble. 3045 // checkbox and avoid the trouble.
2892 event.preventDefault(); 3046 event.preventDefault();
2893 } 3047 }
2894 }; 3048 };
2895 3049
3050 FileManager.prototype.onRootsSelectionChanged_ = function(event) {
3051 var root = this.rootEntries_[this.rootsList_.selectionModel.selectedIndex];
3052 if (!this.currentDirEntry_ ||
3053 !isParentPath(root.fullPath, this.currentDirEntry_.fullPath))
3054 this.changeDirectoryEntry(root);
3055 };
3056
3057 FileManager.prototype.selectDefaultPathInFilenameInput_ = function() {
3058 var input = this.filenameInput_;
3059 input.focus();
3060 var selectionEnd = input.value.lastIndexOf('.');
3061 if (selectionEnd == -1) {
3062 input.select();
3063 } else {
3064 input.selectionStart = 0;
3065 input.selectionEnd = selectionEnd;
3066 }
3067 // Clear, so we never do this again.
3068 this.params_.defaultPath = '';
3069 };
3070
2896 /** 3071 /**
2897 * Update the UI when the selection model changes. 3072 * Update the UI when the selection model changes.
2898 * 3073 *
2899 * @param {cr.Event} event The change event. 3074 * @param {cr.Event} event The change event.
2900 */ 3075 */
2901 FileManager.prototype.onSelectionChanged_ = function(event) { 3076 FileManager.prototype.onSelectionChanged_ = function(event) {
2902 this.summarizeSelection_(); 3077 this.summarizeSelection_();
2903 3078
2904 if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { 3079 if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) {
2905 // If this is a save-as dialog, copy the selected file into the filename 3080 // If this is a save-as dialog, copy the selected file into the filename
2906 // input text box. 3081 // input text box.
2907 3082
2908 if (this.selection && 3083 if (this.selection &&
2909 this.selection.totalCount == 1 && 3084 this.selection.totalCount == 1 &&
2910 this.selection.entries[0].isFile) 3085 this.selection.entries[0].isFile &&
3086 this.filenameInput_.value != this.selection.entries[0].name) {
2911 this.filenameInput_.value = this.selection.entries[0].name; 3087 this.filenameInput_.value = this.selection.entries[0].name;
3088 if (this.params_.defaultPath == this.selection.entries[0].fullPath)
3089 this.selectDefaultPathInFilenameInput_();
3090 }
2912 } 3091 }
2913 3092
2914 this.updateOkButton_(); 3093 this.updateOkButton_();
2915 3094
2916 var self = this; 3095 var self = this;
2917 setTimeout(function() { self.onSelectionChangeComplete_(event) }, 0); 3096 setTimeout(function() { self.onSelectionChangeComplete_(event) }, 0);
2918 }; 3097 };
2919 3098
2920 /** 3099 /**
2921 * Handle selection change related tasks that won't run properly during 3100 * Handle selection change related tasks that won't run properly during
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
3054 * Update the UI when the current directory changes. 3233 * Update the UI when the current directory changes.
3055 * 3234 *
3056 * @param {cr.Event} event The directory-changed event. 3235 * @param {cr.Event} event The directory-changed event.
3057 */ 3236 */
3058 FileManager.prototype.onDirectoryChanged_ = function(event) { 3237 FileManager.prototype.onDirectoryChanged_ = function(event) {
3059 this.updateCommands_(); 3238 this.updateCommands_();
3060 this.updateOkButton_(); 3239 this.updateOkButton_();
3061 3240
3062 this.checkFreeSpace_(this.currentDirEntry_.fullPath); 3241 this.checkFreeSpace_(this.currentDirEntry_.fullPath);
3063 3242
3064 // New folder should never be enabled in the root or media/ directories. 3243 // TODO(dgozman): title may be better than this.
3065 this.newFolderButton_.disabled = isSystemDirEntry(this.currentDirEntry_);
3066
3067 this.document_.title = this.currentDirEntry_.fullPath; 3244 this.document_.title = this.currentDirEntry_.fullPath;
3068 3245
3069 var self = this; 3246 var self = this;
3070 3247
3071 if (this.subscribedOnDirectoryChanges_) { 3248 if (this.subscribedOnDirectoryChanges_) {
3072 chrome.fileBrowserPrivate.removeFileWatch(event.previousDirEntry.toURL(), 3249 chrome.fileBrowserPrivate.removeFileWatch(event.previousDirEntry.toURL(),
3073 function(result) { 3250 function(result) {
3074 if (!result) { 3251 if (!result) {
3075 console.log('Failed to remove file watch'); 3252 console.log('Failed to remove file watch');
3076 } 3253 }
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
3150 // Updated when a user clicks on the label of a file, used to detect 3327 // Updated when a user clicks on the label of a file, used to detect
3151 // when a click is eligible to trigger a rename. Can be null, or 3328 // when a click is eligible to trigger a rename. Can be null, or
3152 // an object with 'path' and 'date' properties. 3329 // an object with 'path' and 'date' properties.
3153 this.lastLabelClick_ = null; 3330 this.lastLabelClick_ = null;
3154 3331
3155 // Clear the table first. 3332 // Clear the table first.
3156 this.dataModel_.splice(0, this.dataModel_.length); 3333 this.dataModel_.splice(0, this.dataModel_.length);
3157 this.currentList_.selectionModel.clear(); 3334 this.currentList_.selectionModel.clear();
3158 3335
3159 this.updateBreadcrumbs_(); 3336 this.updateBreadcrumbs_();
3337 this.updateRootsListSelection_();
3160 3338
3161 if (this.currentDirEntry_.fullPath != '/') { 3339 // Add current request to pending result list
3162 // Add current request to pending result list 3340 this.pendingRescanQueue_.push({
3163 this.pendingRescanQueue_.push({ 3341 onSuccess:opt_callback,
3164 onSuccess:opt_callback, 3342 onError:opt_onError
3165 onError:opt_onError 3343 });
3166 });
3167 3344
3168 if (this.rescanRunning_) 3345 if (this.rescanRunning_)
3169 return; 3346 return;
3170 3347
3171 this.rescanRunning_ = true; 3348 this.rescanRunning_ = true;
3172 3349
3173 // The current list of callbacks is saved and reset. Subsequent 3350 // The current list of callbacks is saved and reset. Subsequent
3174 // calls to rescanDirectory_ while we're still pending will be 3351 // calls to rescanDirectory_ while we're still pending will be
3175 // saved and will cause an additional rescan to happen after a delay. 3352 // saved and will cause an additional rescan to happen after a delay.
3176 var callbacks = this.pendingRescanQueue_; 3353 var callbacks = this.pendingRescanQueue_;
3177 3354
3178 this.pendingRescanQueue_ = []; 3355 this.pendingRescanQueue_ = [];
3179 3356
3180 var self = this; 3357 var self = this;
3181 var reader; 3358 var reader;
3182 3359
3183 function onError() { 3360 function onError() {
3361 if (self.pendingRescanQueue_.length > 0) {
3362 setTimeout(self.rescanDirectory_.bind(self),
3363 SIMULTANEOUS_RESCAN_INTERVAL);
3364 }
3365
3366 self.rescanRunning_ = false;
3367
3368 for (var i= 0; i < callbacks.length; i++) {
3369 if (callbacks[i].onError)
3370 try {
3371 callbacks[i].onError();
3372 } catch (ex) {
3373 console.error('Caught exception while notifying about error: ' +
3374 name, ex);
3375 }
3376 }
3377 }
3378
3379 function onReadSome(entries) {
3380 if (entries.length == 0) {
3381 metrics.recordInterval('DirectoryScan');
3382 if (self.currentDirEntry_.fullPath == DOWNLOADS_DIRECTORY) {
3383 metrics.recordMediumCount("DownloadsCount", self.dataModel_.length);
3384 }
3385
3184 if (self.pendingRescanQueue_.length > 0) { 3386 if (self.pendingRescanQueue_.length > 0) {
3185 setTimeout(self.rescanDirectory_.bind(self), 3387 setTimeout(self.rescanDirectory_.bind(self),
3186 SIMULTANEOUS_RESCAN_INTERVAL); 3388 SIMULTANEOUS_RESCAN_INTERVAL);
3187 } 3389 }
3188 3390
3189 self.rescanRunning_ = false; 3391 self.rescanRunning_ = false;
3190
3191 for (var i= 0; i < callbacks.length; i++) { 3392 for (var i= 0; i < callbacks.length; i++) {
3192 if (callbacks[i].onError) 3393 if (callbacks[i].onSuccess)
3193 try { 3394 try {
3194 callbacks[i].onError(); 3395 callbacks[i].onSuccess();
3195 } catch (ex) { 3396 } catch (ex) {
3196 console.error('Caught exception while notifying about error: ' + 3397 console.error('Caught exception while notifying about error: ' +
3197 name, ex); 3398 name, ex);
3198 } 3399 }
3199 } 3400 }
3401
3402 return;
3200 } 3403 }
3201 3404
3202 function onReadSome(entries) { 3405 // Splice takes the to-be-spliced-in array as individual parameters,
3203 if (entries.length == 0) { 3406 // rather than as an array, so we need to perform some acrobatics...
3204 metrics.recordInterval('DirectoryScan'); 3407 var spliceArgs = [].slice.call(entries);
3205 if (self.currentDirEntry_.fullPath == DOWNLOADS_DIRECTORY) {
3206 metrics.recordMediumCount("DownloadsCount", self.dataModel_.length);
3207 }
3208 3408
3209 if (self.pendingRescanQueue_.length > 0) { 3409 // Hide files that start with a dot ('.').
3210 setTimeout(self.rescanDirectory_.bind(self), 3410 // TODO(rginda): User should be able to override this. Support for other
3211 SIMULTANEOUS_RESCAN_INTERVAL); 3411 // commonly hidden patterns might be nice too.
3212 } 3412 if (self.filterFiles_) {
3413 spliceArgs = spliceArgs.filter(function(e) {
3414 return e.name.substr(0, 1) != '.';
3415 });
3416 }
3213 3417
3214 self.rescanRunning_ = false; 3418 self.prefetchCacheForSorting_(spliceArgs, function() {
3215 for (var i= 0; i < callbacks.length; i++) { 3419 spliceArgs.unshift(0, 0); // index, deleteCount
3216 if (callbacks[i].onSuccess) 3420 self.dataModel_.splice.apply(self.dataModel_, spliceArgs);
3217 try {
3218 callbacks[i].onSuccess();
3219 } catch (ex) {
3220 console.error('Caught exception while notifying about error: ' +
3221 name, ex);
3222 }
3223 }
3224 3421
3225 return; 3422 // Keep reading until entries.length is 0.
3226 } 3423 reader.readEntries(onReadSome, onError);
3424 });
3425 };
3227 3426
3228 // Splice takes the to-be-spliced-in array as individual parameters, 3427 metrics.startInterval('DirectoryScan');
3229 // rather than as an array, so we need to perform some acrobatics...
3230 var spliceArgs = [].slice.call(entries);
3231 3428
3232 // Hide files that start with a dot ('.'). 3429 // If not the root directory, just read the contents.
3233 // TODO(rginda): User should be able to override this. Support for other 3430 reader = this.currentDirEntry_.createReader();
3234 // commonly hidden patterns might be nice too. 3431 reader.readEntries(onReadSome, onError);
3235 if (self.filterFiles_) {
3236 spliceArgs = spliceArgs.filter(function(e) {
3237 return e.name.substr(0, 1) != '.';
3238 });
3239 }
3240
3241 self.prefetchCacheForSorting_(spliceArgs, function() {
3242 spliceArgs.unshift(0, 0); // index, deleteCount
3243 self.dataModel_.splice.apply(self.dataModel_, spliceArgs);
3244
3245 // Keep reading until entries.length is 0.
3246 reader.readEntries(onReadSome, onError);
3247 });
3248 };
3249
3250 metrics.startInterval('DirectoryScan');
3251
3252 // If not the root directory, just read the contents.
3253 reader = this.currentDirEntry_.createReader();
3254 reader.readEntries(onReadSome, onError);
3255 return;
3256 }
3257
3258 // Otherwise, use the provided list of root subdirectories, since the
3259 // real local filesystem root directory (the one we use outside the
3260 // harness) can't be enumerated yet.
3261 var spliceArgs = [].slice.call(this.rootEntries_);
3262 spliceArgs.unshift(0, 0); // index, deleteCount
3263 this.dataModel_.splice.apply(this.dataModel_, spliceArgs);
3264
3265 if (opt_callback)
3266 opt_callback();
3267 }; 3432 };
3268 3433
3269 FileManager.prototype.prefetchCacheForSorting_ = function(entries, callback) { 3434 FileManager.prototype.prefetchCacheForSorting_ = function(entries, callback) {
3270 var field = this.dataModel_.sortStatus.field; 3435 var field = this.dataModel_.sortStatus.field;
3271 if (field) { 3436 if (field) {
3272 this.prepareSortEntries_(entries, field, callback); 3437 this.prepareSortEntries_(entries, field, callback);
3273 } else { 3438 } else {
3274 callback(); 3439 callback();
3275 return; 3440 return;
3276 } 3441 }
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after
3493 var selectionEnd = input.value.lastIndexOf('.'); 3658 var selectionEnd = input.value.lastIndexOf('.');
3494 if (selectionEnd == -1) { 3659 if (selectionEnd == -1) {
3495 input.select(); 3660 input.select();
3496 } else { 3661 } else {
3497 input.selectionStart = 0; 3662 input.selectionStart = 0;
3498 input.selectionEnd = selectionEnd; 3663 input.selectionEnd = selectionEnd;
3499 } 3664 }
3500 }, 0); 3665 }, 0);
3501 }; 3666 };
3502 3667
3503 FileManager.prototype.onNewFolderButtonClick_ = function(event) { 3668 FileManager.prototype.onToggleSidebar_ = function(event) {
3669 if (this.dialogContainer_.hasAttribute('sidebar')) {
3670 this.dialogContainer_.removeAttribute('sidebar');
3671 } else {
3672 this.dialogContainer_.setAttribute('sidebar', 'sidebar');
3673 }
3674 // TODO(dgozman): make table header css-resizable.
3675 setTimeout(this.onResize_.bind(this), 300);
3676 };
3677
3678 FileManager.prototype.onNewFolderCommand_ = function(event) {
3504 var self = this; 3679 var self = this;
3505 3680
3506 function onNameSelected(name) { 3681 function onNameSelected(name) {
3507 var valid = self.validateFileName_(name, function() { 3682 var valid = self.validateFileName_(name, function() {
3508 promptForName(name); 3683 promptForName(name);
3509 }); 3684 });
3510 3685
3511 if (!valid) { 3686 if (!valid) {
3512 // Validation failed. User will be prompted for a new name after they 3687 // Validation failed. User will be prompted for a new name after they
3513 // dismiss the validation error dialog. 3688 // dismiss the validation error dialog.
3514 return; 3689 return;
3515 } 3690 }
3516 3691
3517 self.createNewFolder(name); 3692 self.createNewFolder(name);
3518 } 3693 }
3519 3694
3520 function promptForName(suggestedName) { 3695 function promptForName(suggestedName) {
3521 self.prompt.show(str('NEW_FOLDER_PROMPT'), suggestedName, onNameSelected); 3696 self.prompt.show(str('NEW_FOLDER_PROMPT'), suggestedName, onNameSelected);
3522 } 3697 }
3523 3698
3524 promptForName(str('DEFAULT_NEW_FOLDER_NAME')); 3699 promptForName(str('DEFAULT_NEW_FOLDER_NAME'));
3525 }; 3700 };
3526 3701
3527 FileManager.prototype.createNewFolder = function(name, opt_callback) { 3702 FileManager.prototype.createNewFolder = function(name, opt_callback) {
3528 metrics.recordAction('CreateNewFolder'); 3703 metrics.recordUserAction('CreateNewFolder');
3529 3704
3530 var self = this; 3705 var self = this;
3531 3706
3532 function onSuccess(dirEntry) { 3707 function onSuccess(dirEntry) {
3533 self.rescanDirectory_(function() { 3708 self.rescanDirectory_(function() {
3534 self.selectEntry(name); 3709 self.selectEntry(name);
3535 if (opt_callback) 3710 if (opt_callback)
3536 opt_callback(); 3711 opt_callback();
3537 }); 3712 });
3538 } 3713 }
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
3622 this.dialogType_ != FileManager.SELECT_FOLDER) { 3797 this.dialogType_ != FileManager.SELECT_FOLDER) {
3623 event.preventDefault(); 3798 event.preventDefault();
3624 this.onDirectoryAction(this.selection.entries[0]); 3799 this.onDirectoryAction(this.selection.entries[0]);
3625 } else if (!this.okButton_.disabled) { 3800 } else if (!this.okButton_.disabled) {
3626 event.preventDefault(); 3801 event.preventDefault();
3627 this.onOk_(); 3802 this.onOk_();
3628 } 3803 }
3629 break; 3804 break;
3630 3805
3631 case 32: // Ctrl-Space => New Folder. 3806 case 32: // Ctrl-Space => New Folder.
3632 if (this.newFolderButton_.style.display != 'none' && event.ctrlKey) { 3807 if ((this.dialogType_ == 'saveas-file' ||
3808 this.dialogType_ == 'full-page') && event.ctrlKey) {
3633 event.preventDefault(); 3809 event.preventDefault();
3634 this.onNewFolderButtonClick_(); 3810 this.onNewFolderCommand_();
3635 } 3811 }
3636 break; 3812 break;
3637 3813
3638 case 88: // Ctrl-X => Cut. 3814 case 88: // Ctrl-X => Cut.
3639 this.updateCommands_(); 3815 this.updateCommands_();
3640 if (!this.commands_['cut'].disabled) { 3816 if (!this.commands_['cut'].disabled) {
3641 event.preventDefault(); 3817 event.preventDefault();
3642 this.commands_['cut'].execute(); 3818 this.commands_['cut'].execute();
3643 } 3819 }
3644 break; 3820 break;
(...skipping 332 matching lines...) Expand 10 before | Expand all | Expand 10 after
3977 }); 4153 });
3978 }, onError); 4154 }, onError);
3979 4155
3980 function onError(err) { 4156 function onError(err) {
3981 console.log('Error while checking free space: ' + err); 4157 console.log('Error while checking free space: ' + err);
3982 setTimeout(doCheck, 1000 * 60); 4158 setTimeout(doCheck, 1000 * 60);
3983 } 4159 }
3984 } 4160 }
3985 } 4161 }
3986 })(); 4162 })();
OLDNEW
« no previous file with comments | « chrome/browser/resources/file_manager/css/file_manager.css ('k') | chrome/browser/resources/file_manager/js/harness.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698