OLD | NEW |
| (Empty) |
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 */ | |
13 function 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 */ | |
68 SearchBox.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 | |
78 SearchBox.AutocompleteList.prototype = { | |
79 __proto__: cr.ui.AutocompleteList.prototype | |
80 }; | |
81 | |
82 /** | |
83 * Do nothing when a suggestion is selected. | |
84 * @override | |
85 */ | |
86 SearchBox.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 */ | |
102 SearchBox.AutocompleteList.prototype.onMouseOver_ = function(event) { | |
103 if (event.target.itemInfo) | |
104 this.selectedItem = event.target.itemInfo; | |
105 }; | |
106 | |
107 /** | |
108 * ListItem element for autocomple. | |
109 * | |
110 * @param {HTMLDocument} document Document. | |
111 * @param {Object} item An object representing a suggestion. | |
112 * @constructor | |
113 * @private | |
114 */ | |
115 SearchBox.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 */ | |
144 SearchBox.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 */ | |
153 SearchBox.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 */ | |
163 SearchBox.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 */ | |
173 SearchBox.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 */ | |
184 SearchBox.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 */ | |
193 SearchBox.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 disalbe 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 */ | |
205 SearchBox.prototype.onDragEnd_ = function() { | |
206 this.inputElement.style.pointerEvents = ''; | |
207 }; | |
208 | |
209 /** | |
210 * Updates styles of the search box. | |
211 * @private | |
212 */ | |
213 SearchBox.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 }; | |
OLD | NEW |