- 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 タグでコメント
Last active
September 19, 2022 04:15
-
-
Save nexpr/bd21d4887f26a834d4d496bdcc8d0211 to your computer and use it in GitHub Desktop.
テキスト入力で色やサイズの指定 https://gistcdn.githack.com/nexpr/bd21d4887f26a834d4d496bdcc8d0211/raw/43680d61d597c360db0ec92c6cf7a3fcf8554223/sxml.html
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
<!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