[Files app] ES6 class for ui/file_manager/file_manager/foreground/js/search_controller.js

Bug: 778674
Change-Id: Id6a3c5a48af4085a74282e7f3f89cc95dd426e0e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1630075
Commit-Queue: Luciano Pacheco <lucmult@chromium.org>
Auto-Submit: Luciano Pacheco <lucmult@chromium.org>
Reviewed-by: François Degros <fdegros@chromium.org>
Cr-Commit-Position: refs/heads/master@{#663476}
diff --git a/ui/file_manager/file_manager/foreground/js/search_controller.js b/ui/file_manager/file_manager/foreground/js/search_controller.js
index 17965a4..c0986c7 100644
--- a/ui/file_manager/file_manager/foreground/js/search_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/search_controller.js
@@ -4,53 +4,45 @@
 
 /**
  * Controller for searching.
- * @param {!SearchBox} searchBox Search box UI element.
- * @param {!LocationLine} locationLine Location line UI element.
- * @param {!DirectoryModel} directoryModel Directory model.
- * @param {!TaskController} taskController Task controller to execute the
- *     selected item.
- * @constructor
  */
-function SearchController(
-    searchBox, locationLine, directoryModel, volumeManager, taskController) {
+class SearchController {
   /**
-   * @type {SearchBox}
-   * @private
+   * @param {!SearchBox} searchBox Search box UI element.
+   * @param {!LocationLine} locationLine Location line UI element.
+   * @param {!DirectoryModel} directoryModel Directory model.
+   * @param {!TaskController} taskController Task controller to execute the
+   *     selected item.
    */
-  this.searchBox_ = searchBox;
+  constructor(
+      searchBox, locationLine, directoryModel, volumeManager, taskController) {
+    /** @const @private {!SearchBox} */
+    this.searchBox_ = searchBox;
 
-  /**
-   * @type {LocationLine}
-   * @private
-   */
-  this.locationLine_ = locationLine;
+    /** @const @private {!LocationLine} */
+    this.locationLine_ = locationLine;
 
-  /**
-   * @type {DirectoryModel}
-   * @private
-   */
-  this.directoryModel_ = directoryModel;
+    /** @const @private {!DirectoryModel} */
+    this.directoryModel_ = directoryModel;
 
-  /**
-   * @type {VolumeManager}
-   * @private
-   */
-  this.volumeManager_ = volumeManager;
+    /** @const @private {!VolumeManager} */
+    this.volumeManager_ = volumeManager;
 
-  /**
-   * @type {!TaskController}
-   * @private
-   */
-  this.taskController_ = taskController;
+    /** @const @private {!TaskController} */
+    this.taskController_ = taskController;
 
-  searchBox.addEventListener(
-      SearchBox.EventType.TEXT_CHANGE, this.onTextChange_.bind(this));
-  searchBox.addEventListener(
-      SearchBox.EventType.ITEM_SELECT, this.onItemSelect_.bind(this));
-  directoryModel.addEventListener('directory-changed', this.clear.bind(this));
-}
+    /** @private {string} */
+    this.lastAutocompleteQuery_ = '';
 
-SearchController.prototype = {
+    /** @private {boolean} */
+    this.autocompleteSuggestionsBusy_ = false;
+
+    searchBox.addEventListener(
+        SearchBox.EventType.TEXT_CHANGE, this.onTextChange_.bind(this));
+    searchBox.addEventListener(
+        SearchBox.EventType.ITEM_SELECT, this.onItemSelect_.bind(this));
+    directoryModel.addEventListener('directory-changed', this.clear.bind(this));
+  }
+
   /**
    * Obtains current directory's locaiton info.
    * @type {EntryLocation}
@@ -59,7 +51,7 @@
   get currentLocationInfo_() {
     const entry = this.directoryModel_.getCurrentDirEntry();
     return entry && this.volumeManager_.getLocationInfo(entry);
-  },
+  }
 
   /**
    * Whether the current directory is on drive or not.
@@ -69,191 +61,193 @@
     const currentLocationInfo = this.currentLocationInfo_;
     return currentLocationInfo && currentLocationInfo.isDriveBased;
   }
-};
 
-/**
- * Clears the search state.
- * @param {Event=} opt_event when called from "directory-changed" event.
- */
-SearchController.prototype.clear = function(opt_event) {
-  this.directoryModel_.clearLastSearchQuery();
-  this.searchBox_.clear();
-  // Only update visibility if |clear| is called from "directory-changed" event.
-  if (opt_event) {
-    // My Files currently doesn't implement search so let's hide it.
-    const isMyFiles =
-        (opt_event.newDirEntry &&
-         opt_event.newDirEntry.rootType ===
-             VolumeManagerCommon.RootType.MY_FILES);
-    this.searchBox_.setHidden(isMyFiles);
-  }
-};
-
-/**
- * Handles text change event.
- * @private
- */
-SearchController.prototype.onTextChange_ = function() {
-  const searchString = this.searchBox_.inputElement.value.trimLeft();
-
-  // On drive, incremental search is not invoked since we have an auto-
-  // complete suggestion instead.
-  if (!this.isOnDrive_) {
-    this.search_(searchString);
-    return;
+  /**
+   * Clears the search state.
+   * @param {Event=} opt_event when called from "directory-changed" event.
+   */
+  clear(opt_event) {
+    this.directoryModel_.clearLastSearchQuery();
+    this.searchBox_.clear();
+    // Only update visibility if |clear| is called from "directory-changed"
+    // event.
+    if (opt_event) {
+      // My Files currently doesn't implement search so let's hide it.
+      const isMyFiles =
+          (opt_event.newDirEntry &&
+           opt_event.newDirEntry.rootType ===
+               VolumeManagerCommon.RootType.MY_FILES);
+      this.searchBox_.setHidden(isMyFiles);
+    }
   }
 
-  // When the search text is changed, finishes the search and showes back
-  // the last directory by passing an empty string to
-  // {@code DirectoryModel.search()}.
-  if (this.directoryModel_.isSearching() &&
-      this.directoryModel_.getLastSearchQuery() != searchString) {
-    this.directoryModel_.search('', () => {}, () => {});
-  }
+  /**
+   * Handles text change event.
+   * @private
+   */
+  onTextChange_() {
+    const searchString = this.searchBox_.inputElement.value.trimLeft();
 
-  this.requestAutocompleteSuggestions_();
-};
-
-/**
- * Updates autocompletion items.
- * @private
- */
-SearchController.prototype.requestAutocompleteSuggestions_ = function() {
-  // Remember the most recent query. If there is an other request in progress,
-  // then it's result will be discarded and it will call a new request for
-  // this query.
-  const searchString = this.searchBox_.inputElement.value.trimLeft();
-  this.lastAutocompleteQuery_ = searchString;
-  if (this.autocompleteSuggestionsBusy_) {
-    return;
-  }
-
-  // Clear search if the query empty.
-  if (!searchString) {
-    this.searchBox_.autocompleteList.suggestions = [];
-    return;
-  }
-
-  // Add header item.
-  const headerItem = /** @type {SearchItem} */ (
-      {isHeaderItem: true, searchQuery: searchString});
-  if (!this.searchBox_.autocompleteList.dataModel ||
-      this.searchBox_.autocompleteList.dataModel.length == 0) {
-    this.searchBox_.autocompleteList.suggestions = [headerItem];
-  } else {
-    // Updates only the head item to prevent a flickering on typing.
-    this.searchBox_.autocompleteList.dataModel.splice(0, 1, headerItem);
-  }
-
-  // The autocomplete list should be resized and repositioned here as the
-  // search box is resized when it's focused.
-  this.searchBox_.autocompleteList.syncWidthAndPositionToInput();
-  this.autocompleteSuggestionsBusy_ = true;
-
-  chrome.fileManagerPrivate.searchDriveMetadata(
-      {
-        query: searchString,
-        types: 'ALL',
-        maxResults: 4,
-      },
-      suggestions => {
-        this.autocompleteSuggestionsBusy_ = false;
-
-        // Discard results for previous requests and fire a new search
-        // for the most recent query.
-        if (searchString != this.lastAutocompleteQuery_) {
-          this.requestAutocompleteSuggestions_();
-          return;
-        }
-
-        // Keeps the items in the suggestion list.
-        this.searchBox_.autocompleteList.suggestions =
-            [headerItem].concat(suggestions);
-      });
-};
-
-/**
- * Opens the currently selected suggestion item.
- * @private
- */
-SearchController.prototype.onItemSelect_ = function() {
-  const selectedItem = this.searchBox_.autocompleteList.selectedItem;
-
-  // Clear the current auto complete list.
-  this.lastAutocompleteQuery_ = '';
-  this.searchBox_.autocompleteList.suggestions = [];
-
-  // If the entry is the search item or no entry is selected, just change to
-  // the search result.
-  if (!selectedItem || selectedItem.isHeaderItem) {
-    const query = selectedItem ? selectedItem.searchQuery :
-                                 this.searchBox_.inputElement.value;
-    this.search_(query);
-    return;
-  }
-
-  // Clear the search box if an item except for the search item is
-  // selected. Eventually the following directory change clears the search box,
-  // but if the selected item is located just under /drive/other, the current
-  // directory will not changed. For handling the case, and for improving
-  // response time, clear the text manually here.
-  this.clear();
-
-  // If the entry is a directory, just change the directory.
-  const entry = selectedItem.entry;
-  if (entry.isDirectory) {
-    this.directoryModel_.changeDirectoryEntry(entry);
-    return;
-  }
-
-  // Change the current directory to the directory that contains the
-  // selected file. Note that this is necessary for an image or a video,
-  // which should be opened in the gallery mode, as the gallery mode
-  // requires the entry to be in the current directory model. For
-  // consistency, the current directory is always changed regardless of
-  // the file type.
-  entry.getParent(parentEntry => {
-    // Check if the parent entry points /drive/other or not.
-    // If so it just opens the file.
-    const locationInfo = this.volumeManager_.getLocationInfo(parentEntry);
-    if (!locationInfo ||
-        (locationInfo.isRootEntry &&
-         locationInfo.rootType === VolumeManagerCommon.RootType.DRIVE_OTHER)) {
-      this.taskController_.executeEntryTask(entry);
+    // On drive, incremental search is not invoked since we have an auto-
+    // complete suggestion instead.
+    if (!this.isOnDrive_) {
+      this.search_(searchString);
       return;
     }
-    // If the parent entry can be /drive/other.
-    this.directoryModel_.changeDirectoryEntry(parentEntry, () => {
-      this.directoryModel_.selectEntry(entry);
-      this.taskController_.executeEntryTask(entry);
-    });
-  });
-};
 
-/**
- * Search files and update the list with the search result.
- * @param {string} searchString String to be searched with.
- * @private
- */
-SearchController.prototype.search_ = function(searchString) {
-  const onSearchRescan = function() {
-    // If the current location is somewhere in Drive, all files in Drive can
-    // be listed as search results regardless of current location.
-    // In this case, showing current location is confusing, so use the Drive
-    // root "My Drive" as the current location.
-    if (this.isOnDrive_) {
-      const locationInfo = this.currentLocationInfo_;
-      const rootEntry = locationInfo.volumeInfo.displayRoot;
-      if (rootEntry) {
-        this.locationLine_.show(rootEntry);
-      }
+    // When the search text is changed, finishes the search and showes back
+    // the last directory by passing an empty string to
+    // {@code DirectoryModel.search()}.
+    if (this.directoryModel_.isSearching() &&
+        this.directoryModel_.getLastSearchQuery() != searchString) {
+      this.directoryModel_.search('', () => {}, () => {});
     }
-  };
 
-  const onClearSearch = function() {
-    this.locationLine_.show(this.directoryModel_.getCurrentDirEntry());
-  };
+    this.requestAutocompleteSuggestions_();
+  }
 
-  this.directoryModel_.search(
-      searchString, onSearchRescan.bind(this), onClearSearch.bind(this));
-};
+  /**
+   * Updates autocompletion items.
+   * @private
+   */
+  requestAutocompleteSuggestions_() {
+    // Remember the most recent query. If there is an other request in progress,
+    // then it's result will be discarded and it will call a new request for
+    // this query.
+    const searchString = this.searchBox_.inputElement.value.trimLeft();
+    this.lastAutocompleteQuery_ = searchString;
+    if (this.autocompleteSuggestionsBusy_) {
+      return;
+    }
+
+    // Clear search if the query empty.
+    if (!searchString) {
+      this.searchBox_.autocompleteList.suggestions = [];
+      return;
+    }
+
+    // Add header item.
+    const headerItem = /** @type {SearchItem} */ (
+        {isHeaderItem: true, searchQuery: searchString});
+    if (!this.searchBox_.autocompleteList.dataModel ||
+        this.searchBox_.autocompleteList.dataModel.length == 0) {
+      this.searchBox_.autocompleteList.suggestions = [headerItem];
+    } else {
+      // Updates only the head item to prevent a flickering on typing.
+      this.searchBox_.autocompleteList.dataModel.splice(0, 1, headerItem);
+    }
+
+    // The autocomplete list should be resized and repositioned here as the
+    // search box is resized when it's focused.
+    this.searchBox_.autocompleteList.syncWidthAndPositionToInput();
+    this.autocompleteSuggestionsBusy_ = true;
+
+    chrome.fileManagerPrivate.searchDriveMetadata(
+        {
+          query: searchString,
+          types: 'ALL',
+          maxResults: 4,
+        },
+        suggestions => {
+          this.autocompleteSuggestionsBusy_ = false;
+
+          // Discard results for previous requests and fire a new search
+          // for the most recent query.
+          if (searchString != this.lastAutocompleteQuery_) {
+            this.requestAutocompleteSuggestions_();
+            return;
+          }
+
+          // Keeps the items in the suggestion list.
+          this.searchBox_.autocompleteList.suggestions =
+              [headerItem].concat(suggestions);
+        });
+  }
+
+  /**
+   * Opens the currently selected suggestion item.
+   * @private
+   */
+  onItemSelect_() {
+    const selectedItem = this.searchBox_.autocompleteList.selectedItem;
+
+    // Clear the current auto complete list.
+    this.lastAutocompleteQuery_ = '';
+    this.searchBox_.autocompleteList.suggestions = [];
+
+    // If the entry is the search item or no entry is selected, just change to
+    // the search result.
+    if (!selectedItem || selectedItem.isHeaderItem) {
+      const query = selectedItem ? selectedItem.searchQuery :
+                                   this.searchBox_.inputElement.value;
+      this.search_(query);
+      return;
+    }
+
+    // Clear the search box if an item except for the search item is
+    // selected. Eventually the following directory change clears the search
+    // box, but if the selected item is located just under /drive/other, the
+    // current directory will not changed. For handling the case, and for
+    // improving response time, clear the text manually here.
+    this.clear();
+
+    // If the entry is a directory, just change the directory.
+    const entry = selectedItem.entry;
+    if (entry.isDirectory) {
+      this.directoryModel_.changeDirectoryEntry(entry);
+      return;
+    }
+
+    // Change the current directory to the directory that contains the
+    // selected file. Note that this is necessary for an image or a video,
+    // which should be opened in the gallery mode, as the gallery mode
+    // requires the entry to be in the current directory model. For
+    // consistency, the current directory is always changed regardless of
+    // the file type.
+    entry.getParent(parentEntry => {
+      // Check if the parent entry points /drive/other or not.
+      // If so it just opens the file.
+      const locationInfo = this.volumeManager_.getLocationInfo(parentEntry);
+      if (!locationInfo ||
+          (locationInfo.isRootEntry &&
+           locationInfo.rootType ===
+               VolumeManagerCommon.RootType.DRIVE_OTHER)) {
+        this.taskController_.executeEntryTask(entry);
+        return;
+      }
+      // If the parent entry can be /drive/other.
+      this.directoryModel_.changeDirectoryEntry(parentEntry, () => {
+        this.directoryModel_.selectEntry(entry);
+        this.taskController_.executeEntryTask(entry);
+      });
+    });
+  }
+
+  /**
+   * Search files and update the list with the search result.
+   * @param {string} searchString String to be searched with.
+   * @private
+   */
+  search_(searchString) {
+    const onSearchRescan = function() {
+      // If the current location is somewhere in Drive, all files in Drive can
+      // be listed as search results regardless of current location.
+      // In this case, showing current location is confusing, so use the Drive
+      // root "My Drive" as the current location.
+      if (this.isOnDrive_) {
+        const locationInfo = this.currentLocationInfo_;
+        const rootEntry = locationInfo.volumeInfo.displayRoot;
+        if (rootEntry) {
+          this.locationLine_.show(rootEntry);
+        }
+      }
+    };
+
+    const onClearSearch = function() {
+      this.locationLine_.show(this.directoryModel_.getCurrentDirEntry());
+    };
+
+    this.directoryModel_.search(
+        searchString, onSearchRescan.bind(this), onClearSearch.bind(this));
+  }
+}