1 function Autocompleter(inputElement, list) {
2 this._inputElement = inputElement;
3 this._currentSelection = undefined;
6 this._currentFilter = null;
7 this._candidateWindow = null;
9 inputElement.addEventListener('focus', this.show.bind(this));
10 inputElement.addEventListener('blur', this.hide.bind(this));
11 inputElement.addEventListener('keyup', this.update.bind(this));
12 inputElement.addEventListener('keydown', this.navigate.bind(this));
15 Autocompleter.prototype._ensureCandidateWindow = function () {
16 if (this._candidateWindow)
19 var container = element('ul');
20 container.className = 'candidateWindow';
21 container.style.position = 'absolute';
22 container.style.display = 'none';
23 this._inputElement.parentNode.appendChild(container);
24 this._candidateWindow = container;
27 Autocompleter.prototype._updateCandidates = function (filter) {
28 if (this._currentFilter == filter)
31 var candidates = this._list.filter(function (testName) { return testName.indexOf(filter) >= 0; });
32 if (candidates.length > 50 || candidates.length == 1)
34 this._candidates = candidates;
35 this._currentFilter = filter;
36 this._currentSelection = undefined;
40 Autocompleter.prototype._showCandidateWindow = function () {
41 if (!this._candidateWindow)
43 var style = this._candidateWindow.style;
44 style.display = 'block';
45 style.top = this._inputElement.offsetTop + this._inputElement.offsetHeight + 'px';
46 style.left = this._inputElement.offsetLeft + 'px';
49 Autocompleter.prototype._createItem = function (candidate) {
50 var tokens = candidate.split(this._currentFilter);
52 for (var i = 0; i < tokens.length; i++) {
53 children.push(text(tokens[i]));
54 if (i + 1 < tokens.length)
55 children.push(element('em', [this._currentFilter]));
57 return element('li', children);
60 Autocompleter.prototype.show = function () {
62 this._ensureCandidateWindow();
64 for (var i = 0; i < this._candidates.length; i++)
65 this._candidateWindow.appendChild(this._createItem(this._candidates[i]));
66 this._selectItem(this._currentSelection);
68 if (this._candidates.length)
69 this._showCandidateWindow();
72 Autocompleter.prototype.update = function () {
73 if (this._updateCandidates(this._inputElement.value))
77 Autocompleter.prototype.hide = function () {
78 if (!this._candidateWindow)
80 this._candidateWindow.style.display = 'none';
81 this._candidateWindow.innerHTML = '';
84 Autocompleter.prototype._selectItem = function (index) {
85 if (!this._candidateWindow || this._currentSelection == index)
88 var item = this._candidateWindow.childNodes[index];
91 item.classList.add('selected');
93 var oldItem = this._candidateWindow.childNodes[this._currentSelection];
95 oldItem.classList.remove('selected');
97 this._currentSelection = index;
100 Autocompleter.prototype.navigate = function (event) {
101 if (event.keyCode == 0x28 /* DOM_VK_DOWN */) {
102 this._selectItem(this._currentSelection === undefined ? 0 : Math.min(this._currentSelection + 1, this._candidates.length - 1));
103 event.preventDefault();
104 } else if (event.keyCode == 0x26 /* DOM_VK_UP */) {
105 this._selectItem(this._currentSelection === undefined ? this._candidates.length - 1 : Math.max(this._currentSelection - 1, 0));
106 event.preventDefault();
107 } else if (event.keyCode == 0x0D /* VK_RETURN */) {
108 if (this._currentSelection === undefined)
110 this._inputElement.value = this._candidates[this._currentSelection];
111 } else if (event.keyCode == 0x1B /* DOM_VK_ESCAPE */) {