From 56ca0c8be0defd29dbbc3da29ff6d6e0abf3ab0d Mon Sep 17 00:00:00 2001 From: Steven Kraft Date: Mon, 10 May 2021 16:20:59 -0400 Subject: [PATCH 1/4] Using this.registerMarkdownPostProcessor Instead of MarkdownPreviewRenderer.registerPostProcessor to automatically unregister on unload. --- main.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/main.ts b/main.ts index b204b98..46fcc0e 100644 --- a/main.ts +++ b/main.ts @@ -55,11 +55,10 @@ export default class MarkdownFurigana extends Plugin { async onload () { console.log('loading Markdown Furigana plugin') - MarkdownPreviewRenderer.registerPostProcessor(this.postprocessor) + this.registerMarkdownPostProcessor(this.postprocessor) } onunload () { console.log('unloading Markdown Furigana plugin') - MarkdownPreviewRenderer.unregisterPostProcessor(this.postprocessor) } } From 9febee436fa49bf2feecddaeaa70af60d0b188ae Mon Sep 17 00:00:00 2001 From: Steven Kraft Date: Mon, 10 May 2021 16:32:05 -0400 Subject: [PATCH 2/4] Improved efficiency by removing innerHTML check --- main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.ts b/main.ts index 46fcc0e..3ca4622 100644 --- a/main.ts +++ b/main.ts @@ -37,7 +37,7 @@ const convertFurigana = (element:Node): Node => { export default class MarkdownFurigana extends Plugin { public postprocessor: MarkdownPostProcessor = (el: HTMLElement, ctx: MarkdownPostProcessorContext) => { const blockToReplace = el.querySelector(TAGS) - if (!blockToReplace || !blockToReplace.innerHTML.match(REGEXP)) return + if (!blockToReplace) return function replace (node:Node) { node.childNodes.forEach(child => { From e0eba563afec56a01c6e9ee77adfaff8842663db Mon Sep 17 00:00:00 2001 From: Steven Kraft Date: Mon, 10 May 2021 17:56:03 -0400 Subject: [PATCH 3/4] Improved Security by no longer modifying innerHTML Switched to inserting and replacing elements instead of modifying innerHTML of existing elements. Should prevent arbitrary DOM injection. --- main.ts | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/main.ts b/main.ts index 3ca4622..8ba58ad 100644 --- a/main.ts +++ b/main.ts @@ -1,4 +1,4 @@ -import { Plugin, MarkdownPostProcessor, MarkdownPostProcessorContext, MarkdownPreviewRenderer } from 'obsidian' +import { Plugin, MarkdownPostProcessor, MarkdownPostProcessorContext } from 'obsidian' // Regular Expression for {{kanji|kana|kana|...}} format const REGEXP = /{((?:[一-龯]|[ぁ-んァ-ン])+)((?:\|[ぁ-んァ-ン]*)+)}/gm @@ -6,32 +6,37 @@ const REGEXP = /{((?:[一-龯]|[ぁ-んァ-ン])+)((?:\|[ぁ-んァ-ン]*)+)}/gm // Main Tags to search for Furigana Syntax const TAGS = 'p, h1, h2, h3, h4, h5, h6, ol, ul, table' -const convertFurigana = (element:Node): Node => { - const text = element.textContent - const matches = Array.from(text.matchAll(REGEXP)) +const convertFurigana = (element:Text): Node => { + const matches = Array.from(element.textContent.matchAll(REGEXP)) if (matches.length === 0) return element - - let newText = text - + let lastNode = element for (const match of matches) { const kanji = match[1].split('') const furi = match[2].split('|').slice(1) // First Element will be empty if (kanji.length === furi.length || furi.length === 1) { // Number of Characters in first section must be equal to number of furigana sections (unless only one furigana section) - newText = newText.replace(match[0], function () { - // Create a stringified version of the ruby HTMLElement - if (furi.length === 1) return `${kanji.join('')}${furi[0]}` - const innerHTML = kanji.map((k, i) => { return `${k}${furi[i]}` }).join('') - return `${innerHTML}` - }) + const rubyNode = document.createElement('ruby') + rubyNode.addClass('furi') + let rt + if (furi.length === 1) { + rubyNode.appendChild(document.createTextNode(kanji.join(''))) + rt = document.createElement('rt') + rt.innerText = furi[0] + rubyNode.appendChild(rt) + } else { + kanji.forEach((k, i) => { + rubyNode.appendChild(document.createTextNode(k)) + rt = document.createElement('rt') + rt.innerText = furi[i] + rubyNode.appendChild(rt) + }) + } + const nodeToReplace = lastNode.splitText(lastNode.textContent.indexOf(match[0])) + lastNode = nodeToReplace.splitText(match[0].length) + nodeToReplace.replaceWith(rubyNode) } } - - // Replace TextElement with new Span containing Ruby Element(s) - const newElement = document.createElement('span') - newElement.addClass('furi') - newElement.innerHTML = newText - return newElement + return element } export default class MarkdownFurigana extends Plugin { @@ -43,7 +48,7 @@ export default class MarkdownFurigana extends Plugin { node.childNodes.forEach(child => { if (child.nodeType === 3) { // Nodes of Type 3 are TextElements - child.replaceWith(convertFurigana(child)) + child.replaceWith(convertFurigana(child as Text)) } else if (child.hasChildNodes() && child.nodeName !== 'CODE') { // Ignore content in Code Blocks replace(child) From ae20248effb09d21c008cfe7a723d80f8561ef0f Mon Sep 17 00:00:00 2001 From: Steven Kraft Date: Mon, 10 May 2021 17:56:56 -0400 Subject: [PATCH 4/4] Version Update to 1.1.0 --- manifest.json | 2 +- package.json | 2 +- versions.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index b00f056..9d1cd41 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-markdown-furigana", "name": "Markdown Furigana", - "version": "1.0.0", + "version": "1.1.0", "minAppVersion": "0.9.12", "description": "Simple Markdown to Furigana Rendering Plugin for Obsidian.", "author": "Steven Kraft", diff --git a/package.json b/package.json index af72832..13035a0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-markdown-furigana", - "version": "1.0.0", + "version": "1.1.0", "description": "Simple Markdown to Furigana Rendering Plugin for Obsidian.", "main": "main.js", "scripts": { diff --git a/versions.json b/versions.json index be3344d..a565c2c 100644 --- a/versions.json +++ b/versions.json @@ -1,3 +1,3 @@ { - "1.0.0": "0.9.12" + "1.1.0": "0.9.12" }