-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathhalf-light.js
123 lines (112 loc) · 3.35 KB
/
half-light.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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
(function () {
let targetedStyles;
const openStylableElements = new Set();
const __alreadyAdopted = new WeakMap();
function isInDarkRoot(el) {
if (el.parentElement) {
return isInDarkRoot(el.parentElement)
} else if (el.host) {
if (el.host.hasAttribute('darkened') || el.host.darkened) {
return true
}
return isInDarkRoot(el.host)
}
return el !== document.documentElement && !el.host
}
function processSheet(rules, where="*") {
[...rules].forEach((rule) => {
targetedStyles[where] = targetedStyles[where] || [];
targetedStyles[where].push(rule.cssText);
});
}
function parseMQ(condition) {
let f = condition.match(/(?:--crossroot\({0,1})([^\)]*)/);
return {
isCrossRoot: condition === "--crossroot" || f,
where: f && f.length == 2 && f[1].trim() ? f[1] : "*"
}
}
function refreshTargetedStyles() {
targetedStyles = [];
[...document.styleSheets].forEach((sheet) => {
if (!sheet.ownerNode.matches("head > :not([no-half-light])")) return;
let sheetMQResult = parseMQ(sheet.media.mediaText);
if (sheetMQResult.isCrossRoot) {
processSheet(sheet.cssRules, sheetMQResult.where)
}
[...sheet.cssRules].forEach((rule) => {
let name = rule.constructor.name;
let cond = rule.conditionText || "";
let mqResult = parseMQ(cond);
if (name === "CSSMediaRule" && mqResult.isCrossRoot) {
processSheet(rule.cssRules, mqResult.where)
}
});
});
Object.keys(targetedStyles).forEach((where) => {
let sheet = new CSSStyleSheet();
sheet.insertRule(
"@layer --crossroot {" + targetedStyles[where].join("\n") + "}"
);
targetedStyles[where] = sheet;
});
}
function clearStyles(element) {
element.shadowRoot.adoptedStyleSheets = [];
(__alreadyAdopted.get(element) || []).forEach((s) => {
element.shadowRoot.adoptedStyleSheets.push(s);
});
}
function setStyles(element) {
for (let selector in targetedStyles) {
if (element.matches(selector)) {
element.shadowRoot.adoptedStyleSheets.push(targetedStyles[selector]);
}
}
}
const init = () => {
refreshTargetedStyles();
for (const element of openStylableElements) {
clearStyles(element);
setStyles(element);
}
}
const observer = new MutationObserver(init);
requestAnimationFrame(() => {
init()
observer.observe(document.head, {
childList: true,
subtree: true,
characterData: true,
attributes: true,
});
})
let script = document.currentScript;
let liveEnabled = true;
document.addEventListener("DOMContentLoaded", () => {
if(script.hasAttribute('disable-live-half-light')) {
observer.disconnect()
openStylableElements.clear()
liveEnabled = false
}
})
let old = Element.prototype.attachShadow;
Element.prototype.attachShadow = function () {
let r = old.call(this, ...arguments);
if (arguments[0].mode !== 'open') return r;
requestAnimationFrame(() => {
if (isInDarkRoot(r)) {
return;
}
if (liveEnabled) {
openStylableElements.add(this);
}
__alreadyAdopted.set(
this,
Array.from(this.shadowRoot.adoptedStyleSheets)
);
setStyles(this);
});
return r;
};
})();