Last active
April 2, 2023 14:58
-
-
Save vpnry/f4d16b7ab71f30f4a6f5d4a3d66bd5da to your computer and use it in GitHub Desktop.
Myanmar Popup Dictionary Userscript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name MYENMY-Dictionary | |
| // @namespace https://gist.github.com/vpnry/f4d16b7ab71f30f4a6f5d4a3d66bd5da | |
| // @version 2023.04.02.2129 | |
| // @description MEM-Dictionary For Tampermonkey UserScript | |
| // @author uPNRY with the assistance of ChatGPT | |
| // @match http://*/* | |
| // @match https://*/* | |
| // @exclude http://google.com/* | |
| // @exclude https://google.com/* | |
| // @grant none | |
| // ==/UserScript== | |
| // @run-at document-end | |
| /** | |
| * You need to put your dictionary in const DICTOBJ = {"key", "value"}; | |
| * ------------------------------------------------------------------------- | |
| * On Android use this user script with https://github.com/bromite/bromite | |
| * or Kiwi Browser http://play.google.com/store/apps/details?id=com.kiwibrowser.browser | |
| * ------------------------------------------------------------------------- | |
| * You may want to use GNU nano editor to insert the large filesize dictionary content in to DICTOBJ. | |
| * My dictionary is around 100 MB json! Still work. | |
| * Some functions are generated/modified with chat GPT | |
| */ | |
| (function () { | |
| "use strict"; | |
| var puncSpecialChars = "…‘’!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" + "။၊"; | |
| var punRex = new RegExp( | |
| `^[${puncSpecialChars}]+|[${puncSpecialChars}]+$`, | |
| "g" | |
| ); | |
| var timeoutIdPopup = ""; | |
| let highlightedWord = null; | |
| const filterN = 2; | |
| // put your dictionaries into this DICTOBJ | |
| const DICTOBJ = "INSERTDICTIONARYHEREINSERTDICTIONARYHERE"; | |
| const RESEGMENT_REGULAR_EX = | |
| /(?:(?<!\u1039)([\u1000-\u102A\u103F\u104A-\u104F]|[\u1040-\u1049]+|[^\u1000-\u104F]+)(?![\u103E\u103B]?[\u1039\u103A\u1037]))/g; | |
| function segment(text) { | |
| // RESEGMENT_REGULAR_EX and this func are adapted from | |
| // https://github.com/swanhtet1992/ReSegment/blob/master/Javascript/resegment.js | |
| /** | |
| * | |
| * @author Chan Mrate Ko Ko | |
| * | |
| */ | |
| var outArray = text.replace(RESEGMENT_REGULAR_EX, "𝕊$1").split("𝕊"); | |
| if (outArray.length > 0) { | |
| outArray.shift(); | |
| //out.splice(0, 1); | |
| } | |
| return outArray; | |
| } | |
| function detectLatinWord(word) { | |
| const latinPattern = /^[a-zA-Z\u00C0-\u00FF\s]*$/; | |
| return latinPattern.test(word); | |
| } | |
| function countWordFrequency(n = 2) { | |
| const textContent = document.body.textContent; | |
| const words = textContent.trim().split(/\s+/); | |
| const wordCount = {}; | |
| words.forEach((word) => { | |
| word = word.trim().toLowerCase(); | |
| if (word.length > 0) { | |
| if (!wordCount[word]) { | |
| wordCount[word] = 1; | |
| } else { | |
| wordCount[word]++; | |
| } | |
| } | |
| }); | |
| const totalUniqWord = Object.keys(wordCount).length; | |
| const filteredWords = Object.keys(wordCount).filter((word) => { | |
| return wordCount[word] >= n; | |
| }); | |
| const filteredWordCount = {}; | |
| filteredWords.forEach((word) => { | |
| filteredWordCount[word] = wordCount[word]; | |
| }); | |
| return { | |
| totalUniqWord: totalUniqWord, | |
| filteredWordCount: filteredWordCount, | |
| }; | |
| } | |
| function wrapWords(node) { | |
| // Generated with ChatGPT | |
| if (node.nodeType === Node.TEXT_NODE) { | |
| const words = node.textContent.trim().split(/\s+/); | |
| const fragment = document.createDocumentFragment(); | |
| words.forEach((word, i) => { | |
| word = word.trim(); | |
| if (i > 0) { | |
| fragment.appendChild(document.createTextNode(" ")); // Add a space after each word | |
| } | |
| const w = document.createElement("w"); | |
| w.textContent = word + " "; | |
| fragment.appendChild(w); | |
| w.addEventListener( | |
| "click", | |
| (e) => { | |
| if (highlightedWord !== null) { | |
| highlightedWord.classList.remove("hil1ghtd1"); | |
| } | |
| highlightedWord = w; | |
| w.classList.add("hil1ghtd1"); | |
| gText(e); | |
| }, | |
| false | |
| ); | |
| }); | |
| const wrappedText = fragment.cloneNode(true).textContent; | |
| node.parentNode.replaceChild(fragment, node); | |
| } else if (node.nodeType === Node.ELEMENT_NODE) { | |
| for (let i = 0; i < node.childNodes.length; i++) { | |
| wrapWords(node.childNodes[i]); | |
| } | |
| } | |
| } | |
| /* max z-index: 2147483647; on top, but avoid using it | |
| font-family: Pyidaungsu, "Myanmar3", "Myanmar Sangam MN", "Myanmar MN", "Myanmar Text", Padauk, Tharlon, "Masterpiece Uni Sans", "Noto Sans Myanmar", sans-serif; | |
| background-color: #3B3A38 !important; | |
| */ | |
| let popup_style = ` | |
| display: none; | |
| position: fixed; | |
| top: 0px; | |
| right: 0%; | |
| left: 0%; | |
| z-index: 2147483647; | |
| text-align: left; | |
| font-size: medium; | |
| max-height: 65%; | |
| width: auto; | |
| padding: 4px; | |
| border-bottom: orange solid 1.5px; | |
| background-color: cornsilk !important; | |
| overflow-x: scroll; | |
| overflow-y: scroll; | |
| `; | |
| const wFredDivSyle = ` | |
| text-align: left; | |
| font-size: small; | |
| padding: 4px; | |
| border: brown solid 1px; | |
| `; | |
| // Get the <head> element | |
| let head = document.head; | |
| // If it doesn't exist, create it and add it to the document | |
| if (!head) { | |
| head = document.createElement("head"); | |
| document.documentElement.appendChild(head); | |
| } | |
| if (document.head) { | |
| const styleHighlight = document.createElement("style"); | |
| styleHighlight.textContent = ".hil1ghtd1 { background-color: yellow; }"; | |
| document.head.appendChild(styleHighlight); | |
| } | |
| if (document) { | |
| const { totalUniqWord, filteredWordCount } = countWordFrequency(filterN); | |
| let dict = document.createElement("div"); | |
| dict.id = "pnrydict"; | |
| dict.setAttribute("style", popup_style); | |
| var wFredDiv = document.createElement("div"); | |
| wFredDiv.id = "wFredDiv"; | |
| wFredDiv.setAttribute("style", wFredDivSyle); | |
| if (document.body) { | |
| const body = document.querySelector("body"); | |
| document.body.prepend(dict); | |
| const highFreqCount = Object.keys(filteredWordCount).length; | |
| if (highFreqCount > 0) { | |
| const wFredDivHeader = document.createElement("b"); | |
| wFredDivHeader.textContent = `Unique words/phrases: ${totalUniqWord}. With freq >=${filterN} times: ${highFreqCount}`; | |
| wFredDiv.prepend(wFredDivHeader); | |
| // wFredDiv.append(document.createElement("hr")); | |
| let countN = 0; | |
| const wFredDivTable = document.createElement("table"); | |
| wFredDivTable.style.width = "100%"; | |
| wFredDivTable.style.borderCollapse = "collapse"; | |
| const wFredDivTableBody = document.createElement("tbody"); | |
| const wFredDivTableRow = document.createElement("tr"); | |
| const wFredDivTableCell1 = document.createElement("td"); | |
| wFredDivTableCell1.style.verticalAlign = "top"; | |
| wFredDivTableCell1.style.borderRight = "1px solid gray"; | |
| wFredDivTableRow.appendChild(wFredDivTableCell1); | |
| const wFredDivTableCell2 = document.createElement("td"); | |
| wFredDivTableCell2.style.verticalAlign = "top"; | |
| wFredDivTableCell2.style.paddingLeft = "5px"; | |
| wFredDivTableRow.appendChild(wFredDivTableCell2); | |
| wFredDivTableBody.appendChild(wFredDivTableRow); | |
| wFredDivTable.appendChild(wFredDivTableBody); | |
| Object.keys(filteredWordCount).forEach((word, index) => { | |
| const freq = filteredWordCount[word]; | |
| if (freq >= filterN) { | |
| const wordDiv = document.createElement("div"); | |
| countN++; | |
| wordDiv.textContent = `${countN}. ${word} ${freq}`; | |
| if (index < highFreqCount / 2) { | |
| wFredDivTableCell1.appendChild(wordDiv); | |
| } else { | |
| wFredDivTableCell2.appendChild(wordDiv); | |
| } | |
| } | |
| }); | |
| wFredDiv.appendChild(wFredDivTable); | |
| dict.insertAdjacentElement("afterend", wFredDiv); | |
| wFredDiv.insertAdjacentHTML("afterend", "<br>"); | |
| } | |
| wrapWords(body); | |
| } | |
| } | |
| function findStrFromSelection() { | |
| // Adapted from https://stackoverflow.com/a/70541520 | |
| if (!window.getSelection) { | |
| console.log("Not supported window.getSelection"); | |
| return; | |
| } | |
| var t = ""; | |
| var s = window.getSelection(); | |
| var range = s.getRangeAt(0); | |
| var node = s.anchorNode; | |
| var content = node.textContent; | |
| var startOffset = range.startOffset; | |
| var endOffset = range.endOffset; | |
| // Find starting point | |
| // We move the cursor back until we find a space a line break or the start of the node | |
| do { | |
| startOffset--; | |
| } while ( | |
| startOffset > 0 && | |
| content[startOffset - 1] != " " && | |
| content[startOffset - 1] != "\n" | |
| ); | |
| // Find ending point | |
| // We move the cursor forward until we find a space a line break or the end of the node | |
| do { | |
| endOffset++; | |
| } while ( | |
| content[endOffset] != " " && | |
| content[endOffset] != "\n" && | |
| endOffset < content.length | |
| ); | |
| t = content.substring(startOffset, endOffset); | |
| // Remove all punctuation from word | |
| t = t.replace(punRex, ""); | |
| return t; | |
| } | |
| // function joinNestedArray(ary) { | |
| // return ary.reduce((acc, val) => acc.concat(...val), []).join(" "); | |
| // } | |
| function wrapSection(x) { | |
| return `<section class="pnryDefiClass" onclick="this.innerHTML = ''; this.style.display = 'none';" style="border: solid black 1px; border-radius: 2%;padding: 5px; box-shadow: 3px 3px 10px #888888;; display:;">${x}</section>`; | |
| } | |
| function wrapSectionHead(x) { | |
| return `<section class="pnryWordHeadClass" onclick="document.getElementById('pnrydict').innerHTML=''; document.getElementById('pnrydict').style.display = 'none';" style="border: solid black 1.5px; border-radius: 2%; padding: 5px; box-shadow: 3px 3px 10px #888888; display:;">${x}</section>`; | |
| } | |
| function splitAndLookUp(text) { | |
| text = text.trim(); | |
| const rawInput = text; | |
| let syllables = segment(text); | |
| // filter punctuation, whitespace | |
| syllables = syllables | |
| .filter((element) => element.replace(punRex, "").trim().length > 0) | |
| .map((element) => element.trim()); | |
| let result = []; | |
| let wordList = []; | |
| let i = 0; | |
| while (i < syllables.length) { | |
| for (let j = syllables.length; j > i; j--) { | |
| let word = syllables.slice(i, j).join(""); | |
| if (word in DICTOBJ) { | |
| wordList.push(word); | |
| // let x = `<p style='color:brown;'><b>${word}</b></p>${joinNestedArray(DICTOBJ[word])}`; | |
| let x = `<p style='color:brown;'><b>${word}</b></p>${DICTOBJ[word]}`; | |
| x = wrapSection(x); | |
| result.push(x); | |
| i = j; | |
| break; | |
| } else { | |
| if (j === i + 1) { | |
| let s = syllables[i]; | |
| wordList.push(s); | |
| let x = `<p style='color:brown;'><b>${s}</b></p> ?`; | |
| x = wrapSection(x); | |
| result.push(x); | |
| i++; | |
| } | |
| } | |
| } | |
| } | |
| let split_list = | |
| `<span style="color:blue"><b>${rawInput}</b></span>` + | |
| `<br>=> (${wordList.length}): ` + | |
| wordList.join(" + ") + | |
| '<br><div style="text-align:center;">🤗</div>'; | |
| // console.log(result); | |
| let html = wrapSectionHead(split_list) + result.join(" "); | |
| return html; | |
| } | |
| function lookUpWord(input) { | |
| let i = input.length; | |
| if (DICTOBJ.hasOwnProperty(input)) { | |
| let x = `<p style='color:brown;'>💯 <b>${input}</b></p>${DICTOBJ[input]}`; | |
| x = wrapSection(x); | |
| return x; | |
| } | |
| return splitAndLookUp(input); | |
| } | |
| function gText(e) { | |
| // Modified from https://gist.github.com/shivprasad/1088985 | |
| window.clearTimeout(timeoutIdPopup); | |
| var t = findStrFromSelection(); | |
| let ele = document.getElementById("pnrydict"); | |
| if (!ele) { | |
| return; | |
| } | |
| if (ele.contains(e.target)) { | |
| return; | |
| } | |
| ele.innerHTML = ""; | |
| if (t.length > 0) { | |
| let w = t.toLowerCase(); | |
| //let res = DICTOBJ[w]; | |
| let res = lookUpWord(w); | |
| if (res) { | |
| ele.style.display = ""; | |
| ele.innerHTML = `${res}`; | |
| if (detectLatinWord(w)) { | |
| SKS(w); | |
| } | |
| // auto hide after 60s require JavaScript enabled | |
| timeoutIdPopup = window.setTimeout(function () { | |
| ele.innerHTML = ""; | |
| ele.style.display = "none"; | |
| }, 60000); | |
| } else { | |
| ele.style.display = "none"; | |
| } | |
| // SKS(w); | |
| } else { | |
| ele.style.display = "none"; | |
| } | |
| } | |
| // document.addEventListener("click", gText, false); | |
| // function getClickedWord(e) { | |
| // /** Generated with Bing AI ChatGPT */ | |
| // var defiEl = document.getElementById("dictionary-res"); | |
| // var tocDivBox = document.getElementById("tocDivBox"); | |
| // /** Not getting words on these elements */ | |
| // if ( | |
| // !defiEl.contains(e.target) && | |
| // !tocDivBox.contains(e.target) && | |
| // !e.target.classList.contains("tocDivBox_a") | |
| // ) { | |
| // var node, offset; | |
| // if (document.caretRangeFromPoint) { | |
| // var range = document.caretRangeFromPoint(e.clientX, e.clientY); | |
| // node = range.startContainer; | |
| // offset = range.startOffset; | |
| // } else if (document.caretPositionFromPoint) { | |
| // var caretPos = document.caretPositionFromPoint(e.clientX, e.clientY); | |
| // node = caretPos.offsetNode; | |
| // offset = caretPos.offset; | |
| // } | |
| // var textNodeContent = node.textContent; | |
| // var start = offset; | |
| // var end = offset; | |
| // while (start > 0 && /\S/.test(textNodeContent[start - 1])) { | |
| // start--; | |
| // } | |
| // while (end < textNodeContent.length && /\S/.test(textNodeContent[end])) { | |
| // end++; | |
| // } | |
| // var word = textNodeContent.slice(start, end); | |
| // if (word) { | |
| // word = word.toLowerCase(); | |
| // // Remove all punctuation from word | |
| // word = word.replace( | |
| // new RegExp(`^[${puncSpecialChars}]+|[${puncSpecialChars}]+$`, "g"), | |
| // "" | |
| // ); | |
| // return word.trim(); | |
| // } | |
| // } | |
| // } | |
| // function appendCloseMe() { | |
| // function closeMe(e, head = false) { | |
| // if (head) { | |
| // e = document.getElementById("pnrydict"); | |
| // } | |
| // e.innerHTML = ""; | |
| // e.style.display = "none"; | |
| // } | |
| // var head = document.getElementsByTagName("head")[0]; | |
| // var script = document.createElement("script"); | |
| // script.type = "text/javascript"; | |
| // script.innerHTML = closeMe; | |
| // head.appendChild(script); | |
| // } | |
| // Text to speech | |
| var TTS = "speechSynthesis" in window ? window.speechSynthesis : null; | |
| function SKS(word) { | |
| //Slow version | |
| if (!TTS) { | |
| console.log("TTS is not available in window"); | |
| return; | |
| } | |
| console.log("TTS is available in window"); | |
| // stop the previous speech | |
| TTS.cancel(); | |
| let utterance = new SpeechSynthesisUtterance(word); | |
| utterance.lang = "en-GB"; //en-US, en-GB | |
| utterance.pitch = "0.8"; | |
| utterance.rate = "0.5"; | |
| TTS.speak(utterance); | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment