Skip to main content

VARS_JS

Constant VARS_JS 

Source
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)?;