2021-02-14 08:30:20 +01:00
|
|
|
|
2021-02-22 07:59:51 +01:00
|
|
|
/*Singleton object*/
|
|
|
|
var tag_suggestion_list = {
|
|
|
|
input_el: null,
|
|
|
|
list_element: document.createElement('ol'),
|
2021-02-14 08:30:20 +01:00
|
|
|
suggestion_elements: [],
|
2021-02-22 07:59:51 +01:00
|
|
|
hover_last: -1,
|
2021-02-14 08:30:20 +01:00
|
|
|
}
|
2021-02-22 07:59:51 +01:00
|
|
|
tag_suggestion_list.list_element.setAttribute("class","tag-suggestion-list");
|
|
|
|
tag_suggestion_list.list_element.setAttribute("style","position:absolute;");
|
2021-02-14 08:30:20 +01:00
|
|
|
|
2021-02-22 07:59:51 +01:00
|
|
|
function appendTag(name){
|
|
|
|
return function(event){
|
|
|
|
var ie = tag_suggestion_list.input_el;
|
|
|
|
var prev = ie.value.split(";");
|
|
|
|
prev.pop();
|
|
|
|
prev.push(name);
|
|
|
|
ie.value = prev.join(";");
|
|
|
|
ie.value += ";";
|
|
|
|
ie.focus();
|
|
|
|
tag_suggestion_list.list_element.style = "display:none;";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function hoverTag(name, root){
|
|
|
|
return function(event){
|
|
|
|
var ie = tag_suggestion_list.input_el;
|
|
|
|
if(ie.value.slice(-1) == ";"){//comming from another tab completion
|
|
|
|
var prev = ie.value.slice(hover_last);
|
|
|
|
|
|
|
|
}else{
|
|
|
|
var prev = ie.value.split(";");
|
|
|
|
prev.pop()
|
|
|
|
prev.push(name)
|
|
|
|
ie.value = prev.join(";");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-02-14 08:30:20 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Stolen from medium.com/@jh3y
|
|
|
|
* returns x, y coordinates for absolute positioning of a span within a given text input
|
|
|
|
* at a given selection point
|
|
|
|
* @param {object} input - the input element to obtain coordinates for
|
|
|
|
* @param {number} selectionPoint - the selection point for the input
|
|
|
|
*/
|
|
|
|
function getCursorXY(input, selectionPoint){
|
2021-02-22 07:59:51 +01:00
|
|
|
const {
|
|
|
|
offsetLeft: inputX,
|
|
|
|
offsetTop: inputY,
|
|
|
|
} = input
|
|
|
|
const div = document.createElement('div')
|
|
|
|
const copyStyle = getComputedStyle(input)
|
|
|
|
for (const prop of copyStyle) {
|
|
|
|
div.style[prop] = copyStyle[prop]
|
|
|
|
}
|
|
|
|
const swap = '.'
|
|
|
|
const inputValue = input.tagName === 'INPUT' ? input.value.replace(/ /g, swap) : input.value
|
|
|
|
const textContent = inputValue.substr(0, selectionPoint)
|
|
|
|
div.textContent = textContent
|
|
|
|
if (input.tagName === 'TEXTAREA') div.style.height = 'auto'
|
|
|
|
if (input.tagName === 'INPUT') div.style.width = 'auto'
|
|
|
|
const span = document.createElement('span')
|
|
|
|
span.textContent = inputValue.substr(selectionPoint) || '.'
|
|
|
|
div.appendChild(span)
|
|
|
|
document.body.appendChild(div)
|
|
|
|
const { offsetLeft: spanX, offsetTop: spanY } = span
|
|
|
|
document.body.removeChild(div)
|
|
|
|
return {
|
|
|
|
x: inputX + spanX,
|
|
|
|
y: inputY + spanY,
|
|
|
|
}
|
2021-02-14 08:30:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function display_suggestions(elem, sugg, event){
|
|
|
|
//Check that the value hasn't change since we fired
|
|
|
|
//off the request
|
2021-02-22 07:59:51 +01:00
|
|
|
var tags_so_far = elem.value.split(";");
|
2021-02-14 08:30:20 +01:00
|
|
|
recent = elem.value.split(";").pop().trim();
|
|
|
|
if(recent == sugg[0]){
|
2021-02-22 07:59:51 +01:00
|
|
|
var v = getCursorXY(elem,elem.value.length);
|
|
|
|
var sugx = v.x;
|
|
|
|
var sugy = v.y;
|
|
|
|
var sty = `position:absolute; margin-left:${sugx}px;`;
|
|
|
|
tag_suggestion_list.list_element.style = sty;
|
|
|
|
for(var i in tag_suggestion_list.suggestion_elements){
|
|
|
|
tag_suggestion_list.list_element.removeChild(tag_suggestion_list.suggestion_elements[i]);
|
|
|
|
|
|
|
|
}
|
|
|
|
tag_suggestion_list.suggestion_elements = [];
|
|
|
|
var hover_last = 0;
|
|
|
|
for(var i in tags_so_far){
|
|
|
|
hover_last += tags_so_far[i].length + 1;
|
|
|
|
}
|
|
|
|
tag_suggestion_list.hover_last = hover_last;
|
2021-02-14 08:30:20 +01:00
|
|
|
for(var i in sugg){
|
2021-02-22 07:59:51 +01:00
|
|
|
if(i == 0){
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
var suggestion_el = document.createElement("li");
|
|
|
|
var suggestion_but = document.createElement("input")
|
|
|
|
suggestion_el.appendChild(suggestion_but);
|
|
|
|
suggestion_but.setAttribute("type","button");
|
|
|
|
suggestion_but.setAttribute("value",sugg[i]);
|
|
|
|
suggestion_el.setAttribute("class"," button-clear tag-suggestion");
|
|
|
|
tag_suggestion_list.list_element.appendChild(suggestion_el);
|
|
|
|
tag_suggestion_list.suggestion_elements.push(suggestion_el);
|
|
|
|
suggestion_but.onkeyup = function(event){
|
|
|
|
if(event.key == "Tab"){
|
|
|
|
hoverTag(event.target.value)(event);
|
|
|
|
}else if(event.key == ";"){
|
|
|
|
appendTag(event.target.value)(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
suggestion_but.onclick = function(event){
|
|
|
|
appendTag(event.target.value)(event);
|
|
|
|
}
|
|
|
|
suggestion_but.onblur = function(event){
|
|
|
|
var other_input = false;
|
|
|
|
for(var i in tag_suggestion_list.suggestion_elements){
|
|
|
|
if(tag_suggestion_list.suggestion_elements[i].firstChild == event.relatedTarget){
|
|
|
|
other_input = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
if(!other_input){
|
|
|
|
tag_suggestion_list.list_element.style = "display:none;";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
if(tag_suggestion_list.suggestion_elements.length > 0){
|
|
|
|
|
|
|
|
var last_element = tag_suggestion_list.suggestion_elements[tag_suggestion_list.suggestion_elements.length - 1];
|
|
|
|
//last_element.firstChild.last_element = true;
|
|
|
|
last_element.firstChild.onblur = function(event){
|
|
|
|
tag_suggestion_list.suggestion_elements[0].firstChild.focus();
|
|
|
|
|
|
|
|
}
|
2021-02-14 08:30:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function hint_tags(elem, event){
|
|
|
|
//Get the most recent tag
|
2021-02-22 07:59:51 +01:00
|
|
|
var recent = elem.value.split(";").pop().trim();
|
2021-02-14 08:30:20 +01:00
|
|
|
if(recent.length > 0){
|
|
|
|
//Ask the server for tags that look like this
|
2021-02-22 07:59:51 +01:00
|
|
|
var xhr = new XMLHttpRequest();
|
2021-02-14 08:30:20 +01:00
|
|
|
xhr.open("GET", "/_api?call=suggest&data=" + recent);
|
|
|
|
xhr.onreadystatechange = function(e){
|
|
|
|
if(xhr.readyState === 4){
|
|
|
|
suggestions = xhr.response.split(";");
|
|
|
|
display_suggestions(elem,suggestions, event);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
xhr.send()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function init(){
|
2021-02-22 07:59:51 +01:00
|
|
|
var head_el = document.head;
|
|
|
|
var extra_css_el = document.createElement("link");
|
|
|
|
document.head.appendChild(extra_css_el);
|
|
|
|
extra_css_el.setAttribute("rel","stylesheet");
|
|
|
|
extra_css_el.setAttribute("href","/_css/suggest_tags.css");
|
|
|
|
var tag_el_list = document.getElementsByName("tags");
|
2021-02-14 08:30:20 +01:00
|
|
|
console.assert(tag_el_list.length == 1);
|
2021-02-22 07:59:51 +01:00
|
|
|
var tag_el = tag_el_list[0];
|
|
|
|
tag_suggestion_list.input_el = tag_el;
|
2021-02-14 08:30:20 +01:00
|
|
|
tag_el.onkeyup = function(event){
|
|
|
|
hint_tags(tag_el, event);
|
|
|
|
}
|
2021-02-22 07:59:51 +01:00
|
|
|
tag_el.onblur = function(event){
|
|
|
|
var not_suggestion = true;
|
|
|
|
var ies = tag_suggestion_list.suggestion_elements;
|
|
|
|
for(var i in ies){
|
|
|
|
if(event.relatedTarget == ies[i].firstChild){
|
|
|
|
not_suggestion = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(not_suggestion){
|
|
|
|
tag_suggestion_list.list_element.style = "display:none;";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var fieldset = tag_el.parentNode;
|
|
|
|
fieldset.appendChild(tag_suggestion_list.list_element);
|
|
|
|
var paste_el = document.getElementsByName("tags");
|
2021-02-14 08:30:20 +01:00
|
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded",init,false);
|