smr/assets/suggest_tags.js

106 lines
3.8 KiB
JavaScript

console.log("Hello, world!");
tag_suggestion_list = {
list_element: null,
suggestion_elements: [],
}
var lel = document.createElement('div');
/**
* 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){
const {
offsetLeft: inputX,
offsetTop: inputY,
} = input
// create a dummy element that will be a clone of our input
const div = document.createElement('div')
// get the computed style of the input and clone it onto the dummy element
const copyStyle = getComputedStyle(input)
for (const prop of copyStyle) {
div.style[prop] = copyStyle[prop]
}
// we need a character that will replace whitespace when filling our dummy element if it's a single line <input/>
const swap = '.'
const inputValue = input.tagName === 'INPUT' ? input.value.replace(/ /g, swap) : input.value
// set the div content to that of the textarea up until selection
const textContent = inputValue.substr(0, selectionPoint)
// set the text content of the dummy element div
div.textContent = textContent
if (input.tagName === 'TEXTAREA') div.style.height = 'auto'
// if a single line input then the div needs to be single line and not break out like a text area
if (input.tagName === 'INPUT') div.style.width = 'auto'
// create a marker element to obtain caret position
const span = document.createElement('span')
// give the span the textContent of remaining content so that the recreated dummy element is as close as possible
span.textContent = inputValue.substr(selectionPoint) || '.'
// append the span marker to the div
div.appendChild(span)
// append the dummy element to the body
document.body.appendChild(div)
// get the marker position, this is the caret position top and left relative to the input
const { offsetLeft: spanX, offsetTop: spanY } = span
// lastly, remove that dummy element
// NOTE:: can comment this out for debugging purposes if you want to see where that span is rendered
document.body.removeChild(div)
// return an object with the x and y of the caret. account for input positioning so that you don't need to wrap the input
return {
x: inputX + spanX,
y: inputY + spanY,
}
}
function display_suggestions(elem, sugg, event){
console.log("sugg:",sugg);
//Check that the value hasn't change since we fired
//off the request
recent = elem.value.split(";").pop().trim();
if(recent == sugg[0]){
var sugx, sugy = getCursorXY(elem,elem.value.length);
console.log("Looking at position to display suggestions:",sugx, sugy);
for(var i in sugg){
console.log("Displaying suggestion:",sugg[i]);
lel.setAttribute('style',`left: $(sugx)px; top: $(sugy)px;`);
}
}
}
function hint_tags(elem, event){
//Get the most recent tag
recent = elem.value.split(";").pop().trim();
if(recent.length > 0){
console.log("Most recent tag:",recent);
//Ask the server for tags that look like this
xhr = new XMLHttpRequest();
xhr.open("GET", "/_api?call=suggest&data=" + recent);
xhr.onreadystatechange = function(e){
if(xhr.readyState === 4){
console.log("Event:",e);
suggestions = xhr.response.split(";");
console.log("suggestions:",suggestions);
display_suggestions(elem,suggestions, event);
}
}
xhr.send()
}
}
function init(){
tag_el_list = document.getElementsByName("tags");
console.assert(tag_el_list.length == 1);
tag_el = tag_el_list[0];
tag_el.onkeyup = function(event){
console.log("Looking at tag:", event);
console.log("And element:",tag_el);
hint_tags(tag_el, event);
}
}
document.addEventListener("DOMContentLoaded",init,false);