diff --git a/index.html b/index.html index b6908e4..bf5de0c 100644 --- a/index.html +++ b/index.html @@ -40,6 +40,7 @@ show all words random word resources + update available! close all open hisho tabs and reopen hisho to update.
diff --git a/main.css b/main.css index e2d1893..4d6036e 100644 --- a/main.css +++ b/main.css @@ -160,6 +160,10 @@ header.fullscreen #search { max-width: 500px; } +header #update { + display: none; +} + .header-animation { animation: clearly-cheating 1s ease-in-out; } diff --git a/main.js b/main.js index e49ea81..33ab76d 100644 --- a/main.js +++ b/main.js @@ -81,7 +81,7 @@ document.addEventListener("keydown", (e) => { function stripWord(word, space = false) { return space == false ? word.replace(/[^a-zA-Z-]/gu, "").toLowerCase() : - word.replace(/[^a-zA-Z-\s]/gu, "").toLowerCase(); + word.replace(/[^a-zA-Z-\s]/gu, "").toLowerCase(); } heonianVowels = { @@ -200,7 +200,7 @@ function generateRuby(word, addLinks = false) { } } } - + return ruby; } @@ -225,12 +225,12 @@ function loadDictionary() { //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"))); + 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("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)); } @@ -251,7 +251,7 @@ function loadDictionary() { function search(word, type) { console.log(type); - if (type == "all") {word = ""}; + if (type == "all") { word = "" }; if (type == "random") { let random = Math.floor(Math.random() * Object.keys(json).length); return [Object.keys(json)[random]]; @@ -365,7 +365,7 @@ function doSearch(state = true) { 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 += ""+json[results[i]][o]["type"]+"
"; + if (last != json[results[i]][o]["type"]) meaning.innerHTML += "" + json[results[i]][o]["type"] + "
"; last = json[results[i]][o]["type"]; if (types[json[results[i]][o]["type"]] == undefined) { types[json[results[i]][o]["type"]] = 1; @@ -373,17 +373,17 @@ function doSearch(state = true) { types[json[results[i]][o]["type"]]++; } //number, meaning (*required) - meaning.innerHTML += ""+(o+1)+""+json[results[i]][o]["meaning"]+""; + meaning.innerHTML += "" + (o + 1) + "" + json[results[i]][o]["meaning"] + ""; //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 += "
"+json[results[i]][o]["translation"]+""; + meaning.innerHTML += "
" + json[results[i]][o]["translation"] + ""; } //example if (json[results[i]][o]["examples"] != undefined) { let temp = "" temp += "
Examples

"; for (let e in json[results[i]][o]["examples"]) { - temp += ""+generateRuby(e, true)+" - "+json[results[i]][o]["examples"][e]+"
"; + temp += "" + generateRuby(e, true) + " - " + json[results[i]][o]["examples"][e] + "
"; } temp += "

"; meaning.innerHTML += temp; @@ -394,7 +394,7 @@ function doSearch(state = true) { temp += "
Antonyms

"; for (let e in json[results[i]][o]["antonyms"]) { let r = json[results[i]][o]["antonyms"][e]; - temp += ""+generateRuby(r, true); + temp += "" + generateRuby(r, true); if (json[r] != undefined) { temp += " - "; for (let i in json[r]) { @@ -412,7 +412,7 @@ function doSearch(state = true) { temp += "

Synonyms

"; for (let e in json[results[i]][o]["synonyms"]) { let r = json[results[i]][o]["synonyms"][e]; - temp += ""+generateRuby(r, true); + temp += "" + generateRuby(r, true); if (json[r] != undefined) { temp += " - "; for (let i in json[r]) { @@ -427,14 +427,14 @@ function doSearch(state = true) { } //notes if (json[results[i]][o]["notes"] != undefined) { - meaning.innerHTML += "

"+json[results[i]][o]["notes"]+"

"; + meaning.innerHTML += "

" + json[results[i]][o]["notes"] + "

"; } //(source, etc) if (json[results[i]][o]["canon-etymology"] != undefined) { - meaning.innerHTML += "
Canon Etymology

"+json[results[i]][o]["canon-etymology"]+"

"; + meaning.innerHTML += "
Canon Etymology

" + json[results[i]][o]["canon-etymology"] + "

"; } if (json[results[i]][o]["meta-etymology"] != undefined) { - meaning.innerHTML += "
Meta Etymology

"+json[results[i]][o]["meta-etymology"]+"

"; + meaning.innerHTML += "
Meta Etymology

" + json[results[i]][o]["meta-etymology"] + "

"; } //todo result.appendChild(meaning); //y, yeah. @@ -445,22 +445,22 @@ function doSearch(state = true) { let header = document.createElement("div"); header.classList.add("results-header"); //When using search methods, accomodate search text. - switch(type) { - //show:all - case "all": - val = "Showing all words"; - break; - case "random": - val = "Showing random word: " + results[0]; - break; - //OMG A WORD - default: - val = "search results for: " + val; - break; + switch (type) { + //show:all + case "all": + val = "Showing all words"; + break; + case "random": + val = "Showing random word: " + results[0]; + break; + //OMG A WORD + default: + val = "search results for: " + val; + break; } header.innerHTML += "" + val + "
"; for (let i in types) { - if(i !== ""){header.innerHTML += ""+i+"s - "+types[i]+"";} + if (i !== "") { header.innerHTML += "" + i + "s - " + types[i] + ""; } } main.prepend(header); } @@ -476,10 +476,57 @@ function goHome(state = true) { if (state == true) history.pushState(null, "", url); } +async function registerSW() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.register('./sw.js').then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + if (installingWorker == null) { + return; + } + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the updated precached content has been fetched, + // but the previous service worker will still serve the older + // content until all client tabs are closed. + header.querySelector("#update").style.display = "block"; + + // Execute callback + if (config && config.onUpdate) { + config.onUpdate(registration); + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); + + // Execute callback + if (config && config.onSuccess) { + config.onSuccess(registration); + } + } + } + }; + }; + }).catch(error => { + console.error('Error during service worker registration:', error); + }); + } +} + +function removeSW() { + navigator.serviceWorker.getRegistrations().then(function(registrations) { + for(let registration of registrations) { + registration.unregister() + }}); //dev use only!! or whatever... i just copypasted this from SO +} + window.onload = () => { header = document.querySelector("header"); main = document.querySelector("main"); - header.querySelector("#search button").onclick = () => {toggleIME();}; + header.querySelector("#search button").onclick = () => { toggleIME(); }; header.querySelector("span.heonian").onclick = () => { if (!header.classList.contains("fullscreen")) { goHome(); @@ -513,7 +560,8 @@ window.onload = () => { doSearch(false); } }); - //also check if ?s is there . for hecks sake (todo) + registerSW(); + //also check if ?s is there . for hecks sake (todo) (TODO!!!!!!) } const sortObject = obj => Object.keys(obj).sort().reduce((res, key) => (res[key] = obj[key], res), {}) \ No newline at end of file diff --git a/sw.js b/sw.js new file mode 100644 index 0000000..5c4b358 --- /dev/null +++ b/sw.js @@ -0,0 +1,91 @@ +const currentVersion = 'v1'; + +const addResourcesToCache = async (resources) => { + const cache = await caches.open(currentVersion); + await cache.addAll(resources); +}; + +const putInCache = async (request, response) => { + const cache = await caches.open(currentVersion); + await cache.put(request, response); +}; + +const cacheFirst = async ({ request, preloadResponsePromise }) => { + // First try to get the resource from the cache + const responseFromCache = await caches.match(request); + if (responseFromCache) { + return responseFromCache; + } + + // Next try to use the preloaded response, if it's there + const preloadResponse = await preloadResponsePromise; + if (preloadResponse) { + console.info('using preload response', preloadResponse); + putInCache(request, preloadResponse.clone()); + return preloadResponse; + } + + // Next try to get the resource from the network + try { + const responseFromNetwork = await fetch(request); + // response may be used only once + // we need to save clone to put one copy in cache + // and serve second one + putInCache(request, responseFromNetwork.clone()); + return responseFromNetwork; + } catch (error) { + return new Response('Network error happened', { + status: 408, + headers: { 'Content-Type': 'text/plain' }, + }); + } +}; + +const deleteCache = async key => { + await caches.delete(key) +} + +const deleteOldCaches = async () => { + const cacheKeepList = [currentVersion]; + const keyList = await caches.keys() + const cachesToDelete = keyList.filter(key => !cacheKeepList.includes(key)) + await Promise.all(cachesToDelete.map(deleteCache)); +} + +self.addEventListener('activate', (event) => { + event.waitUntil(deleteOldCaches()); +}); + +const enableNavigationPreload = async () => { + if (self.registration.navigationPreload) { + // Enable navigation preloads! + await self.registration.navigationPreload.enable(); + } +}; + +self.addEventListener('activate', (event) => { + event.waitUntil(enableNavigationPreload()); +}); + +self.addEventListener('install', (event) => { + event.waitUntil( + addResourcesToCache([ + './heonian-resources/wordlist.json', + './heonian-ime/ime.js', + './font.otf', + './index.html', + './main.css', + './main.js', + './manifest.json' + ]) + ); +}); + +self.addEventListener('fetch', (event) => { + event.respondWith( + cacheFirst({ + request: event.request, + preloadResponsePromise: event.preloadResponse + }) + ); +}); \ No newline at end of file diff --git a/temp.png b/temp.png deleted file mode 100644 index 188e237..0000000 Binary files a/temp.png and /dev/null differ