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