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 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);