| Index: chrome/browser/resources/file_manager/js/directory_model.js
|
| diff --git a/chrome/browser/resources/file_manager/js/directory_model.js b/chrome/browser/resources/file_manager/js/directory_model.js
|
| index 155b94e378eef5a2cf8cb94a9192a057e53add22..650f296b43191eb23dc5b1085b403c693f1a57a6 100644
|
| --- a/chrome/browser/resources/file_manager/js/directory_model.js
|
| +++ b/chrome/browser/resources/file_manager/js/directory_model.js
|
| @@ -55,6 +55,21 @@ function DirectoryModel(root, singleSelection, metadataCache) {
|
| * @type {Object.<string, boolean>}
|
| */
|
| this.volumeReadOnlyStatus_ = {};
|
| +
|
| + /**
|
| + * Directory in which search results are displayed. Not null iff search
|
| + * results are being displayed.
|
| + * @private
|
| + * @type {Entry}
|
| + */
|
| + this.searchDirEntry_ = null;
|
| +
|
| + /**
|
| + * Is search in progress.
|
| + * @private
|
| + * @type {boolean}
|
| + */
|
| + this.isSearching_ = false;
|
| }
|
|
|
| /**
|
| @@ -106,6 +121,21 @@ DirectoryModel.GDATA_ACCESS_LAZY = 1;
|
| DirectoryModel.GDATA_ACCESS_FULL = 2;
|
|
|
| /**
|
| + * Root path used for displaying gdata content search results.
|
| + * Search results will be shown in directory 'GDATA_SEARCH_ROOT_PATH/query'.
|
| + *
|
| + * @const
|
| + * @type {string}
|
| + */
|
| +DirectoryModel.GDATA_SEARCH_ROOT_PATH = '/drive/.search';
|
| +
|
| +/**
|
| + * @const
|
| + * @type {Array.<string>}
|
| + */
|
| +DirectoryModel.GDATA_SEARCH_ROOT_COMPONENTS = ['', 'drive', '.search'];
|
| +
|
| +/**
|
| * DirectoryModel extends cr.EventTarget.
|
| */
|
| DirectoryModel.prototype.__proto__ = cr.EventTarget.prototype;
|
| @@ -177,7 +207,21 @@ DirectoryModel.prototype.isReadOnly = function() {
|
| };
|
|
|
| /**
|
| - * @param {string} path Path to check.
|
| + * @return {boolean} True if search is in progress.
|
| + */
|
| +DirectoryModel.prototype.isSearching = function() {
|
| + return this.isSearching_;
|
| +};
|
| +
|
| +/**
|
| + * @return {boolean} True if we are currently showing search results.
|
| + */
|
| +DirectoryModel.prototype.isOnGDataSearchDir = function() {
|
| + return this.getSearchOrCurrentDirEntry() != this.getCurrentDirEntry();
|
| +};
|
| +
|
| +/**
|
| + * @param {strin} path Path to check.
|
| * @return {boolean} True if the |path| is read only.
|
| */
|
| DirectoryModel.prototype.isPathReadOnly = function(path) {
|
| @@ -222,6 +266,16 @@ DirectoryModel.prototype.getCurrentDirEntry = function() {
|
| };
|
|
|
| /**
|
| + * If search results are being displayed, returns search directory, else returns
|
| + * current directory.
|
| + *
|
| + * @return {DirectoryEntry} search or directory entry.
|
| + */
|
| +DirectoryModel.prototype.getSearchOrCurrentDirEntry = function() {
|
| + return this.searchDirEntry_ || this.currentDirEntry_;
|
| +};
|
| +
|
| +/**
|
| * @return {string} Path for the current directory.
|
| */
|
| DirectoryModel.prototype.getCurrentDirPath = function() {
|
| @@ -336,7 +390,8 @@ DirectoryModel.prototype.addFilter = function(name, filter) {
|
| * @param {string} name Identifier of a filter.
|
| */
|
| DirectoryModel.prototype.removeFilter = function(name) {
|
| - delete this.filters_[name];
|
| + if (this.filters_[name])
|
| + delete this.filters_[name];
|
| this.rescanSoon();
|
| };
|
|
|
| @@ -426,7 +481,7 @@ DirectoryModel.prototype.createScanner_ = function(list, successCallback) {
|
| }
|
|
|
| return new DirectoryModel.Scanner(
|
| - this.currentDirEntry_,
|
| + this.getSearchOrCurrentDirEntry(),
|
| list,
|
| onSuccess,
|
| onFailure,
|
| @@ -513,6 +568,38 @@ DirectoryModel.prototype.prefetchCacheForSorting_ = function(entries,
|
| };
|
|
|
| /**
|
| + * Gets name that should be displayed in the UI for the entry.
|
| + * @param {string} path Full path of the entry whose display name we are
|
| + * getting.
|
| + * @param {string} defaultName Default name to use if no name is calculated.
|
| + * @return {string} Name to be used for display.
|
| + */
|
| +DirectoryModel.prototype.getDisplayName = function(path, defaultName) {
|
| + var searchResultName = util.getFileAndDisplayNameForGDataSearchResult(path);
|
| + return searchResultName ? searchResultName.displayName : defaultName;
|
| +};
|
| +
|
| +/**
|
| + * Creates file name that should be used as a new file name in filesystem
|
| + * operations while renaming. If the given entry is not a gdata search result
|
| + * entry, |displayName| will be used.
|
| + *
|
| + * @private
|
| + * @param {Entry} entry Entry which is being renamed.
|
| + * @param {string} displayName The new file name provided by user.
|
| + * @return {string} File name that should be used in renaming filesystem
|
| + * operations.
|
| + */
|
| +DirectoryModel.prototype.getEntryNameForRename_ = function(entry, displayName) {
|
| + // If we are renaming gdata search result, we'll have to format newName to
|
| + // use in file system operation like: <resource_id>.<file_name>.
|
| + var searchResultName =
|
| + util.getFileAndDisplayNameForGDataSearchResult(entry.fullPath);
|
| + return searchResultName ? searchResultName.resourceId + '.' + displayName :
|
| + displayName;
|
| +};
|
| +
|
| +/**
|
| * Delete the list of files and directories from filesystem and
|
| * update the file list.
|
| * @param {Array.<Entry>} entries Entries to delete.
|
| @@ -549,14 +636,19 @@ DirectoryModel.prototype.deleteEntries = function(entries, opt_callback) {
|
| * @param {string} name Filename.
|
| */
|
| DirectoryModel.prototype.onEntryChanged = function(name) {
|
| - var currentEntry = this.currentDirEntry_;
|
| + var currentEntry = this.getSearchOrCurrentDirEntry();
|
| + if (currentEntry != this.currentDirEntry_)
|
| + return;
|
| var dm = this.fileList_;
|
| var self = this;
|
|
|
| function onEntryFound(entry) {
|
| + // Do nothing if current directory changed during async operations.
|
| + if (self.getSearchOrCurrentDirEntry() != currentEntry)
|
| + return;
|
| self.prefetchCacheForSorting_([entry], function() {
|
| // Do nothing if current directory changed during async operations.
|
| - if (self.currentDirEntry_ != currentEntry)
|
| + if (self.getSearchOrCurrentDirEntry() != currentEntry)
|
| return;
|
|
|
| var index = self.findIndexByName_(name);
|
| @@ -581,7 +673,7 @@ DirectoryModel.prototype.onEntryChanged = function(name) {
|
| dm.splice(index, 1);
|
| };
|
|
|
| - util.resolvePath(this.currentDirEntry_, name, onEntryFound, onError);
|
| + util.resolvePath(currentEntry, name, onEntryFound, onError);
|
| };
|
|
|
| /**
|
| @@ -600,11 +692,12 @@ DirectoryModel.prototype.findIndexByName_ = function(name) {
|
| /**
|
| * Rename the entry in the filesystem and update the file list.
|
| * @param {Entry} entry Entry to rename.
|
| - * @param {string} newName New name.
|
| + * @param {string} newDisplayName New name.
|
| * @param {function} errorCallback Called on error.
|
| * @param {function} opt_successCallback Called on success.
|
| */
|
| -DirectoryModel.prototype.renameEntry = function(entry, newName, errorCallback,
|
| +DirectoryModel.prototype.renameEntry = function(entry, newDisplayName,
|
| + errorCallback,
|
| opt_successCallback) {
|
| var self = this;
|
| function onSuccess(newEntry) {
|
| @@ -613,25 +706,31 @@ DirectoryModel.prototype.renameEntry = function(entry, newName, errorCallback,
|
| var index = fileList.indexOf(entry);
|
| if (index >= 0)
|
| fileList.splice(index, 1, newEntry);
|
| - self.selectEntry(newName);
|
| + self.selectEntry(newEntry.name);
|
| // If the entry doesn't exist in the list it mean that it updated from
|
| // outside (probably by directory rescan).
|
| if (opt_successCallback)
|
| opt_successCallback();
|
| });
|
| }
|
| - entry.moveTo(this.currentDirEntry_, newName, onSuccess, errorCallback);
|
| +
|
| + var newEntryName = this.getEntryNameForRename_(entry, newDisplayName);
|
| + entry.moveTo(this.getSearchOrCurrentDirEntry(), newEntryName, onSuccess,
|
| + errorCallback);
|
| };
|
|
|
| /**
|
| * Checks if current directory contains a file or directory with this name.
|
| - * @param {string} newName Name to check.
|
| + * @param {string} entry Entry to which newName will be given.
|
| + * @param {string} displayName Name to check.
|
| * @param {function(boolean, boolean?)} callback Called when the result's
|
| * available. First parameter is true if the entry exists and second
|
| * is true if it's a file.
|
| */
|
| -DirectoryModel.prototype.doesExist = function(newName, callback) {
|
| - util.resolvePath(this.currentDirEntry_, newName,
|
| +DirectoryModel.prototype.doesExist = function(entry, displayName, callback) {
|
| + var entryName = this.getEntryNameForRename_(entry, displayName);
|
| +
|
| + util.resolvePath(this.getSearchOrCurrentDirEntry(), entryName,
|
| function(entry) {
|
| callback(true, entry.isFile);
|
| },
|
| @@ -677,7 +776,15 @@ DirectoryModel.prototype.createDirectory = function(name, successCallback,
|
| * @param {string} path New current directory path.
|
| */
|
| DirectoryModel.prototype.changeDirectory = function(path) {
|
| - this.resolveDirectory(path, function(directoryEntry) {
|
| + var targetPath = path;
|
| + // We should not be changing directory to gdata search path. If we do, default
|
| + // to gdata root.
|
| + if (DirectoryModel.isGDataSearchPath(path)) {
|
| + console.error('Attempt to change directory to search path.');
|
| + targetPath = '/' + DirectoryModel.GDATA_DIRECTORY;
|
| + }
|
| +
|
| + this.resolveDirectory(targetPath, function(directoryEntry) {
|
| this.changeDirectoryEntry_(false, directoryEntry);
|
| }.bind(this), function(error) {
|
| console.error('Error changing directory to ' + path + ': ', error);
|
| @@ -759,6 +866,7 @@ DirectoryModel.prototype.changeRoot = function(path) {
|
| */
|
| DirectoryModel.prototype.changeDirectoryEntry_ = function(initial, dirEntry,
|
| opt_callback) {
|
| + this.clearSearch_();
|
| var previous = this.currentDirEntry_;
|
| this.currentDirEntry_ = dirEntry;
|
| function onRescanComplete() {
|
| @@ -1135,6 +1243,87 @@ DirectoryModel.isSystemDirectory = function(path) {
|
| };
|
|
|
| /**
|
| + * Performs search and displays results. The search type is dependent on the
|
| + * current directory. If we are currently on gdata, server side content search
|
| + * over gdata mount point. If the current directory is not on the gdata, file
|
| + * name search over current directory wil be performed.
|
| + *
|
| + * @param {string} query Query that will be searched for.
|
| + * @param {function} onSearchRescan Function that will be called when the search
|
| + * directory is rescanned (i.e. search results are displayed)
|
| + * @param {function} onClearSearch Function to be called when search state gets
|
| + * cleared.
|
| + */
|
| +DirectoryModel.prototype.search = function(query,
|
| + onSearchRescan,
|
| + onClearSearch) {
|
| + if (!query) {
|
| + if (this.isSearching_)
|
| + this.clearSearch_();
|
| + return;
|
| + }
|
| +
|
| + this.isSearching_ = true;
|
| +
|
| + // If we alreaqdy have event listener for an old search, we have to remove it.
|
| + if (this.onSearchRescan_)
|
| + this.removeEventListener('rescan-completed', this.onSearchRescan_);
|
| +
|
| + this.onSearchRescan_ = onSearchRescan;
|
| + this.onClearSearch_ = onClearSearch;
|
| +
|
| + this.addEventListener('rescan-completed', this.onSearchRescan_);
|
| +
|
| + // If we are offline, let's fallback to file name search inside dir.
|
| + if (this.getRootType() == DirectoryModel.RootType.GDATA &&
|
| + !this.isOffline()) {
|
| + var self = this;
|
| + // Create shadow directory which will contain search results.
|
| + this.root_.getDirectory(DirectoryModel.createGDataSearchPath(query),
|
| + {create: false},
|
| + function(dir) {
|
| + self.searchDirEntry_ = dir;
|
| + self.rescanSoon();
|
| + },
|
| + function() {
|
| + self.isSearching_ = false;
|
| + });
|
| + } else {
|
| + var queryLC = query.toLowerCase();
|
| + this.searchDirEntry_ = this.currentDirEntry_;
|
| + this.addFilter(
|
| + 'searchbox',
|
| + function(e) {
|
| + return e.name.toLowerCase().indexOf(queryLC) > -1;
|
| + });
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Clears any state set by previous searches.
|
| + * @private
|
| + */
|
| +DirectoryModel.prototype.clearSearch_ = function() {
|
| + if (!this.isSearching_)
|
| + return;
|
| + this.searchDirEntry_ = null;
|
| + this.isSearching_ = false;
|
| + // This will trigger rescan.
|
| + this.removeFilter('searchbox');
|
| +
|
| + if (this.onSearchRescan_) {
|
| + this.removeEventListener('rescan-completed', this.onSearchRescan_);
|
| + this.onSearchRescan_ = null;
|
| + }
|
| +
|
| + if (this.onClearSearch_) {
|
| + this.onClearSearch_();
|
| + this.onClearSearch_ = null;
|
| + }
|
| +};
|
| +
|
| +/**
|
| * @param {string} path Any path.
|
| * @return {string} The root path.
|
| */
|
| @@ -1199,6 +1388,28 @@ DirectoryModel.isRootPath = function(path) {
|
| };
|
|
|
| /**
|
| + * Checks if the provided path is under gdata search.
|
| + *
|
| + * @param {string} path Path to be tested.
|
| + * @return {boolean} Is the path gdata search path.
|
| + */
|
| +DirectoryModel.isGDataSearchPath = function(path) {
|
| + return path == DirectoryModel.GDATA_SEARCH_ROOT_PATH ||
|
| + path.indexOf(DirectoryModel.GDATA_SEARCH_ROOT_PATH + '/') == 0;
|
| +};
|
| +
|
| +/**
|
| + * Creates directory path in which gdata content search results for |query|
|
| + * should be displayed.
|
| + *
|
| + * @param {string} query Search query.
|
| + * @return {string} Virtual directory path for search results.
|
| + */
|
| +DirectoryModel.createGDataSearchPath = function(query) {
|
| + return DirectoryModel.GDATA_SEARCH_ROOT_PATH + '/' + query;
|
| +};
|
| +
|
| +/**
|
| * @constructor
|
| * @extends cr.EventTarget
|
| * @param {DirectoryEntry} dir Directory to scan.
|
|
|