Better Furigana Markup

Run Settings
LanguageJavaScript
Language Version
Run Command
// Vision: Turn this into a library! // besides being able to call functions this should also automatically parse HTML tags // <div data-better-furigana>猫[ねこ]</div> should render the rubys inside properly // this obv also should have options like <div ... data-better-furigana-render-pitch-at="#other-elem"> // or osmething like that const examples = [ "猫[ねこ]", "役に立つ[や/く\\_た_]", "相槌[あ/いづ\\ち]", "我輩[わ/がはい]は|猫[ね\\こ]である" ]; console.log("Examples:", examples); const parsed = examples.map(e => parse(e)); console.log("Parsed:\n", parsed); console.log("Rendered:\n", examples.map(e => render(e))); function parse(str) { const regex = new RegExp("(\\p{L}+)\\[([\\p{L}/\\\\_]+)\\]", "ugm"); const result = []; while ((m = regex.exec(str)) !== null) { if (m.index === regex.lastIndex) { regex.lastIndex++; } result.push(m); } return result; } // TODO: add configuration options function render(str) { const parsed = parse(str); if (parsed.length === 0) { return ""; } for (let component of parsed) { // TODO: add seperate function to render hiragana but with pitch accent // e.g. 私見[し/けん] becomes し/けん // TODO: add automatic categorisation to a pattern like Heiban, Nakadaka, etc. const rubyString = rubify(component[1], component[2]); str = str.replace(component[0], rubyString); } // remove "|" as a special delimiter return str; } function rubify(word, annotation) { const annotationParts = annotation.split(""); const result = []; let wordIndex = 0; for (let i = 0; i < annotationParts.length; i++) { const annotation = annotationParts[i]; if (annotation === "/" || annotation === "\\") { continue; } if (annotation === "|") { wordIndex++; continue; } if (annotation === "_") { wordIndex += 2; result.push(null); continue; } if (!result[wordIndex]) { result[wordIndex] = []; } const elementBefore = i > 0 ? annotationParts[i - 1] : null; const nextElement = i < annotationParts.length - 1 ? annotationParts[i + 1] : null; const elem = {val: annotation}; if (elementBefore === "/") { elem.pitchUp = true; } if (nextElement === "\\") { elem.pitchDown = true; } result[wordIndex].push(elem); } const renderLetter = (elem) => { return elem.val; }; const makeRuby = (word, annotation) => { return `<ruby>${word}<rt>${annotation}</rt></ruby>`; } if (result.length === 1) { return `<span class="word">${makeRuby(word, result[0].map(renderLetter).join(""))}</span>`; } const wordParts = word.split(""); const wordMap = {}; for (let i = 0; i < wordParts.length; i++) { const p = wordParts[i]; wordMap[p] = result[i]; } console.log(`${word}[${annotation}]`, wordMap); let wordString = ""; for (let wordPart of Object.keys(wordMap)) { const annotations = wordMap[wordPart]; if (annotations === null) { wordString += wordPart; continue; } wordString += makeRuby(wordPart, annotations.map(renderLetter).join("")); } return `<span class="word">${wordString}</span>`; }
Editor Settings
Theme
Key bindings
Full width
Lines