-
Notifications
You must be signed in to change notification settings - Fork 0
/
x-style.js
104 lines (95 loc) · 2.94 KB
/
x-style.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
(() => {
// Aliases to aid in minification:
var doc = document;
var querySelectorAll = (e, s) => e.querySelectorAll(s);
// Plugins, list of functions that take the css and return a new css
var pluginsPre = [];
var pluginsPost = [];
/**
* x-style
* @param {string} attr - HTML attribute that contains the css, usually "css"
* @param {boolean} [noMutate] - Don't mutate the DOM by adding attributes
*/
var xstyle = (attr, noMutate) => {
var styleEl;
var style = "";
var selectorCount = 0;
var processedCss = new Map();
var attributeForSelector = `${attr}-match`;
var observer = new MutationObserver((mutations) => {
for (var mutation of mutations) {
if (mutation.type === "attributes") {
processEl(mutation.target);
} else if (mutation.type === "childList") {
for (var el of mutation.addedNodes) {
if (!(el instanceof HTMLElement)) continue;
if (el.hasAttribute(attr)) {
processEl(el);
}
[...querySelectorAll(el, `[${attr}]`)].forEach(processEl);
}
}
}
emitStyle();
});
var emitStyle = () => {
if (style) {
styleEl = doc.createElement("style");
styleEl.innerHTML = style;
doc.head.appendChild(styleEl);
styleEl = null;
style = "";
}
};
var setAttribute = (el, rawCss) => {
var selectorAttr = `${attributeForSelector}-${processedCss.get(rawCss)}`;
var prop = '__'+attributeForSelector;
if (el[prop]) {
el.removeAttribute(el[prop]);
}
el.setAttribute(selectorAttr, '');
el[prop] = selectorAttr;
return selectorAttr;
};
/**
* Process an element.
* Extract the css from the attribute and add it to the style element.
* If the css has already been processed, either add the attributeForSelector
* or do nothing.
* The style element is added to the head on the next microtask.
* @param {HTMLElement} el
*/
var processEl = (el) => {
var rawCss = el.getAttribute(attr);
var css;
if (!rawCss || processedCss.has(rawCss)) {
if (!noMutate) {
setAttribute(el, rawCss);
}
return;
}
processedCss.set(rawCss, ++selectorCount);
if (noMutate) {
css = `[${attr}="${CSS.escape(rawCss)}"]`;
} else {
css = `[${setAttribute(el, rawCss)}]`;
}
pluginsPre.forEach((plugin) => rawCss = plugin(rawCss));
css += ` { ${rawCss} }`;
pluginsPost.forEach((plugin) => css = plugin(css));
style += css + "\n";
};
querySelectorAll(doc, `[${attr}]`).forEach(processEl);
emitStyle();
observer.observe(doc.documentElement, {
attributes: true,
attributeFilter: [attr],
childList: true,
subtree: true,
});
}
xstyle.pre = pluginsPre;
xstyle.post = pluginsPost;
xstyle.version = "0.0.3";
window.xstyle = xstyle;
})();