OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 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 'use strict'; | 5 'use strict'; |
6 | 6 |
7 // If directory files changes too often, don't rescan directory more than once | 7 // If directory files changes too often, don't rescan directory more than once |
8 // per specified interval | 8 // per specified interval |
9 var SIMULTANEOUS_RESCAN_INTERVAL = 1000; | 9 var SIMULTANEOUS_RESCAN_INTERVAL = 1000; |
10 // Used for operations that require almost instant rescan. | 10 // Used for operations that require almost instant rescan. |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
66 }; | 66 }; |
67 | 67 |
68 /** | 68 /** |
69 * @return {cr.ui.ArrayDataModel} Files in the current directory. | 69 * @return {cr.ui.ArrayDataModel} Files in the current directory. |
70 */ | 70 */ |
71 DirectoryModel.prototype.getFileList = function() { | 71 DirectoryModel.prototype.getFileList = function() { |
72 return this.currentFileListContext_.fileList; | 72 return this.currentFileListContext_.fileList; |
73 }; | 73 }; |
74 | 74 |
75 /** | 75 /** |
76 * Sort the file list. | |
77 * @param {string} sortField Sort field. | |
78 * @param {string} sortDirection "asc" or "desc". | |
79 */ | |
80 DirectoryModel.prototype.sortFileList = function(sortField, sortDirection) { | |
81 this.getFileList().sort(sortField, sortDirection); | |
82 }; | |
83 | |
84 /** | |
85 * @return {cr.ui.ListSelectionModel|cr.ui.ListSingleSelectionModel} Selection | 76 * @return {cr.ui.ListSelectionModel|cr.ui.ListSingleSelectionModel} Selection |
86 * in the fileList. | 77 * in the fileList. |
87 */ | 78 */ |
88 DirectoryModel.prototype.getFileListSelection = function() { | 79 DirectoryModel.prototype.getFileListSelection = function() { |
89 return this.fileListSelection_; | 80 return this.fileListSelection_; |
90 }; | 81 }; |
91 | 82 |
92 /** | 83 /** |
93 * @return {?RootType} Root type of current root, or null if not found. | 84 * @return {?RootType} Root type of current root, or null if not found. |
94 */ | 85 */ |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
188 }; | 179 }; |
189 | 180 |
190 /** | 181 /** |
191 * @return {DirectoryEntry} Current directory. | 182 * @return {DirectoryEntry} Current directory. |
192 */ | 183 */ |
193 DirectoryModel.prototype.getCurrentDirEntry = function() { | 184 DirectoryModel.prototype.getCurrentDirEntry = function() { |
194 return this.currentDirContents_.getDirectoryEntry(); | 185 return this.currentDirContents_.getDirectoryEntry(); |
195 }; | 186 }; |
196 | 187 |
197 /** | 188 /** |
198 * @return {Array.<Entry>} Array of selected entries.. | 189 * @return {Array.<Entry>} Array of selected entries. |
199 * @private | 190 * @private |
200 */ | 191 */ |
201 DirectoryModel.prototype.getSelectedEntries_ = function() { | 192 DirectoryModel.prototype.getSelectedEntries_ = function() { |
202 var indexes = this.fileListSelection_.selectedIndexes; | 193 var indexes = this.fileListSelection_.selectedIndexes; |
203 var fileList = this.getFileList(); | 194 var fileList = this.getFileList(); |
204 if (fileList) { | 195 if (fileList) { |
205 return indexes.map(function(i) { | 196 return indexes.map(function(i) { |
206 return fileList.item(i); | 197 return fileList.item(i); |
207 }); | 198 }); |
208 } | 199 } |
(...skipping 427 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
636 this.fileListSelection_.endChange(); | 627 this.fileListSelection_.endChange(); |
637 successCallback(newEntry); | 628 successCallback(newEntry); |
638 } | 629 } |
639 }.bind(this), function(reason) { | 630 }.bind(this), function(reason) { |
640 tracker.stop(); | 631 tracker.stop(); |
641 errorCallback(reason); | 632 errorCallback(reason); |
642 }); | 633 }); |
643 }; | 634 }; |
644 | 635 |
645 /** | 636 /** |
646 * @param {DirectoryEntry} dirEntry The entry of the new directory. | |
647 * @param {function()=} opt_callback Executed if the directory loads | |
648 * successfully. | |
649 * @private | |
650 */ | |
651 DirectoryModel.prototype.changeDirectoryEntrySilent_ = function(dirEntry, | |
652 opt_callback) { | |
653 var onScanComplete = function() { | |
654 if (opt_callback) | |
655 opt_callback(); | |
656 // For tests that open the dialog to empty directories, everything | |
657 // is loaded at this point. | |
658 chrome.test.sendMessage('directory-change-complete'); | |
659 }; | |
660 this.clearAndScan_( | |
661 DirectoryContents.createForDirectory(this.currentFileListContext_, | |
662 dirEntry), | |
663 onScanComplete.bind(this)); | |
664 }; | |
665 | |
666 /** | |
667 * Change the current directory to the directory represented by | 637 * Change the current directory to the directory represented by |
668 * a DirectoryEntry or a fake entry. | 638 * a DirectoryEntry or a fake entry. |
669 * | 639 * |
670 * Dispatches the 'directory-changed' event when the directory is successfully | 640 * Dispatches the 'directory-changed' event when the directory is successfully |
671 * changed. | 641 * changed. |
672 * | 642 * |
673 * @param {DirectoryEntry|Object} dirEntry The entry of the new directory to | 643 * @param {DirectoryEntry|Object} dirEntry The entry of the new directory to |
674 * be opened. | 644 * be opened. |
675 * @param {function()=} opt_callback Executed if the directory loads | 645 * @param {function()=} opt_callback Executed if the directory loads |
676 * successfully. | 646 * successfully. |
677 */ | 647 */ |
678 DirectoryModel.prototype.changeDirectoryEntry = function( | 648 DirectoryModel.prototype.changeDirectoryEntry = function( |
679 dirEntry, opt_callback) { | 649 dirEntry, opt_callback) { |
680 if (util.isFakeEntry(dirEntry)) { | |
681 this.specialSearch(dirEntry); | |
682 if (opt_callback) | |
683 opt_callback(); | |
684 return; | |
685 } | |
686 | |
687 // Increment the sequence value. | 650 // Increment the sequence value. |
688 this.changeDirectorySequence_++; | 651 this.changeDirectorySequence_++; |
652 this.clearSearch_(); | |
689 | 653 |
690 this.fileWatcher_.changeWatchedDirectory(dirEntry, function(sequence) { | 654 var promise = new Promise( |
691 if (this.changeDirectorySequence_ !== sequence) | 655 function(onFulfilled, onRejected) { |
692 return; | 656 this.fileWatcher_.changeWatchedDirectory(dirEntry, onFulfilled); |
693 var previous = this.currentDirContents_.getDirectoryEntry(); | 657 }.bind(this)). |
694 this.clearSearch_(); | |
695 this.changeDirectoryEntrySilent_(dirEntry, opt_callback); | |
696 | 658 |
697 var e = new Event('directory-changed'); | 659 then(function(sequence) { |
698 e.previousDirEntry = previous; | 660 return new Promise(function(onFulfilled, onRejected) { |
699 e.newDirEntry = dirEntry; | 661 if (this.changeDirectorySequence_ !== sequence) |
700 this.dispatchEvent(e); | 662 return; |
701 }.bind(this, this.changeDirectorySequence_)); | 663 |
664 var newDirectoryContents = this.createDirectoryContents_( | |
665 this.currentFileListContext_, dirEntry, ''); | |
666 if (!newDirectoryContents) | |
667 return; | |
668 | |
669 var event = new Event('directory-changed'); | |
670 event.previousDirEntry = this.currentDirContents_.getDirectoryEntry(); | |
671 event.newDirEntry = dirEntry; | |
672 | |
673 this.clearAndScan_(newDirectoryContents, opt_callback); | |
674 this.dispatchEvent(event); | |
675 }.bind(this)); | |
676 }.bind(this, this.changeDirectorySequence_)); | |
702 }; | 677 }; |
703 | 678 |
704 /** | 679 /** |
705 * Creates an object which could say whether directory has changed while it has | 680 * Creates an object which could say whether directory has changed while it has |
706 * been active or not. Designed for long operations that should be cancelled | 681 * been active or not. Designed for long operations that should be cancelled |
707 * if the used change current directory. | 682 * if the used change current directory. |
708 * @return {Object} Created object. | 683 * @return {Object} Created object. |
709 */ | 684 */ |
710 DirectoryModel.prototype.createDirectoryChangeTracker = function() { | 685 DirectoryModel.prototype.createDirectoryChangeTracker = function() { |
711 var tracker = { | 686 var tracker = { |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
773 DirectoryModel.prototype.selectIndex = function(index) { | 748 DirectoryModel.prototype.selectIndex = function(index) { |
774 // this.focusCurrentList_(); | 749 // this.focusCurrentList_(); |
775 if (index >= this.getFileList().length) | 750 if (index >= this.getFileList().length) |
776 return; | 751 return; |
777 | 752 |
778 // If a list bound with the model it will do scrollIndexIntoView(index). | 753 // If a list bound with the model it will do scrollIndexIntoView(index). |
779 this.fileListSelection_.selectedIndex = index; | 754 this.fileListSelection_.selectedIndex = index; |
780 }; | 755 }; |
781 | 756 |
782 /** | 757 /** |
783 * Called when VolumeInfoList is updated. | 758 * Handles update of VolumeInfoList. |
784 * @param {Event} event Event of VolumeInfoList's 'splice'. | 759 * @param {Event} event Event of VolumeInfoList's 'splice'. |
785 * @private | 760 * @private |
786 */ | 761 */ |
787 DirectoryModel.prototype.onVolumeInfoListUpdated_ = function(event) { | 762 DirectoryModel.prototype.onVolumeInfoListUpdated_ = function(event) { |
788 // When the volume where we are is unmounted, fallback to the default volume's | 763 // When the volume where we are is unmounted, fallback to the default volume's |
789 // root. If current directory path is empty, stop the fallback | 764 // root. If current directory path is empty, stop the fallback |
790 // since the current directory is initializing now. | 765 // since the current directory is initializing now. |
791 if (this.getCurrentDirEntry() && | 766 if (this.getCurrentDirEntry() && |
792 !this.volumeManager_.getVolumeInfo(this.getCurrentDirEntry())) { | 767 !this.volumeManager_.getVolumeInfo(this.getCurrentDirEntry())) { |
793 this.volumeManager_.getDefaultDisplayRoot(function(displayRoot) { | 768 this.volumeManager_.getDefaultDisplayRoot(function(displayRoot) { |
794 this.changeDirectoryEntry(displayRoot); | 769 this.changeDirectoryEntry(displayRoot); |
795 }.bind(this)); | 770 }.bind(this)); |
796 } | 771 } |
797 }; | 772 }; |
798 | 773 |
799 /** | 774 /** |
775 * Creates directory contents for the entry and query. | |
776 * | |
777 * @param {FileListContext} context File list context. | |
778 * @param {DirectoryEntry} entry Current directory. | |
779 * @param {opt_query} opt_query Search query string. | |
mtomasz
2014/02/04 02:20:12
nit: opt_query -> string=
hirono
2014/02/04 05:53:11
Done.
| |
780 * @return {DirectoryEntry} Directory contents. | |
mtomasz
2014/02/04 02:20:12
nit: DirectoryEntry -> DirectoryContents
hirono
2014/02/04 05:53:11
Done.
| |
781 * @private | |
782 */ | |
783 DirectoryModel.prototype.createDirectoryContents_ = | |
784 function(context, entry, opt_query) { | |
785 var query = (opt_query || '').trimLeft(); | |
786 var locationInfo = this.volumeManager_.getLocationInfo(entry); | |
787 if (!locationInfo) | |
788 return null; | |
789 var canUseDriveSearch = this.volumeManager_.getDriveConnectionState().type !== | |
790 util.DriveConnectionType.OFFLINE && | |
791 locationInfo.isDriveBased; | |
792 | |
793 if (query && canUseDriveSearch) { | |
794 // Drive search. | |
795 return DirectoryContents.createForDriveSearch(context, entry, query); | |
796 } else if (query) { | |
797 // Local search. | |
798 return DirectoryContents.createForLocalSearch(context, entry, query); | |
799 } if (util.isFakeEntry(entry)) { | |
mtomasz
2014/02/04 02:20:12
nit: isFakeEntry -> locationInfo.isSpecialSearchRo
hirono
2014/02/04 05:53:11
Done.
| |
800 // Drive special search. | |
801 var searchType; | |
802 switch (locationInfo.rootType) { | |
803 case RootType.DRIVE_OFFLINE: | |
804 searchType = | |
805 DriveMetadataSearchContentScanner.SearchType.SEARCH_OFFLINE; | |
806 break; | |
807 case RootType.DRIVE_SHARED_WITH_ME: | |
808 searchType = | |
809 DriveMetadataSearchContentScanner.SearchType.SEARCH_SHARED_WITH_ME; | |
810 break; | |
811 case RootType.DRIVE_RECENT: | |
812 searchType = | |
813 DriveMetadataSearchContentScanner.SearchType.SEARCH_RECENT_FILES; | |
814 break; | |
815 default: | |
816 // Unknown special search entry. | |
817 throw new Error('Unknown special search type.'); | |
818 } | |
819 return DirectoryContents.createForDriveMetadataSearch( | |
820 context, | |
821 entry, | |
822 searchType); | |
823 } else { | |
824 // Local fetch or search. | |
825 return DirectoryContents.createForDirectory(context, entry); | |
826 } | |
827 }; | |
828 | |
829 /** | |
800 * Performs search and displays results. The search type is dependent on the | 830 * Performs search and displays results. The search type is dependent on the |
801 * current directory. If we are currently on drive, server side content search | 831 * current directory. If we are currently on drive, server side content search |
802 * over drive mount point. If the current directory is not on the drive, file | 832 * over drive mount point. If the current directory is not on the drive, file |
803 * name search over current directory will be performed. | 833 * name search over current directory will be performed. |
804 * | 834 * |
805 * @param {string} query Query that will be searched for. | 835 * @param {string} query Query that will be searched for. |
806 * @param {function(Event)} onSearchRescan Function that will be called when the | 836 * @param {function(Event)} onSearchRescan Function that will be called when the |
807 * search directory is rescanned (i.e. search results are displayed). | 837 * search directory is rescanned (i.e. search results are displayed). |
808 * @param {function()} onClearSearch Function to be called when search state | 838 * @param {function()} onClearSearch Function to be called when search state |
809 * gets cleared. | 839 * gets cleared. |
810 * TODO(olege): Change callbacks to events. | 840 * TODO(olege): Change callbacks to events. |
811 */ | 841 */ |
812 DirectoryModel.prototype.search = function(query, | 842 DirectoryModel.prototype.search = function(query, |
813 onSearchRescan, | 843 onSearchRescan, |
814 onClearSearch) { | 844 onClearSearch) { |
815 query = query.trimLeft(); | |
816 | |
817 this.clearSearch_(); | 845 this.clearSearch_(); |
818 | |
819 var currentDirEntry = this.getCurrentDirEntry(); | 846 var currentDirEntry = this.getCurrentDirEntry(); |
820 if (!currentDirEntry) { | 847 if (!currentDirEntry) { |
821 // Not yet initialized. Do nothing. | 848 // Not yet initialized. Do nothing. |
822 return; | 849 return; |
823 } | 850 } |
824 | 851 |
825 if (!query) { | 852 if (!(query || '').trimLeft()) { |
826 if (this.isSearching()) { | 853 if (this.isSearching()) { |
827 var newDirContents = DirectoryContents.createForDirectory( | 854 var newDirContents = DirectoryContents.createForDirectory( |
828 this.currentFileListContext_, | 855 this.currentFileListContext_, |
829 this.currentDirContents_.getLastNonSearchDirectoryEntry()); | 856 currentDirEntry); |
830 this.clearAndScan_(newDirContents); | 857 this.clearAndScan_(newDirContents); |
831 } | 858 } |
832 return; | 859 return; |
833 } | 860 } |
834 | 861 |
862 var newDirContents = this.createDirectoryContents_( | |
863 this.currentFileListContext_, currentDirEntry, query); | |
864 if (!newDirContents) | |
865 return; | |
866 | |
835 this.onSearchCompleted_ = onSearchRescan; | 867 this.onSearchCompleted_ = onSearchRescan; |
836 this.onClearSearch_ = onClearSearch; | 868 this.onClearSearch_ = onClearSearch; |
837 | |
838 this.addEventListener('scan-completed', this.onSearchCompleted_); | 869 this.addEventListener('scan-completed', this.onSearchCompleted_); |
839 | |
840 // If we are offline, let's fallback to file name search inside dir. | |
841 // A search initiated from directories in Drive or special search results | |
842 // should trigger Drive search. | |
843 var newDirContents; | |
844 var isDriveOffline = this.volumeManager_.getDriveConnectionState().type === | |
845 util.DriveConnectionType.OFFLINE; | |
846 var locationInfo = this.volumeManager_.getLocationInfo(currentDirEntry); | |
847 if (!isDriveOffline && locationInfo && locationInfo.isDriveBased) { | |
848 // Drive search is performed over the whole drive, so pass drive root as | |
849 // |directoryEntry|. | |
850 newDirContents = DirectoryContents.createForDriveSearch( | |
851 this.currentFileListContext_, | |
852 currentDirEntry, | |
853 this.currentDirContents_.getLastNonSearchDirectoryEntry(), | |
854 query); | |
855 } else { | |
856 newDirContents = DirectoryContents.createForLocalSearch( | |
857 this.currentFileListContext_, currentDirEntry, query); | |
858 } | |
859 this.clearAndScan_(newDirContents); | 870 this.clearAndScan_(newDirContents); |
860 }; | 871 }; |
861 | 872 |
862 /** | 873 /** |
863 * Performs special search and displays results. e.g. Drive files available | |
864 * offline, shared-with-me files, recently modified files. | |
865 * @param {Object} fakeEntry Fake entry representing a special search. | |
866 * @param {string=} opt_query Query string used for the search. | |
867 */ | |
868 DirectoryModel.prototype.specialSearch = function(fakeEntry, opt_query) { | |
869 var query = opt_query || ''; | |
870 | |
871 // Increment the sequence value. | |
872 this.changeDirectorySequence_++; | |
873 | |
874 this.clearSearch_(); | |
875 this.onSearchCompleted_ = null; | |
876 this.onClearSearch_ = null; | |
877 | |
878 // Obtains a volume information. | |
879 // TODO(hirono): Obtain the proper profile's volume information. | |
880 var volumeInfo = this.volumeManager_.getCurrentProfileVolumeInfo( | |
881 util.VolumeType.DRIVE); | |
882 if (!volumeInfo) { | |
883 // It seems that the volume is already unmounted or drive is disable. | |
884 return; | |
885 } | |
886 | |
887 var onDriveDirectoryResolved = function(sequence, driveRoot) { | |
888 if (this.changeDirectorySequence_ !== sequence) | |
889 return; | |
890 | |
891 var locationInfo = this.volumeManager_.getLocationInfo(fakeEntry); | |
892 if (!locationInfo) | |
893 return; | |
894 | |
895 var searchOption; | |
896 switch (locationInfo.rootType) { | |
897 case RootType.DRIVE_OFFLINE: | |
898 searchOption = | |
899 DriveMetadataSearchContentScanner.SearchType.SEARCH_OFFLINE; | |
900 break; | |
901 case RootType.DRIVE_SHARED_WITH_ME: | |
902 searchOption = | |
903 DriveMetadataSearchContentScanner.SearchType.SEARCH_SHARED_WITH_ME; | |
904 break; | |
905 case RootType.DRIVE_RECENT: | |
906 searchOption = | |
907 DriveMetadataSearchContentScanner.SearchType.SEARCH_RECENT_FILES; | |
908 break; | |
909 default: | |
910 // Unknown special search entry. | |
911 throw new Error('Unknown special search type.'); | |
912 } | |
913 | |
914 var newDirContents = DirectoryContents.createForDriveMetadataSearch( | |
915 this.currentFileListContext_, | |
916 fakeEntry, driveRoot, query, searchOption); | |
917 var previous = this.currentDirContents_.getDirectoryEntry(); | |
918 this.clearAndScan_(newDirContents); | |
919 | |
920 var e = new Event('directory-changed'); | |
921 e.previousDirEntry = previous; | |
922 e.newDirEntry = fakeEntry; | |
923 this.dispatchEvent(e); | |
924 }.bind(this, this.changeDirectorySequence_); | |
925 | |
926 volumeInfo.resolveDisplayRoot( | |
927 onDriveDirectoryResolved /* success */, function() {} /* failed */); | |
928 }; | |
929 | |
930 /** | |
931 * In case the search was active, remove listeners and send notifications on | 874 * In case the search was active, remove listeners and send notifications on |
932 * its canceling. | 875 * its canceling. |
933 * @private | 876 * @private |
934 */ | 877 */ |
935 DirectoryModel.prototype.clearSearch_ = function() { | 878 DirectoryModel.prototype.clearSearch_ = function() { |
936 if (!this.isSearching()) | 879 if (!this.isSearching()) |
937 return; | 880 return; |
938 | 881 |
939 if (this.onSearchCompleted_) { | 882 if (this.onSearchCompleted_) { |
940 this.removeEventListener('scan-completed', this.onSearchCompleted_); | 883 this.removeEventListener('scan-completed', this.onSearchCompleted_); |
941 this.onSearchCompleted_ = null; | 884 this.onSearchCompleted_ = null; |
942 } | 885 } |
943 | 886 |
944 if (this.onClearSearch_) { | 887 if (this.onClearSearch_) { |
945 this.onClearSearch_(); | 888 this.onClearSearch_(); |
946 this.onClearSearch_ = null; | 889 this.onClearSearch_ = null; |
947 } | 890 } |
948 }; | 891 }; |
OLD | NEW |