/* 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) => '' + match + '')); }); 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 = "Tag does not exist yet. Create it?"; 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); } }