138 lines
4.6 KiB
HTML
138 lines
4.6 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Document</title>
|
|
|
|
<style>
|
|
textarea {
|
|
width: 100%;
|
|
height: 250px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<form onsubmit="convert(event)">
|
|
<fieldset>
|
|
<legend>Data</legend>
|
|
|
|
<label for="input">Input</label>
|
|
<br />
|
|
<textarea name="input" id="input" required></textarea>
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend>Output</legend>
|
|
|
|
<label for="output">Output</label>
|
|
<br />
|
|
<textarea name="output" id="output" disabled></textarea>
|
|
<br />
|
|
<button
|
|
type="button"
|
|
onclick="window.navigator.clipboard.writeText(document.getElementById('output').value)"
|
|
>
|
|
Copy
|
|
</button>
|
|
</fieldset>
|
|
|
|
<br />
|
|
<button>Submit</button>
|
|
</form>
|
|
|
|
<script>
|
|
const INDENT_SPACES = 4;
|
|
function get_indent_str(indent) {
|
|
return " ".repeat(indent > 0 ? indent * INDENT_SPACES : 0);
|
|
}
|
|
|
|
function gen_lisp(
|
|
element,
|
|
indent = 0,
|
|
add_n_after_elements = false,
|
|
) {
|
|
const indent_str = get_indent_str(indent);
|
|
let output = "";
|
|
|
|
for (const node of element.childNodes) {
|
|
if (node.nodeName === "#text") {
|
|
const data = node.data.trim();
|
|
|
|
if (!data) {
|
|
continue;
|
|
}
|
|
|
|
if (data.startsWith("{")) {
|
|
// templating
|
|
let text = "";
|
|
|
|
for (const line of data
|
|
.replaceAll('"', '\\"')
|
|
.split("\n")) {
|
|
text += ` ${line.trim()}`;
|
|
}
|
|
|
|
output += `${indent_str}(text "${text.trim().replaceAll('"', '\\"').replaceAll('\\\\"', '\\"').replaceAll("}} {{", "}}\n{{")}")\n`;
|
|
} else {
|
|
// normal text
|
|
output += `${indent_str}(text "${data.replaceAll('"', '\\"')}")\n`;
|
|
}
|
|
} else if (node.localName) {
|
|
output += `${indent_str}(${node.localName}\n`;
|
|
|
|
// add attributes
|
|
for (const attr of node.attributes || []) {
|
|
output += `${get_indent_str(indent + 1)}("${attr.name}" "${attr.value.replaceAll('"', '\\\\\\"')}")\n`;
|
|
}
|
|
|
|
// add children
|
|
output += gen_lisp(node, indent + 1);
|
|
|
|
// close block
|
|
output = output.slice(0, output.length - 1); // remove last character (new line)
|
|
output += add_n_after_elements ? ")\n\n" : ")\n";
|
|
} else if (node.nodeName === "#comment") {
|
|
let text = "";
|
|
|
|
for (const line of node.textContent.split("\n")) {
|
|
if (line.trim() === "prettier-ignore") {
|
|
continue;
|
|
}
|
|
|
|
text += `${indent_str};${line.trimEnd()}\n`;
|
|
}
|
|
|
|
output += text;
|
|
} else {
|
|
console.warn(
|
|
"skipped element with no local name:",
|
|
node,
|
|
);
|
|
}
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
function convert(e) {
|
|
e.preventDefault();
|
|
const element_document = new DOMParser().parseFromString(
|
|
e.target.input.value,
|
|
"text/html",
|
|
);
|
|
|
|
const start = performance.now();
|
|
e.target.output.value = gen_lisp(
|
|
element_document.body,
|
|
0,
|
|
true,
|
|
);
|
|
|
|
console.log(
|
|
`processed ${e.target.input.value.length} characters in ${performance.now() - start}ms`,
|
|
);
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|