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), {}) |