diff --git a/ime.js b/ime.js index eb8f49d..f8643ca 100644 --- a/ime.js +++ b/ime.js @@ -1,434 +1,458 @@ -let input = []; +// bug: if you fuck up input really badly, you might get a ??? unicode to print. +// it successfully breaks everything. however, im not managing to repro it so... +// (shruggie) -let currentWord = []; - -let selected = -1; +let h; window.onload = () => { document.querySelectorAll(".ime").forEach(input => { - input.addEventListener("keydown", (key) => { - imeDown(key, input); - }); - input.addEventListener("mousedown", (e) => { - e.preventDefault(); - input.focus(); - imeUpdate(input); - }) + h = new HeonianIME(); + h.imeAttach(input); }); } -function imePush() { - //basically equavlient to pressing 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 = []; - } - imeReset(); - } else { - if (selected+1 >= input.length) { - selected = -1; - } else { - selected += 1; - } - imeReset(true); - } -} +class HeonianIME { + constructor() { + this.input = []; + this.currentWord = []; + this.selected = -1; -function imeDown(keyEvent, inputField) { - keyEvent.preventDefault(); - switch (keyEvent.key) { - case "Backspace": - if (selected == -1) { - if (currentWord.join("") == "") { - currentWord = input.pop() || []; + this.inputState = 0; //STARTING, COMPOSTING, TRAILING, Fucking Nothing, ...cleanup + this.stateState = 0; //depends on the state + this.inputFull = []; + this.inputCurrent = ""; + + this.ourHtml; + + this.hVowels = { //standalone, composing, trailing + "a": ["0", "5", "A"], + "e": ["1", "6", "B"], + "i": ["2", "7", "C"], + "o": ["3", "8", "D"], + "u": ["4", "9", "E"] + }; + + this.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", + }; + + this.hVowelsK = Object.keys(this.hVowels) + this.hConsonantsK = Object.keys(this.hConsonants); + this.hConsonantsR = this.inverse(this.hConsonants); + } + + imeAttach(what) { //re-attaching is unsupported :3 + this.ourHtml = what; + what.addEventListener("keydown", (key) => { + this.imeDown(key, what); + }); + what.addEventListener("mousedown", (e) => { + this.mouse(e, what); + }); + } + + mouse(e, what) { + e.preventDefault(); + what.focus(); + this.imeUpdate(what); + } + + imeDetach() { + //how do we detach this without . uh, + this.imeDown = ()=>{}; + this.mouse = ()=>{}; + //close enough? + } + + imePush() { + //basically equavlient to pressing enter. + if (this.selected == -1) { + if (this.currentWord.join() == "") { //blame js :) + let s = document.createElement("p"); + s.innerText = this.input.join(""); + document.body.appendChild(s); + this.input = []; + this.selected = -1; + } else { + this.input.push(this.currentWord); + this.currentWord = []; + } + this.imeReset(); + } else { + if (this.selected + 1 >= this.input.length) { + this.selected = -1; + } else { + this.selected += 1; + } + this.imeReset(true); + } + } + + imeDown(keyEvent, inputField) { + keyEvent.preventDefault(); + switch (keyEvent.key) { + case "Backspace": + if (this.selected == -1) { + if (this.currentWord.join("") == "") { + this.currentWord = this.input.pop() || []; + } else { + this.imeRestore(this.currentWord); + // this.currentWord.pop(); + } } else { - imeRestore(currentWord); - // currentWord.pop(); + this.imeRestore(this.input[this.selected]); + // this.input[this.selected].pop(); + if (this.input[this.selected].join("") == "") { + this.input.splice(this.selected, 1); + this.imeMove("left"); + } } - } else { - imeRestore(input[selected]); - // input[selected].pop(); - if (input[selected].join("") == "") { - input.splice(selected,1); - imeMove("left"); + break; + case "Enter": + this.imePush() + break; + case "Space": + //adds a ', on second press . an actual space + break; + case "ArrowLeft": + this.imeMove("left"); + break; + case "ArrowRight": + this.imeMove("right"); + break; + case "ArrowDown": + this.imeMove("start"); + break; + case "ArrowUp": + this.imeMove("end"); + break; + case "Escape": + this.imeDetach(); + break; + default: + this.imeInput(keyEvent.key); + if (this.selected == -1) { + this.currentWord = this.inputFull; + } else { + this.input[this.selected] = this.inputFull; } - } - break; - case "Enter": - imePush() - break; - case "Space": - //adds a ', on second press . an actual space - 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; + break; } - to = from+input[selected].join("").length; - inputField.setSelectionRange(from, to); + this.imeUpdate(inputField); } -} -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(true); -} - -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(soft = false) { - stateState = 0; - inputState = 0; - inputFull = []; - inputCurrent = ""; - if (!soft) { - if (selected == -1) { - currentWord = []; + imeUpdate(inputField) { + let renderText = ""; + this.input.forEach((w) => { + renderText += w.join(""); + }) + inputField.value = renderText + this.currentWord.join(""); + if (this.selected == -1) { + inputField.setSelectionRange(renderText.length, renderText.length + this.currentWord.join("").length); } else { - input[selected] = []; + if (this.input.join("") != "") { + let from = 0; + for (let x = 0; x < this.selected; x++) { + from += this.input[x].join("").length; + } + let to = from + this.input[this.selected].join("").length; + inputField.setSelectionRange(from, to); + } } } -} -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); + clamp(min, max, value) { + return Math.max(min, Math.min(max, value)); + } + + imeMove(direction) { + let d = 1; + if (this.currentWord.join("") != "") { + this.input.push(this.currentWord); + this.currentWord = []; + d = 2; + } + switch (direction) { + case "start": + this.selected = 0; + break; + case "end": + this.selected = -1; + break; + case "left": + if (this.selected == -1) this.selected = this.input.length; + this.selected -= 1 * d; + if (this.selected != -1) { + this.selected = this.clamp(0, this.input.length, this.selected); + //is there even a point in clamping it here,,,, + } + break; + case "right": + this.selected += 1 * d; + this.selected = this.clamp(0, this.input.length, this.selected); + //tbh same here, like. + //oh well ig ???/ + if (this.selected == this.input.length) { + this.selected = -1; + } + break; + } + this.imeReset(true); + } + + getUnicodeVowel(vowel, sot) { + if (sot == "standalone") sot = 0; + if (sot == "composing") sot = 1; + if (sot == "trailing") sot = 2; + return String.fromCharCode(parseInt("E00" + this.hVowels[vowel][sot], 16)); + } + + getUnicodeConsonant(consonant, scv) { + if (scv == "standalone") { scv = 0; } + else if (scv == "trailing") { scv = 1; } + else { + scv = Number(this.hVowels[scv][0]) + 2 + } + return String.fromCharCode(parseInt("E" + this.hConsonants[consonant] + "" + scv, 16)); + } + + inverse(obj) { + var retobj = {}; + for (var key in obj) { + retobj[obj[key]] = key; + } + return retobj; + } + + imeReset(soft = false) { + this.stateState = 0; + this.inputState = 0; + this.inputFull = []; + this.inputCurrent = ""; + if (!soft) { + if (this.selected == -1) { + this.currentWord = []; + } else { + this.input[this.selected] = []; + } + } + } + + imeInfo(decHex) { + try { + let hex = decHex.codePointAt(0).toString(16).split(""); + hex[1] = hex[1].toString(16);//parseInt(hex[1], 16); + hex[2] = hex[2].toString(16);//parseInt(hex[2], 16); + hex[3] = hex[3].toString(16);//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 ""; + } + } + + imeChange(inState, stState, pop, array) { + this.inputState = inState; + this.stateState = stState; + if (pop) { + array.pop(); + } + this.inputFull = array; + } + + // @remi@snug.moe: + // we made a backspace key that works!!!!!!! + // @meduelelateta@sweet.succubi.services: + // ari fell asleep + + imeRestore(array) { + let hex = array[array.length - 1].codePointAt(0).toString(16).split(""); + hex[1] = hex[1].toString(16);//parseInt(hex[1], 16); + hex[2] = hex[2].toString(16);//parseInt(hex[2], 16); + hex[3] = hex[3].toString(16);//parseInt(hex[3], 16); + let previous = this.imeInfo(array[array.length - 2])[1]; let ka = (hex[1] == 0 && hex[2] == 0) ? "a" : "k"; if (ka == "k") { if (hex[3] == 1) { //trailing - return([ka,"trailing"]) + if (previous == "trailing") { + //trailing twice + // console.log("2.1"); + this.imeChange(2, 1, true, array); + } else { + //trailing once + // console.log("2.0"); + this.imeChange(2, 0, true, array); + } } else if (hex[3] == 0) { - //standalone - //full reset / no restore necessary - return([ka,"standalone"]) + if (array.length != 1) { + //0.1, i think? + // console.log("0.1"); + this.imeChange(0, 1, true, array); + } else { + //standalone + // console.log("full reset"); + this.imeReset(); + } } else { //composing - return([ka,"composing"]) + if (previous == "standalone") { + //0.1?? + //recreate?? last digit??? + let c = this.hConsonantsR[[hex[1], hex[2]].join("").toUpperCase()]; + array.pop(); + array.push(this.getUnicodeConsonant(c, "standalone")); + this.inputCurrent = c; + + // console.log("0.1"); + this.imeChange(0, 1, false, array); + } else { + //0.2???? fuck it lets try and find out ig + //recreate?? last digit??? + let c = this.hConsonantsR[[hex[1], hex[2]].join("").toUpperCase()]; + array.pop(); + array.push(this.getUnicodeConsonant(c, "standalone")); + this.inputCurrent = c; + + // console.log("0.2"); + this.imeChange(0, 2, false, array); + } } } else { if (hex[3] <= 8) { //standalone - return([ka,"standalone"]) + // console.log("full reset"); + this.imeReset(); } 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; + if (previous == "trailing") { + //trailing twice + // console.log("2.1"); + this.imeChange(2, 1, true, array); } else { - inputFull.push(getUnicodeConsonant(key, "standalone")); - stateState = 1; - inputCurrent = key; + //trailing once + // console.log("2.0"); + this.imeChange(2, 0, true, array); + } + } + } + } + + imeInput(key) { + key = key.toLowerCase(); + if (!this.hConsonantsK.includes(key) && !this.hVowelsK.includes(key)) return; + switch (this.inputState) { + case 0: //starting + if (this.stateState == 0) { + if (this.hVowelsK.includes(key)) { + this.inputFull.push(this.getUnicodeVowel(key, "standalone")); + this.inputState = 2; + } else { + this.inputFull.push(this.getUnicodeConsonant(key, "standalone")); + this.stateState = 1; + this.inputCurrent = key; + } + break; + } else if (this.stateState == 1) { + if (this.hVowelsK.includes(key)) { + this.inputState = 1; + this.stateState = 0; + } else { + this.inputFull.push(this.getUnicodeConsonant(key, "trailing")); + this.inputCurrent = key; + this.stateState = 2; + break; + } + } else if (this.stateState == 2) { + if (this.hVowelsK.includes(key)) { + this.inputState = 1; + this.stateState = 0; + } else break; + } + case 1: //composing + if (this.hVowelsK.includes(key)) { + this.inputState = 2; + this.inputFull.pop(); + this.inputFull.push(this.getUnicodeConsonant(this.inputCurrent, key)); + this.inputCurrent = ""; } break; - } else if (stateState == 1) { - if (hVowelsK.includes(key)) { - inputState = 1; - stateState = 0; + case 2: //trailing + if (this.hVowelsK.includes(key)) { + if (this.stateState == 1 && this.inputCurrent != "") { + this.inputFull.pop(); + let ic = this.inputCurrent; + let k = key; + this.imePush(); + this.inputFull.pop(); + this.inputFull.push(this.getUnicodeConsonant(ic, k)); + this.inputCurrent = ""; + this.inputState = 2; + } else { + this.inputFull.push(this.getUnicodeVowel(key, "trailing")); + this.inputCurrent = ""; + this.stateState += 1; + } } else { - inputFull.push(getUnicodeConsonant(key, "trailing")); - inputCurrent = key; - stateState = 2; - break; + this.inputFull.push(this.getUnicodeConsonant(key, "trailing")); + this.inputCurrent = key; + this.stateState += 1; } - } 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)) { - if (stateState == 1 && inputCurrent != "") { - inputFull.pop(); - let ic = inputCurrent; + if (this.stateState == 2) { + this.stateState = 0; + this.inputState = 3; + }; + break; + case 3: //go to next word uwu + if (this.hVowelsK.includes(key)) { + this.inputFull.pop(); + let ic = this.inputCurrent; let k = key; - imePush(); - inputFull.pop(); - inputFull.push(getUnicodeConsonant(ic, k)); - inputCurrent = ""; - inputState = 2; + this.imePush(); + this.inputFull.pop(); + this.inputFull.push(this.getUnicodeConsonant(ic, k)); + this.inputCurrent = ""; + this.inputState = 2; } else { - inputFull.push(getUnicodeVowel(key, "trailing")); - inputCurrent = ""; - stateState += 1; + this.imePush(); + this.imeInput(key); } - } else { - inputFull.push(getUnicodeConsonant(key, "trailing")); - inputCurrent = key; - stateState += 1; - } - if (stateState == 2) { - stateState = 0; - inputState = 3; - }; - break; - case 3: //go to next word uwu - if (key != "") { - inputFull.pop(); - let ic = inputCurrent; - let k = key; - imePush(); - inputFull.pop(); - inputFull.push(getUnicodeConsonant(ic, k)); - inputCurrent = ""; - inputState = 2; - } else { - imePush(); - imeInput(key); - } - break; + break; + } } } \ No newline at end of file