• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5'use strict';
6
7/**
8 * Search box.
9 *
10 * @param {element} element Root element of the search box.
11 * @constructor
12 */
13function SearchBox(element) {
14  /**
15   * Autocomplete List.
16   * @type {AutocompleteList}
17   */
18  this.autocompleteList = new SearchBox.AutocompleteList(element.ownerDocument);
19
20  /**
21   * Root element of the search box.
22   * @type {HTMLElement}
23   */
24  this.element = element;
25
26  /**
27   * Text input of the search box.
28   * @type {HTMLElement}
29   */
30  this.inputElement = element.querySelector('input');
31
32  /**
33   * Clear button of the search box.
34   * @type {HTMLElement}
35   */
36  this.clearButton = element.querySelector('.clear');
37
38  /**
39   * Text measure.
40   * @type {TextMeasure}
41   * @private
42   */
43  this.textMeasure_ = new TextMeasure(this.inputElement);
44
45  Object.freeze(this);
46
47  // Register events.
48  this.inputElement.addEventListener('input', this.updateStyles_.bind(this));
49  this.inputElement.addEventListener('keydown', this.onKeyDown_.bind(this));
50  this.inputElement.addEventListener('focus', this.onFocus_.bind(this));
51  this.inputElement.addEventListener('blur', this.onBlur_.bind(this));
52  element.querySelector('.icon').addEventListener(
53      'click', this.onIconClick_.bind(this));
54  element.parentNode.appendChild(this.autocompleteList);
55}
56
57/**
58 * Autocomplete list for search box.
59 * @param {HTMLDocument} document Document.
60 * @constructor
61 */
62SearchBox.AutocompleteList = function(document) {
63  var self = cr.ui.AutocompleteList.call(this);
64  self.__proto__ = SearchBox.AutocompleteList.prototype;
65  self.id = 'autocomplete-list';
66  self.autoExpands = true;
67  self.itemConstructor = SearchBox.AutocompleteListItem_.bind(null, document);
68  self.addEventListener('mouseover', self.onMouseOver_.bind(self));
69  return self;
70};
71
72SearchBox.AutocompleteList.prototype = {
73  __proto__: cr.ui.AutocompleteList.prototype
74};
75
76/**
77 * Do nothing when a suggestion is selected.
78 * @override
79 */
80SearchBox.AutocompleteList.prototype.handleSelectedSuggestion = function() {};
81
82/**
83 * Change the selection by a mouse over instead of just changing the
84 * color of moused over element with :hover in CSS. Here's why:
85 *
86 * 1) The user selects an item A with up/down keys (item A is highlighted)
87 * 2) Then the user moves the cursor to another item B
88 *
89 * If we just change the color of moused over element (item B), both
90 * the item A and B are highlighted. This is bad. We should change the
91 * selection so only the item B is highlighted.
92 *
93 * @param {Event} event Event.
94 * @private
95 */
96SearchBox.AutocompleteList.prototype.onMouseOver_ = function(event) {
97  if (event.target.itemInfo)
98    this.selectedItem = event.target.itemInfo;
99};
100
101/**
102 * ListItem element for autocomple.
103 *
104 * @param {HTMLDocument} document Document.
105 * @param {Object} item An object representing a suggestion.
106 * @constructor
107 * @private
108 */
109SearchBox.AutocompleteListItem_ = function(document, item) {
110  var li = new cr.ui.ListItem();
111  li.itemInfo = item;
112
113  var icon = document.createElement('div');
114  icon.className = 'detail-icon';
115
116  var text = document.createElement('div');
117  text.className = 'detail-text';
118
119  if (item.isHeaderItem) {
120    icon.setAttribute('search-icon', '');
121    text.innerHTML =
122        strf('SEARCH_DRIVE_HTML', util.htmlEscape(item.searchQuery));
123  } else {
124    var iconType = FileType.getIcon(item.entry);
125    icon.setAttribute('file-type-icon', iconType);
126    // highlightedBaseName is a piece of HTML with meta characters properly
127    // escaped. See the comment at fileBrowserPrivate.searchDriveMetadata().
128    text.innerHTML = item.highlightedBaseName;
129  }
130  li.appendChild(icon);
131  li.appendChild(text);
132  return li;
133};
134
135/**
136 * Updates the size related style.
137 */
138SearchBox.prototype.updateSizeRelatedStyle = function() {
139  // Hide the search box if there is not enough space.
140  this.element.classList.toggle(
141      'too-short',
142      this.element.clientWidth < 100);
143};
144
145/**
146 * Clears the search query.
147 */
148SearchBox.prototype.clear = function() {
149  this.inputElement.value = '';
150  this.updateStyles_();
151};
152
153/**
154 * Handles a focus event of the search box.
155 * @private
156 */
157SearchBox.prototype.onFocus_ = function() {
158  this.element.classList.toggle('has-cursor', true);
159  this.inputElement.tabIndex = '99';  // See: go/filesapp-tabindex.
160  this.autocompleteList.attachToInput(this.inputElement);
161};
162
163/**
164 * Handles a blur event of the search box.
165 * @private
166 */
167SearchBox.prototype.onBlur_ = function() {
168  this.element.classList.toggle('has-cursor', false);
169  this.inputElement.tabIndex = '-1';
170  this.autocompleteList.detach();
171};
172
173/**
174 * Handles a keydown event of the search box.
175 * @private
176 */
177SearchBox.prototype.onKeyDown_ = function() {
178  // Handle only Esc key now.
179  if (event.keyCode != 27 || this.inputElement.value)
180    return;
181  this.inputElement.blur();
182};
183
184/**
185 * Handles a click event of the search icon.
186 * @private
187 */
188SearchBox.prototype.onIconClick_ = function() {
189  this.inputElement.focus();
190};
191
192/**
193 * Updates styles of the search box.
194 * @private
195 */
196SearchBox.prototype.updateStyles_ = function() {
197  this.element.classList.toggle('has-text',
198                                 !!this.inputElement.value);
199  var width = this.textMeasure_.getWidth(this.inputElement.value) +
200      16 /* Extra space to allow leeway. */;
201  this.inputElement.style.width = width + 'px';
202};
203