OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 })(); |
OLD | NEW |