Skip to content

Instantly share code, notes, and snippets.

@nexpr
Last active September 19, 2022 04:15
Show Gist options
  • Save nexpr/bd21d4887f26a834d4d496bdcc8d0211 to your computer and use it in GitHub Desktop.
Save nexpr/bd21d4887f26a834d4d496bdcc8d0211 to your computer and use it in GitHub Desktop.
  • XML としてパースする
  • S タグの属性でスタイルを指定
    • a: text-align
    • c: color
    • d: text-decoration
    • f: font-family
    • s: font-size
    • w: font-weight
    • ex: <S w="bold" c="blue">blue-bold</S>
  • D タグでタグ定義
    • name: タグ名
    • scope: タグの有効スコープ
      • global: 全体
      • sibling: D タグの兄弟要素
      • children: D タグの子要素
      • D タグ要素より前には影響を与えない
    • ex: <D name="FOO" c="red" /><FOO>red</FOO>
  • C タグでコメント
<!DOCTYPE html>
<meta charset="utf-8" />
<script type="module">
const parseAttrs = (element) => {
const attrs = {}
for (const attr of element.attributes) {
attrs[attr.name] = attr.value
}
return attrs
}
const applyAttrs = (elem, attrs) => {
for (const [k, v] of Object.entries(attrs)) {
if (k === "c") {
elem.style.color = v
} else if (k === "f") {
elem.style.fontFamily = v
} else if (k === "w") {
elem.style.fontWeight = v
} else if (k === "s") {
elem.style.fontSize = v
} else if (k === "a") {
elem.style.textAlign = v
elem.style.display = "block"
} else if (k === "d") {
elem.style.textDecoration = v
}
}
}
const convert = (str) => {
const parsed = new DOMParser().parseFromString(`<S>${str}</S>`, "text/xml")
console.log(parsed)
const root = parsed.documentElement
const result_root = document.createElement("root")
const global_defined = {}
const walk = (target, elem, defined) => {
const nodes = [...elem.childNodes]
for (const [index, node] of nodes.entries()) {
const sub_defined = Object.create(defined)
if (node.nodeName === "#text") {
let text = node.textContent
if (index === 0) text = text.trimStart()
if (index === nodes.length - 1) text = text.trimEnd()
target.append(text)
} else if (node.tagName in defined) {
const defined_attrs = defined[node.tagName]
const span = document.createElement("span")
const attrs = parseAttrs(node)
applyAttrs(span, { ...defined_attrs, ...attrs })
walk(span, node, sub_defined)
target.append(span)
} else if (node.tagName === "S") {
const span = document.createElement("span")
const attrs = parseAttrs(node)
applyAttrs(span, attrs)
walk(span, node, sub_defined)
target.append(span)
} else if (node.tagName === "D") {
const scope = node.getAttribute("scope")
const name = node.getAttribute("name")
const attrs = parseAttrs(node)
if (scope === "global") {
global_defined[name] = attrs
} else if (scope === "sibling") {
defined[name] = attrs
} else {
sub_defined[name] = attrs
}
walk(target, node, sub_defined)
} else if (node.tagName === "C") {
// comment
} else if (node.tagName === "parsererror") {
target.append(node.cloneNode(true))
} else {
const error = document.createElement("div")
error.textContent = `Found unknown tag "${node.tagName}"`
error.style = "color:red;border:2px solid crimson;padding:4px 8px;background-color:lavenderblush"
target.append(error)
}
}
}
walk(result_root, root, global_defined)
return result_root.innerHTML
}
let gtid = null
src.oninput = () => {
const ltid = gtid = Math.random()
setTimeout(() => {
if (ltid === gtid) update()
}, 2000)
}
const update = () => {
const code = convert(src.value)
html.value = code
result.innerHTML = code
}
src.value = `
|foo <S c="#f88">color</S> a<S w="bold" f="consolas" s="12px">bold code</S> bar
|text1<S a="center">center</S>text2<S a="right">right</S>text3
|text4<S a="center">center<S a="right">right</S></S>text5
|<S c="#f00">
| <D name="FOO" c="#080">
| A <FOO>B</FOO> C <FOO w="bold">D</FOO> E
| </D>
| <FOO>F</FOO>
| <D name="FOO" c="orange" scope="sibling"/>
| <FOO>G</FOO>
| <D name="BAR" c="royalblue" scope="global"/>
| <BAR>H</BAR>
|</S>
|<FOO>I</FOO>
|<BAR>J</BAR>
|<C>not shown</C>shown
`.split("\n").map(x => x.match(/^\s*\|(.*)/)?.[1]).filter(Boolean).join("\n")
update(src.value)
</script>
<style>
* {
box-sizing: border-box;
}
body {
margin: 0;
display: grid;
min-height: 100vh;
grid-template-columns: 1fr 1fr;
grid-template-rows: 3fr 2fr;
gap: 10px;
padding: 10px;
font-size: 16px;
}
textarea {
resize: none;
font-family: monaco, consolas;
}
#result {
grid-row: 1 / 3;
grid-column: 2;
border: 1px solid silver;
border-radius: 3px;
margin: 0;
padding: 10px;
white-space: pre-wrap;
}
</style>
<textarea id="src"></textarea>
<textarea id="html" readonly></textarea>
<div id="result"></div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment