Last active
March 5, 2023 13:31
-
-
Save tachibanayui/636009165e8fdd4a2ee93542b486a614 to your computer and use it in GitHub Desktop.
Jisho + sharpi Lyrics
This file contains 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 Jisho.org + sharpi Lyrics | |
// @namespace http://tampermonkey.net/ | |
// @copyright MIT | |
// @version 0.1 | |
// @description Allow multiline input and output of japanese text and add romaji support | |
// @author sharpi (Tachibana Yui) | |
// @match https://jisho.org/* | |
// @icon https://www.google.com/s2/favicons?sz=64&domain=google.com | |
// @grant none | |
// @require https://unpkg.com/wanakana@5.1.0/wanakana.min.js | |
// ==/UserScript== | |
(() => { | |
// const keyword = decodeURI(window.location.pathname.split("/").last()); | |
const swanakana = document.createElement("script"); | |
swanakana.src = "https://unpkg.com/wanakana@5.1.0/wanakana.min.js"; | |
document.head.appendChild(swanakana); | |
const css = ` | |
#zen_bar { | |
max-height: 500px; | |
} | |
h1.logo a { | |
display: inline-block; | |
} | |
`; | |
const style = document.createElement("style"); | |
style.innerHTML = css; | |
document.head.appendChild(style); | |
const style2 = document.createElement("style"); | |
document.head.appendChild(style2); | |
// Branding | |
const brand = document.createElement("a"); | |
brand.href = "https://github.com/tachibanayui"; | |
brand.innerText = " + sharpi Lyrics"; | |
brand.style.background = "none"; | |
brand.style.display = "inline"; | |
brand.style.fontSize = "2rem"; | |
brand.style.color = "white"; | |
brand.style.textDecoration = "none"; | |
brand.classList.add("sharpi_lyrics"); | |
document.querySelector("h1.logo").appendChild(brand); | |
// --- | |
// Analytics (remove if you're not comfortable with this) | |
const gtag1 = document.createElement("script"); | |
gtag1.async = true; | |
gtag1.src = "https://www.googletagmanager.com/gtag/js?id=G-W9S48X5HJV"; | |
const gtag2 = document.createElement("script"); | |
gtag2.innerHTML = ` | |
window.dataLayer = window.dataLayer || []; | |
function gtag(){dataLayer.push(arguments);} | |
gtag('js', new Date()); | |
gtag('config', 'G-W9S48X5HJV'); | |
`; | |
document.head.prepend(gtag1, gtag2); | |
// --- | |
const oldKw = document.getElementById("keyword"); | |
const keyword = oldKw.getAttribute("value").replaceAll(" ", "\n"); | |
const newKw = createSearchKw(); | |
newKw.value = keyword; | |
const parent = oldKw.parentElement; | |
parent.insertBefore(newKw, oldKw); | |
parent.removeChild(oldKw); | |
const search = document.getElementById("search"); | |
search.style.display = "contents"; | |
document.querySelector("#page_container div").prepend(createControls()); | |
const words = document.querySelectorAll("#zen_bar li"); | |
let iWord = -1; | |
let current; | |
const romajis = []; | |
for (const line of keyword.split("\n")) { | |
if (!line.trim()) { | |
(current ?? words.item(iWord)) | |
.insertAdjacentElement("afterend", document.createElement("br")); | |
continue; | |
} | |
const lastChar = line.trim().last(); | |
let num = line | |
.chars() | |
.filter((x) => x === lastChar) | |
.count(); | |
let g; | |
while (num > 0) { | |
g = words.item(++iWord); | |
const newg = g.cloneNode(); | |
newg.classList.add("visRomaji"); | |
g.classList.add("visJapanese"); | |
const text = g.querySelector( | |
"span.japanese_word__text_wrapper" | |
).innerText; | |
let furi = g.querySelector( | |
"span.japanese_word__furigana_wrapper" | |
).innerText; | |
furi = furi ? furi : text; | |
const [fr, er] = createRomaji(furi, g.getAttribute("data-pos")); | |
newg.appendChild(fr); | |
newg.appendChild(er); | |
romajis.push(newg); | |
for (const letter of text) { | |
if (letter === lastChar) { | |
num--; | |
} | |
} | |
} | |
current = createSeperator(); | |
g.insertAdjacentElement("afterend", current); | |
for (const romaji of romajis) { | |
current.insertAdjacentElement("afterend", romaji); | |
current = romaji; | |
} | |
romajis.length = 0; | |
current.insertAdjacentElement("afterend", document.createElement("br")); | |
} | |
function createSearchKw() { | |
const newKw = document.createElement("textarea"); | |
newKw.setAttribute("class", "keyword japanese_gothic"); | |
newKw.setAttribute("name", "keyword"); | |
newKw.setAttribute("id", "keyword"); | |
newKw.setAttribute("tabindex", "1"); | |
newKw.setAttribute("lang", "ja"); | |
newKw.setAttribute("autocapitalize", "off"); | |
newKw.setAttribute("autocomplete", "off"); | |
newKw.setAttribute("autocorrect", "off"); | |
newKw.setAttribute( | |
"placeholder", | |
"English, Japanese, Romaji, words or text" | |
); | |
newKw.setAttribute("style", "height: 200px; color: white;"); | |
return newKw; | |
} | |
function createSeperator() { | |
const sep = document.createElement("br"); | |
// sep.innerText = " / "; | |
// sep.style.fontSize = "2rem"; | |
return sep; | |
} | |
function createRomaji(word, type) { | |
const romaji = toRomaji(word, type); | |
const fr = document.createElement("span"); | |
fr.classList.add("japanese_word__furigana_wrapper"); | |
const frc = document.createElement("span"); | |
frc.classList.add("japanese_word__furigana"); | |
frc.innerText = word === romaji ? "" : word; | |
fr.appendChild(frc); | |
const er = document.createElement('span'); | |
er.classList.add("japanese_word__text_wrapper"); | |
const erc = document.createElement("a"); | |
er.appendChild(erc); | |
const ercc = document.createElement("span"); | |
ercc.classList.add("japanese_word__text_with_furigana"); | |
ercc.innerText = romaji; | |
erc.appendChild(ercc); | |
return [fr, er]; | |
} | |
function toRomaji(word, type) { | |
// special cases | |
if (type === "Particle") { | |
switch (word.trim()) { | |
case "は": | |
return "wa"; | |
case "を": | |
return "o"; | |
case "へ": | |
return "e"; | |
} | |
} | |
return wanakana.toRomaji(word); | |
} | |
let showJapanese = true; | |
let showRomaji = true; | |
function createControls() { | |
const pdiv = document.createElement("div"); | |
const checkJp = document.createElement("input"); | |
checkJp.type = "checkbox"; | |
checkJp.checked = true; | |
checkJp.addEventListener("change", e => { | |
showJapanese = e.target.checked; | |
style2.innerHTML = createControlStyles(); | |
}) | |
const checkRj = document.createElement("input"); | |
checkRj.checked = true; | |
checkRj.type = "checkbox"; | |
checkRj.addEventListener("change", (e) => { | |
showRomaji = e.target.checked; | |
style2.innerHTML = createControlStyles(); | |
}); | |
pdiv.append(checkJp, "Show Japanese", document.createElement("br"), checkRj, "Show Romaji"); | |
return pdiv; | |
} | |
function createControlStyles() { | |
return ` | |
.visJapanese { | |
display: ${showJapanese ? "inline-block" : "none"} !important; | |
} | |
.visRomaji { | |
display: ${showRomaji ? "inline-block" : "none"} !important; | |
} | |
`; | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment