• 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  this.inputElement.ownerDocument.addEventListener('dragover',
53                                                   this.onDragEnter_.bind(this),
54                                                   true);
55  this.inputElement.ownerDocument.addEventListener('dragend',
56                                                   this.onDragEnd_.bind(this),
57                                                   true);
58  element.querySelector('.icon').addEventListener(
59      'click', this.onIconClick_.bind(this));
60  element.parentNode.appendChild(this.autocompleteList);
61}
62
63/**
64 * Autocomplete list for search box.
65 * @param {HTMLDocument} document Document.
66 * @constructor
67 */
68SearchBox.AutocompleteList = function(document) {
69  var self = cr.ui.AutocompleteList.call(this);
70  self.__proto__ = SearchBox.AutocompleteList.prototype;
71  self.id = 'autocomplete-list';
72  self.autoExpands = true;
73  self.itemConstructor = SearchBox.AutocompleteListItem_.bind(null, document);
74  self.addEventListener('mouseover', self.onMouseOver_.bind(self));
75  return self;
76};
77
78SearchBox.AutocompleteList.prototype = {
79  __proto__: cr.ui.AutocompleteList.prototype
80};
81
82/**
83 * Do nothing when a suggestion is selected.
84 * @override
85 */
86SearchBox.AutocompleteList.prototype.handleSelectedSuggestion = function() {};
87
88/**
89 * Change the selection by a mouse over instead of just changing the
90 * color of moused over element with :hover in CSS. Here's why:
91 *
92 * 1) The user selects an item A with up/down keys (item A is highlighted)
93 * 2) Then the user moves the cursor to another item B
94 *
95 * If we just change the color of moused over element (item B), both
96 * the item A and B are highlighted. This is bad. We should change the
97 * selection so only the item B is highlighted.
98 *
99 * @param {Event} event Event.
100 * @private
101 */
102SearchBox.AutocompleteList.prototype.onMouseOver_ = function(event) {
103  if (event.target.itemInfo)
104    this.selectedItem = event.target.itemInfo;
105};
106
107/**
108 * ListItem element for autocomplete.
109 *
110 * @param {HTMLDocument} document Document.
111 * @param {Object} item An object representing a suggestion.
112 * @constructor
113 * @private
114 */
115SearchBox.AutocompleteListItem_ = function(document, item) {
116  var li = new cr.ui.ListItem();
117  li.itemInfo = item;
118
119  var icon = document.createElement('div');
120  icon.className = 'detail-icon';
121
122  var text = document.createElement('div');
123  text.className = 'detail-text';
124
125  if (item.isHeaderItem) {
126    icon.setAttribute('search-icon', '');
127    text.innerHTML =
128        strf('SEARCH_DRIVE_HTML', util.htmlEscape(item.searchQuery));
129  } else {
130    var iconType = FileType.getIcon(item.entry);
131    icon.setAttribute('file-type-icon', iconType);
132    // highlightedBaseName is a piece of HTML with meta characters properly
133    // escaped. See the comment at fileBrowserPrivate.searchDriveMetadata().
134    text.innerHTML = item.highlightedBaseName;
135  }
136  li.appendChild(icon);
137  li.appendChild(text);
138  return li;
139};
140
141/**
142 * Clears the search query.
143 */
144SearchBox.prototype.clear = function() {
145  this.inputElement.value = '';
146  this.updateStyles_();
147};
148
149/**
150 * Handles a focus event of the search box.
151 * @private
152 */
153SearchBox.prototype.onFocus_ = function() {
154  this.element.classList.toggle('has-cursor', true);
155  this.inputElement.tabIndex = '99';  // See: go/filesapp-tabindex.
156  this.autocompleteList.attachToInput(this.inputElement);
157};
158
159/**
160 * Handles a blur event of the search box.
161 * @private
162 */
163SearchBox.prototype.onBlur_ = function() {
164  this.element.classList.toggle('has-cursor', false);
165  this.inputElement.tabIndex = '-1';
166  this.autocompleteList.detach();
167};
168
169/**
170 * Handles a keydown event of the search box.
171 * @private
172 */
173SearchBox.prototype.onKeyDown_ = function() {
174  // Handle only Esc key now.
175  if (event.keyCode != 27 || this.inputElement.value)
176    return;
177  this.inputElement.blur();
178};
179
180/**
181 * Handles a click event of the search icon.
182 * @private
183 */
184SearchBox.prototype.onIconClick_ = function() {
185  this.inputElement.focus();
186};
187
188/**
189 * Handles a dragenter event and refuses a drag source of files.
190 * @param {DragEvent} event The dragenter event.
191 * @private
192 */
193SearchBox.prototype.onDragEnter_ = function(event) {
194  // For normal elements, they does not accept drag drop by default, and accept
195  // it by using event.preventDefault. But input elements accept drag drop
196  // by default. So disable the input element here to prohibit drag drop.
197  if (event.dataTransfer.types.indexOf('text/plain') === -1)
198    this.inputElement.style.pointerEvents = 'none';
199};
200
201/**
202 * Handles a dragend event.
203 * @private
204 */
205SearchBox.prototype.onDragEnd_ = function() {
206  this.inputElement.style.pointerEvents = '';
207};
208
209/**
210 * Updates styles of the search box.
211 * @private
212 */
213SearchBox.prototype.updateStyles_ = function() {
214  this.element.classList.toggle('has-text',
215                                 !!this.inputElement.value);
216  var width = this.textMeasure_.getWidth(this.inputElement.value) +
217      16 /* Extra space to allow leeway. */;
218  this.inputElement.style.width = width + 'px';
219};
220