pics/public/js/autosuggest.js

178 lines
5.9 KiB
JavaScript
Raw Permalink Normal View History

/*
Copyright (c) 2015, Aaron van Geffen
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of conditions
and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the distribution.
*/
'use strict';
class AutoSuggest {
constructor(opt) {
if (typeof opt.inputElement === "undefined" || typeof opt.listElement === "undefined" ||
typeof opt.baseUrl === "undefined" || typeof opt.appendCallback === "undefined") {
return;
}
this.input = document.getElementById(opt.inputElement);
this.input.autocomplete = "off";
this.list = document.getElementById(opt.listElement);
this.appendCallback = opt.appendCallback;
this.baseurl = opt.baseUrl;
this.input.addEventListener('keydown', event => this.doSelection(event), false);
this.input.addEventListener('keyup', event => this.onType(event), false);
}
doSelection(event) {
if (typeof this.container === "undefined" || this.container.children.length === 0) {
return;
}
switch (event.key) {
case 'Enter':
event.preventDefault();
this.container.children[this.selectedIndex].click();
break;
case 'ArrowUp':
case 'ArrowDown':
event.preventDefault();
this.findSelectedElement().className = '';
this.selectedIndex += event.key === 'ArrowUp' ? -1 : 1;
if (this.selectedIndex < 0) {
this.selectedIndex = this.container.children.length - 1;
} else if (this.selectedIndex === this.container.children.length) {
this.selectedIndex = 0;
}
let new_el = this.findSelectedElement().className = 'selected';
break;
}
};
findSelectedElement() {
return this.container.children[this.selectedIndex];
};
onType(event) {
if (['Enter', 'ArrowDown', 'ArrowUp'].indexOf(event.key) !== -1) {
return;
}
let tokens = event.target.value.split(/\s+/).filter(token => token.length >= 2);
if (tokens.length === 0) {
if (typeof this.container !== "undefined") {
this.clearContainer();
}
return false;
}
let request_uri = this.baseurl + '/suggest/?type=tags&data=' + window.encodeURIComponent(tokens.join(" "));
let request = new HttpRequest('get', request_uri, {}, this.onReceive, this);
};
onReceive(response, self) {
self.openContainer();
self.clearContainer();
self.fillContainer(response);
};
openContainer() {
if (this.container) {
if (!this.container.parentNode) {
this.input.parentNode.appendChild(this.container);
}
return this.container;
}
this.container = document.createElement('ul');
this.container.className = 'autosuggest';
this.input.parentNode.appendChild(this.container);
return this.container;
};
clearContainer() {
while (this.container.children.length > 0) {
this.container.removeChild(this.container.children[0]);
}
};
clearInput() {
this.input.value = "";
this.input.focus();
};
closeContainer() {
this.container.parentNode.removeChild(this.container);
};
fillContainer(response) {
this.selectedIndex = 0;
let query = this.input.value.trim().replace(/[\-\[\]{}()*+?.,\\\/^\$|#]/g, ' ');
let query_tokens = query.split(/ +/).sort((a,b) => a.length - b.length);
response.items.forEach((item, i) => {
let node = document.createElement('li');
node.innerHTML = this.highlightMatches(query_tokens, item.label);
node.jsondata = item;
node.addEventListener('click', event => {
this.appendCallback(event.target.jsondata);
this.closeContainer();
this.clearInput();
});
this.container.appendChild(node);
if (this.container.children.length === 1) {
node.className = 'selected';
}
});
};
highlightMatches(query_tokens, item) {
let itemTokens = item.split(/ +/);
let queryTokens = new RegExp('(' + query_tokens.join('\|') + ')', 'i');
itemTokens.forEach((token, index) => {
item = item.replace(token, token.replace(queryTokens, ($1, match) => '<strong>' + match + '</strong>'));
});
return item;
};
}
class TagAutoSuggest extends AutoSuggest {
constructor(opt) {
super(opt);
this.type = "tags";
}
fillContainer(response) {
if (response.items.length > 0) {
super.fillContainer.call(this, response);
} else {
let node = document.createElement('li')
node.innerHTML = "<em>Tag does not exist yet. Create it?</em>";
node.addEventListener('click', event => {
this.createNewTag(response => this.appendCallback(response));
this.closeContainer();
this.clearInput();
});
this.container.appendChild(node);
this.selectedIndex = 0;
node.className = 'selected';
}
};
createNewTag(callback) {
let request_uri = this.baseurl + '/suggest/?type=createtag';
let request = new HttpRequest('post', request_uri, 'tag=' + encodeURIComponent(this.input.value), callback, this);
}
}