let input = []; let currentWord = []; let selected = -1; window.onload = () => { document.querySelectorAll(".ime").forEach(input => { input.addEventListener("keydown", (key) => { imeDown(key, input); }); input.addEventListener("mousedown", (e) => { e.preventDefault(); input.focus(); imeUpdate(input); }) }); } function imeDown(keyEvent, inputField) { keyEvent.preventDefault(); switch (keyEvent.key) { case "Backspace": if (selected == -1) { if (currentWord.join("") == "") { currentWord = input.pop() || []; } else { imeRestore(currentWord); // currentWord.pop(); } } else { imeRestore(input[selected]); // input[selected].pop(); if (input[selected].join("") == "") { input.splice(selected,1); imeMove("left"); } } break; case "Enter": if (selected == -1) { if (currentWord.join() == "") { //blame js :) let s = document.createElement("p"); s.innerText = input.join(""); document.body.appendChild(s); input = []; selected = -1; } else { input.push(currentWord); currentWord = []; } } else { if (selected+1 >= input.length) { selected = -1; } else { selected += 1; } } imeReset(); break; case "Space": //adds a ' break; case "ArrowLeft": imeMove("left"); break; case "ArrowRight": imeMove("right"); break; case "ArrowDown": imeMove("start"); break; case "ArrowUp": imeMove("end"); break; default: imeInput(keyEvent.key); if (selected == -1) { currentWord = inputFull; } else { input[selected] = inputFull; } break; } imeUpdate(inputField); } function imeUpdate(inputField) { let renderText = ""; input.forEach((w) => { renderText += w.join(""); }) inputField.value = renderText + currentWord.join(""); if (selected == -1) { inputField.setSelectionRange(renderText.length, renderText.length+currentWord.join("").length); } else { from = 0; for (let x = 0; x < selected; x++) { from += input[x].join("").length; } to = from+input[selected].join("").length; inputField.setSelectionRange(from, to); } } function clamp(min, max, value) { return Math.max(min, Math.min(max, value)); } function imeMove(direction) { let d = 1; if (currentWord.join("") != "") { input.push(currentWord); currentWord = []; d = 2; } switch (direction) { case "start": selected = 0; break; case "end": selected = -1; break; case "left": if (selected == -1) selected = input.length; selected -= 1 * d; if (selected != -1) { selected = clamp(0,input.length,selected); //is there even a point in clamping it here,,,, } break; case "right": selected += 1 * d; selected = clamp(0,input.length,selected); //tbh same here, like. //oh well ig ???/ if (selected == input.length) { selected = -1; } break; } imeReset(); } let inputState = 0; //STARTING, COMPOSTING, TRAILING, Fucking Nothing, ...cleanup let stateState = 0; //depends on the state let inputFull = []; let inputCurrent = ""; let hVowels = { //standalone, composing, trailing "a": ["0", "5", "A"], "e": ["1", "6", "B"], "i": ["2", "7", "C"], "o": ["3", "8", "D"], "u": ["4", "9", "E"] }; let hConsonants = { //0 = standalone, 1 = composing, 2-6 = vowels "g": "01", "s": "02", "r": "03", "n": "04", "c": "05", "m": "06", "j": "07", "f": "08", "t": "09", "k": "0A", "w": "0B", "l": "0C", "p": "0D", "b": "0E", "d": "0F", "h": "10", }; function getUnicodeVowel(vowel, sot) { if (sot == "standalone") sot = 0; if (sot == "composing") sot = 1; if (sot == "trailing") sot = 2; return String.fromCharCode(parseInt("E00" + hVowels[vowel][sot], 16)); } function getUnicodeConsonant(consonant, scv) { if (scv == "standalone") {scv = 0;} else if (scv == "trailing") {scv = 1;} else {scv = Number(hVowels[scv][0])+2} return String.fromCharCode(parseInt("E" + hConsonants[consonant] + "" + scv, 16)); } function inverse(obj){ var retobj = {}; for(var key in obj){ retobj[obj[key]] = key; } return retobj; } let hVowelsK = Object.keys(hVowels) let hConsonantsK = Object.keys(hConsonants); let hConsonantsR = inverse(hConsonants); function debugInput(key) { console.log("pre "+key+": inputstate - "+inputState +", statestate - "+ stateState); console.log("inputfull: " + inputFull.join(" ") + ", inputcurrent:" + inputCurrent); imeInput(key); console.log("post "+key+": inputstate - "+inputState +", statestate - "+ stateState); console.log("inputfull: " + inputFull.join(" ") + ", inputcurrent:" + inputCurrent); console.log("------------------------------") } function imeReset() { stateState = 0; inputState = 0; inputFull = []; inputCurrent = ""; if (selected == -1) { currentWord = []; } else { input[selected] = []; } } function imeInfo(decHex) { try { let hex = decHex.codePointAt(0).toString(16); hex[1] = parseInt(hex[1], 16); hex[2] = parseInt(hex[2], 16); hex[3] = parseInt(hex[3], 16); let ka = (hex[1] == 0 && hex[2] == 0) ? "a" : "k"; if (ka == "k") { if (hex[3] == 1) { //trailing return([ka,"trailing"]) } else if (hex[3] == 0) { //standalone //full reset / no restore necessary return([ka,"standalone"]) } else { //composing return([ka,"composing"]) } } else { if (hex[3] <= 8) { //standalone return([ka,"standalone"]) } else { //trailing return([ka,"trailing"]) } } } catch { return ""; } } function imeChange(inState, stState, pop, array) { inputState = inState; stateState = stState; if (pop) { array.pop(); } inputFull = array; } // @remi@snug.moe: // we made a backspace key that works!!!!!!! // @meduelelateta@sweet.succubi.services: // ari fell asleep function imeRestore(array) { let hex = array[array.length-1].codePointAt(0).toString(16); hex[1] = parseInt(hex[1], 16); hex[2] = parseInt(hex[2], 16); hex[3] = parseInt(hex[3], 16); let previous = imeInfo(array[array.length-2])[1]; let ka = (hex[1] == 0 && hex[2] == 0) ? "a" : "k"; if (ka == "k") { if (hex[3] == 1) { //trailing if (previous == "trailing") { //trailing twice console.log("2.1"); imeChange(2,1,true,array); } else { //trailing once console.log("2.0"); imeChange(2,0,true,array); } } else if (hex[3] == 0) { console.log(array.length); if (array.length != 1) { //0.1, i think? console.log("0.1"); imeChange(0,1,true,array); } else { //standalone console.log("full reset"); imeReset(); } } else { //composing if (previous == "standalone") { //0.1?? //recreate?? last digit??? let c = hConsonantsR[[hex[1], hex[2]].join("").toUpperCase()]; array.pop(); array.push(getUnicodeConsonant(c, "standalone")); inputCurrent = c; console.log("0.1"); imeChange(0,1,false,array); } else { //0.2???? fuck it lets try and find out ig //recreate?? last digit??? let c = hConsonantsR[[hex[1], hex[2]].join("").toUpperCase()]; array.pop(); array.push(getUnicodeConsonant(c, "standalone")); inputCurrent = c; console.log("0.2"); imeChange(0,2,false,array); } } } else { if (hex[3] <= 8) { //standalone console.log("full reset"); imeReset(); } else { //trailing if (previous == "trailing") { //trailing twice console.log("2.1"); imeChange(2,1,true,array); } else { //trailing once console.log("2.0"); imeChange(2,0,true,array); } } } } function imeInput(key) { key = key.toLowerCase(); if (!hConsonantsK.includes(key) && !hVowelsK.includes(key)) return; switch (inputState) { case 0: //starting if (stateState == 0) { if (hVowelsK.includes(key)) { inputFull.push(getUnicodeVowel(key, "standalone")); inputState = 2; } else { inputFull.push(getUnicodeConsonant(key, "standalone")); stateState = 1; inputCurrent = key; } break; } else if (stateState == 1) { if (hVowelsK.includes(key)) { inputState = 1; stateState = 0; } else { inputFull.push(getUnicodeConsonant(key, "trailing")); inputCurrent = key; stateState = 2; break; } } else if (stateState == 2) { if (hVowelsK.includes(key)) { inputState = 1; stateState = 0; } else break; } case 1: //composing if (hVowelsK.includes(key)) { inputState = 2; inputFull.pop(); inputFull.push(getUnicodeConsonant(inputCurrent, key)); inputCurrent = ""; } break; case 2: //trailing if (hVowelsK.includes(key)) { inputFull.push(getUnicodeVowel(key, "trailing")); stateState += 1; } else { inputFull.push(getUnicodeConsonant(key, "trailing")); stateState += 1; } if (stateState == 2) { stateState = 0; inputState = 3; } break; case 3: //go to next word uwu //todo :) break; } }