426 lines
16 KiB
JavaScript
426 lines
16 KiB
JavaScript
let json = null;
|
|
|
|
let searchDictionary = {};
|
|
|
|
let header = null;
|
|
let main = null;
|
|
|
|
let ime = null;
|
|
|
|
function toggleIME() {
|
|
if (ime == null) {
|
|
ime = new HeonianIME(header.querySelector("input"));
|
|
header.querySelector("#search button").style.color = "var(--fg)";
|
|
} else {
|
|
ime.imeDetach();
|
|
ime = null;
|
|
header.querySelector("#search button").style.color = "var(--grey)";
|
|
}
|
|
}
|
|
|
|
function animateHeader(inout = false) {
|
|
//todo: debounce this
|
|
if (inout) {
|
|
header.classList.add("header-animation-reverse");
|
|
header.classList.remove("header-animation");
|
|
main.classList.add("main-animation-reverse");
|
|
setTimeout(() => {
|
|
header.classList.add("fullscreen");
|
|
main.style.opacity = "0";
|
|
main.classList.remove("main-animation-reverse");
|
|
}, 500);
|
|
setTimeout(() => {
|
|
header.classList.remove("header-animation-reverse");
|
|
}, 1000);
|
|
} else {
|
|
header.classList.add("header-animation");
|
|
header.classList.remove("header-animation-reverse");
|
|
setTimeout(() => {
|
|
header.classList.remove("fullscreen");
|
|
main.classList.add("main-animation");
|
|
}, 500);
|
|
setTimeout(() => {
|
|
header.classList.remove("header-animation");
|
|
main.style.opacity = "1";
|
|
main.classList.remove("main-animation");
|
|
}, 1000);
|
|
}
|
|
//somewhere here we animate in the actual page too, but it.. doesnt exist yet
|
|
}
|
|
|
|
// on space animateHeader()
|
|
document.addEventListener("keydown", (e) => {
|
|
//on / key press
|
|
if (e.keyCode === 191) {
|
|
e.preventDefault();
|
|
header.querySelector("input").focus();
|
|
}
|
|
if (e.keyCode === 27) {
|
|
//if search box not focused, and if we're not on the home page, go back to home page
|
|
if (!header.querySelector("input").matches(":focus") && !header.classList.contains("fullscreen")) {
|
|
e.preventDefault();
|
|
goHome();
|
|
} else if (header.querySelector("input").matches(":focus") && ime != null) {
|
|
toggleIME();
|
|
} else if (header.querySelector("input").matches(":focus")) {
|
|
header.querySelector("input").value = "";
|
|
}
|
|
}
|
|
if (e.keyCode === 13) {
|
|
if (header.querySelector("input").matches(":focus") && ime == null) {
|
|
//search
|
|
e.preventDefault();
|
|
if (header.classList.contains("fullscreen")) {
|
|
animateHeader();
|
|
}
|
|
doSearch();
|
|
}
|
|
}
|
|
});
|
|
|
|
function stripWord(word, space = false) {
|
|
return space == false ? word.replace(/[^a-zA-Z-]/gu, "").toLowerCase() :
|
|
word.replace(/[^a-zA-Z-\s]/gu, "").toLowerCase();
|
|
}
|
|
|
|
heonianVowels = {
|
|
"-1": "",
|
|
"0": "",
|
|
"1": "a",
|
|
"2": "e",
|
|
"3": "i",
|
|
"4": "o",
|
|
"5": "u",
|
|
"a": "a",
|
|
"b": "e",
|
|
"c": "i",
|
|
"d": "o",
|
|
"e": "u"
|
|
}
|
|
|
|
heonianVowelsSeparate = {
|
|
"0": "a",
|
|
"1": "e",
|
|
"2": "i",
|
|
"3": "o",
|
|
"4": "u",
|
|
"a": "a",
|
|
"b": "e",
|
|
"c": "i",
|
|
"d": "o",
|
|
"e": "u"
|
|
}
|
|
|
|
heonianConsonants = {
|
|
"00": "",
|
|
"01": "g",
|
|
"02": "sh",
|
|
"03": "r",
|
|
"04": "ny",
|
|
"05": "ch",
|
|
"06": "m",
|
|
"07": "j",
|
|
"07": "y",
|
|
"08": "f",
|
|
"09": "t",
|
|
"0a": "k",
|
|
"0b": "w",
|
|
"0c": "l",
|
|
"0d": "p",
|
|
"0e": "b",
|
|
"0f": "d",
|
|
"10": "h",
|
|
};
|
|
|
|
function heonianToRoman(text, strict = false) {
|
|
let roman = "";
|
|
for (let i = 0; i < text.length; i++) {
|
|
let h = text.codePointAt(i);
|
|
if (h >= 57344 && h <= 57606) {
|
|
h = text.codePointAt(i).toString(16).split("");
|
|
if (h[1] == 0 && h[2] == 0) { //if vowel
|
|
roman += heonianVowelsSeparate[h[3]];
|
|
} else { //if consonant
|
|
if (h[1] == 0 && h[2] == 6 && h[3] == 1) {
|
|
roman += "n";
|
|
} else {
|
|
roman += heonianConsonants[h[1] + "" + h[2]] + heonianVowels[h[3] - 1];
|
|
}
|
|
}
|
|
} else if (strict == false) {
|
|
roman += text.charAt(i);
|
|
}
|
|
}
|
|
return roman;
|
|
}
|
|
|
|
function processVerb(word) {
|
|
//basically, turn welikanyapash into liku
|
|
//and uhhhhhhhhhhh maybe. return as an object with. all the things . used, for info
|
|
return word;
|
|
//but ... not yet uwu
|
|
}
|
|
|
|
function generateRuby(word) {
|
|
letters = word.split("");
|
|
ruby = "";
|
|
for (let i = 0; i < letters.length; i++) {
|
|
ruby += `<ruby>${letters[i]}<rt>${heonianToRoman(letters[i])}</rt></ruby>`;
|
|
}
|
|
return ruby;
|
|
}
|
|
|
|
function loadDictionary() {
|
|
fetch("./wordlist.json").then((e) => {
|
|
if (e.status === 200) {
|
|
//convert to json lmao
|
|
e.json().then((e) => {
|
|
json = e;
|
|
|
|
let values = Object.values(json);
|
|
let keys = Object.keys(json);
|
|
//prepare search. maybe async this if we're loading a specific word?
|
|
for (let i = 0; i < keys.length; i++) {
|
|
try {
|
|
//create array
|
|
searchDictionary[keys[i]] = [];
|
|
|
|
//add: word
|
|
// searchDictionary[keys[i]].push(keys[i]);
|
|
|
|
//add: romanized word
|
|
searchDictionary[keys[i]].push(stripWord(heonianToRoman(keys[i])));
|
|
//what if i add it here.
|
|
searchDictionary[keys[i]].push(stripWord(heonianToRoman(keys[i]).replace("dsh","z")));
|
|
|
|
//add: translations, meanings
|
|
for (let o = 0; o < values[i].length; o++) {
|
|
if (values[i][o].hasOwnProperty("translation")) searchDictionary[keys[i]].push(stripWord(values[i][o]["translation"], true));
|
|
if (values[i][o].hasOwnProperty("meaning")) searchDictionary[keys[i]].push(stripWord(values[i][o]["meaning"], true));
|
|
}
|
|
} catch (e) {
|
|
console.log(keys[i] + " failed to load :/");
|
|
}
|
|
}
|
|
|
|
//ok, we're all ready!
|
|
document.querySelector("header input").placeholder = "search";
|
|
});
|
|
} else {
|
|
alert("yeah something went horribly wrong loading the wordlist so uh,,, certified ike moment");
|
|
return;
|
|
}
|
|
});
|
|
}
|
|
|
|
function search(word) {
|
|
let words = word.split(" ");
|
|
for (let i = 0; i < words.length; i++) {
|
|
words[i] = stripWord(words[i]);
|
|
words[i] = heonianToRoman(words[i]);
|
|
words[i] = processVerb(words[i]);
|
|
}
|
|
word = words.join("");
|
|
console.log("debug - search: " + word);
|
|
let result = [];
|
|
for (let key in searchDictionary) {
|
|
for (let value in searchDictionary[key]) {
|
|
if (searchDictionary[key][value].includes(word)) {
|
|
if (value == 0) {
|
|
result.push(key);
|
|
break;
|
|
} else {
|
|
//if its a description (i.e: includes a space)
|
|
//do startsWith or something instead to prevent
|
|
//false positive search results
|
|
let t = false;
|
|
let d = searchDictionary[key][value].split(" ")
|
|
for (let w in d) {
|
|
if (d[w].startsWith(word)) {
|
|
t = true;
|
|
}
|
|
}
|
|
if (t) {
|
|
result.push(key);
|
|
break;
|
|
}
|
|
}
|
|
} else if (words.length > 1 && stripWord(searchDictionary[key][value]).includes(word)) {
|
|
result.push(key);
|
|
break;
|
|
//can still produce false positives, but... very low chance
|
|
//i'll fix it once it starts happening...
|
|
//(just... re-creating the search tems to actually have spaces should do the trick, right?)
|
|
//(yeah, i'll wanna . strip spaces to not be more than one in a row and all that but. H)
|
|
//idk. this should be fine for now, and possibly, ever
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function doSearch(state = true) {
|
|
let val = header.querySelector("input").value;
|
|
let results = search(val);
|
|
if (results.length == 0) {
|
|
main.innerHTML = "no results lulw";
|
|
} else {
|
|
main.innerHTML = "";
|
|
/*
|
|
[words - 420 found / expressions - 69 found / wahtever else - ygettheidea] (click one to filter to just those)
|
|
*/
|
|
let types = {}
|
|
for (let i = 0; i < results.length; i++) {
|
|
let result = document.createElement("div");
|
|
result.classList.add("result");
|
|
|
|
// 1. add word in heo + romanized ruby as header
|
|
let header = document.createElement("span");
|
|
header.classList.add("result-header");
|
|
header.innerHTML = generateRuby(results[i]);
|
|
|
|
// 2. add tags [tags like. common word, slang, formal???, type, etc]
|
|
// hhh how do i do this part, what tags. would i add. would this be. per word, or per definition?
|
|
|
|
// 3. formal/nonformal versions, inflections button
|
|
// could try to work on this though
|
|
|
|
// 4. audio clip if found
|
|
|
|
// 5. render all meanings of word
|
|
/*
|
|
short english translation, followed by . long description/explanation of word
|
|
maybe an example? usage?
|
|
and. source/who made it/etc
|
|
repeat for all meanings of word,
|
|
*/
|
|
result.appendChild(header);
|
|
let last = "";
|
|
for (let o = 0; o < json[results[i]].length; o++) {
|
|
let meaning = document.createElement("div");
|
|
meaning.classList.add("result-meaning");
|
|
//ripping off jisho: Bold, word type (*required)
|
|
if (last != json[results[i]][o]["type"]) meaning.innerHTML += "<b class='capitalize'>"+json[results[i]][o]["type"]+"</b><br>";
|
|
last = json[results[i]][o]["type"];
|
|
if (types[json[results[i]][o]["type"]] == undefined) {
|
|
types[json[results[i]][o]["type"]] = 1;
|
|
} else {
|
|
types[json[results[i]][o]["type"]]++;
|
|
}
|
|
//number, meaning (*required)
|
|
meaning.innerHTML += "<span class='result-number'>"+(o+1)+"</span><span class='result-big'>"+json[results[i]][o]["meaning"]+"</span>";
|
|
//longer translation (below are not required, make sure to check for them)
|
|
if (json[results[i]][o]["translation"] != undefined && json[results[i]][o]["translation"].toLowerCase() != json[results[i]][o]["meaning"].toLowerCase()) {
|
|
meaning.innerHTML += "<br><span class='result-translation'>"+json[results[i]][o]["translation"]+"</span>";
|
|
}
|
|
//example
|
|
if (json[results[i]][o]["examples"] != undefined) {
|
|
let temp = ""
|
|
temp += "<details><summary>Examples</summary><p>";
|
|
for (let e in json[results[i]][o]["examples"]) {
|
|
temp += "<span class='heonian'>"+generateRuby(e)+"</span> - "+json[results[i]][o]["examples"][e]+"<br>";
|
|
}
|
|
temp += "</p></details>";
|
|
meaning.innerHTML += temp;
|
|
}
|
|
//antonyms, synonyms
|
|
if (json[results[i]][o]["antonyms"] != undefined) {
|
|
let temp = ""
|
|
temp += "<details class='result-nyms'><summary>Antonyms</summary><p>";
|
|
for (let e in json[results[i]][o]["antonyms"]) {
|
|
let r = json[results[i]][o]["antonyms"][e];
|
|
temp += "<span class='heonian'>"+generateRuby(r);
|
|
if (json[r] != undefined) {
|
|
temp += " - ";
|
|
for (let i in json[r]) {
|
|
if (i != 0) temp += ", ";
|
|
temp += json[r][i]["translation"];
|
|
}
|
|
}
|
|
temp += "</span><br>";
|
|
}
|
|
temp += "</p></details>";
|
|
meaning.innerHTML += temp;
|
|
}
|
|
if (json[results[i]][o]["synonyms"] != undefined) {
|
|
let temp = ""
|
|
temp += "<details class='result-nyms'><summary>Synonyms</summary><p>";
|
|
for (let e in json[results[i]][o]["synonyms"]) {
|
|
let r = json[results[i]][o]["synonyms"][e];
|
|
temp += "<span class='heonian'>"+generateRuby(r);
|
|
if (json[r] != undefined) {
|
|
temp += " - ";
|
|
for (let i in json[r]) {
|
|
if (i != 0) temp += ", ";
|
|
temp += json[r][i]["translation"];
|
|
}
|
|
}
|
|
temp += "</span><br>";
|
|
}
|
|
temp += "</p></details>";
|
|
meaning.innerHTML += temp;
|
|
}
|
|
//notes
|
|
if (json[results[i]][o]["notes"] != undefined) {
|
|
meaning.innerHTML += "<p class='result-notes'>"+json[results[i]][o]["notes"]+"</p>";
|
|
}
|
|
//(source, etc)
|
|
if (json[results[i]][o]["canon-etymology"] != undefined) {
|
|
meaning.innerHTML += "<details class='result-meta'><summary>Canon Etymology</summary><p>"+json[results[i]][o]["canon-etymology"]+"</p></details>";
|
|
}
|
|
if (json[results[i]][o]["meta-etymology"] != undefined) {
|
|
meaning.innerHTML += "<details class='result-meta'><summary>Meta Etymology</summary><p>"+json[results[i]][o]["meta-etymology"]+"</p></details>";
|
|
}
|
|
//todo
|
|
result.appendChild(meaning); //y, yeah.
|
|
}
|
|
main.appendChild(result);
|
|
}
|
|
types = sortObject(types);
|
|
let header = document.createElement("div");
|
|
header.classList.add("results-header");
|
|
header.innerHTML += "<span class='heonian'>search results for: " + val + "</span><br>";
|
|
for (let i in types) {
|
|
header.innerHTML += "<span class='results-header-count'>"+i+"s - "+types[i]+"</span>";
|
|
}
|
|
main.prepend(header);
|
|
}
|
|
const url = new URL(window.location);
|
|
url.searchParams.set('s', val);
|
|
if (state == true) history.pushState(val, "", url);
|
|
}
|
|
|
|
function goHome(state = true) {
|
|
animateHeader(true);
|
|
const url = new URL(window.location);
|
|
url.searchParams.delete('s');
|
|
if (state == true) history.pushState(null, "", url);
|
|
}
|
|
|
|
window.onload = () => {
|
|
header = document.querySelector("header");
|
|
main = document.querySelector("main");
|
|
header.querySelector("#search button").onclick = () => {toggleIME();};
|
|
header.querySelector("span.heonian").onclick = () => {
|
|
if (!header.classList.contains("fullscreen")) {
|
|
goHome();
|
|
}
|
|
};
|
|
loadDictionary();
|
|
window.addEventListener('popstate', (e) => {
|
|
if (e.state == null) {
|
|
if (!header.classList.contains("fullscreen")) {
|
|
goHome(false);
|
|
}
|
|
} else {
|
|
if (header.classList.contains("fullscreen")) {
|
|
animateHeader(false);
|
|
}
|
|
header.querySelector("input").value = e.state;
|
|
doSearch(false);
|
|
}
|
|
console.log("location: " + document.location + ", state: " + JSON.stringify(e.state));
|
|
});
|
|
}
|
|
|
|
const sortObject = obj => Object.keys(obj).sort().reduce((res, key) => (res[key] = obj[key], res), {}) |