pub const VARS_JS: &str = "// The numbers an emitter hands the stylesheet, set where a policy allows it.\n//\n// A bar\'s value, a row\'s depth, a track\'s place and a split\'s share are custom\n// properties the rules read. A `style-src` without `\'unsafe-inline\'` refuses\n// every style attribute, so the emitters write the declarations into\n// `data-vars` instead, and this sets each one through the CSSOM, which the\n// policy does not police.\n//\n// A classic script in the head, not deferred, on purpose: the observer is\n// running before the parser reaches the body, so every element gets its\n// properties as it is inserted and the first paint is already right. A deferred\n// script would paint every chart flat and then grow it.\n//\n// Only custom properties. Applying any declaration found in markup would hand\n// back exactly what the policy refused, so a name that does not start with\n// `--` is skipped.\n(() => {\n \"use strict\";\n\n /** The attribute the emitters write: `makeover_webview::VARS_ATTR`. */\n const ATTR = \"data-vars\";\n\n /** What this script set on each element, so an update can take it back. */\n const set = new WeakMap();\n\n /** `--a: 1; --b: 2` as pairs, keeping only custom properties. */\n const pairs = (text) => {\n const found = new Map();\n for (const part of text.split(\";\")) {\n const at = part.indexOf(\":\");\n if (at < 0) continue;\n const name = part.slice(0, at).trim();\n if (!name.startsWith(\"--\")) continue;\n found.set(name, part.slice(at + 1).trim());\n }\n return found;\n };\n\n /**\n * Make the element\'s properties what its attribute says now.\n *\n * A morph that changes the attribute in place leaves the element, so a\n * property the new text no longer names is removed rather than kept from\n * the old one.\n */\n const apply = (el) => {\n const next = pairs(el.getAttribute(ATTR) ?? \"\");\n for (const name of set.get(el) ?? []) {\n if (!next.has(name)) el.style.removeProperty(name);\n }\n for (const [name, value] of next) el.style.setProperty(name, value);\n set.set(el, [...next.keys()]);\n };\n\n /** An inserted subtree: the node itself and everything under it. */\n const within = (node) => {\n if (node.nodeType !== Node.ELEMENT_NODE) return;\n if (node.hasAttribute(ATTR)) apply(node);\n for (const el of node.querySelectorAll(`[${ATTR}]`)) apply(el);\n };\n\n new MutationObserver((records) => {\n for (const record of records) {\n if (record.type === \"attributes\") apply(record.target);\n else record.addedNodes.forEach(within);\n }\n }).observe(document.documentElement, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [ATTR],\n });\n\n // Whatever the parser had already written before this ran.\n within(document.documentElement);\n})();\n";Expand description
The script that applies VARS_ATTR, for a host to serve beside its other
assets.
Without it every number the rules read from that attribute is missing: bars and charts flat, tree rows unindented, a split screen at even halves. Serve it as a classic script in the head, not deferred, so its observer is running before the parser reaches the body and the first paint already has every property.
std::fs::write("static/vars.js", makeover_webview::VARS_JS)?;