-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.browser.js
143 lines (113 loc) · 3.24 KB
/
index.browser.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
function computedVariables(
name = '',
func = () => '',
selector = window,
events = ['load', 'resize', 'input', 'click', 'recompute']
) {
// Reads individual CSS rules from a CSS rule list
const readStylesheet = stylesheet =>
Array.from(stylesheet.cssRules).map(rule =>
// If the rule contains its own CSS rule list
rule.cssRules
// process this rule as a rule list
? readStylesheet(rule)
// Otherwise red the rule as a CSS rule
: readRule(rule)
)
// For any CSS rule or DOM node
const readRule = rule => {
// Check if any declared properties start with the supplied name
const props = Array.from(rule.style).filter(prop => prop.startsWith(name))
// If any matching properties are found
if (props.length) {
props.forEach(prop =>
// Set a new property ending with `-value`
rule.style.setProperty(
`${prop}-value`,
// That's equal to the initial value of the matching property
rule.style.getPropertyValue(prop)
)
)
}
// If there are matching properties
return props.length
// Return the CSS rule or DOM node
? rule
// Otherwise return nothing
: null
}
// Flatten infinitely nested arrays into a single list
const flattenArray = arr =>
arr.filter(item => item).reduce(
(acc, child) => acc.concat(
Array.isArray(child)
? flattenArray(child)
: child
),
[]
)
// Try to parse a string as JSON
const attemptJSON = string => {
try {
const values = JSON.parse(string)
if (values) {
return values
}
} catch (e) {
return ''
}
}
// Process CSS rule or DOM node
const processRule = (rules, e) =>
rules.forEach(rule =>
Array.from(rule.style)
// Find custom properties matching our name
.filter(prop => prop.startsWith(name) && prop.endsWith('-value') === false)
.forEach(prop => {
rule.style.setProperty(
// Set the CSS variable
prop,
// To the output of the supplied function
func(
// Given the initial value parsed as JSON
attemptJSON(rule.style.getPropertyValue(`${prop}-value`)),
// The event object for the event recomputing the variable
e,
// and a reference to the original CSS rule or DOM node
rule
)
)
})
)
const registerEvent = (target, event, rules) =>
target.addEventListener(
event,
e => processRule(rules, e)
)
// Rules to be processed
const rules = [
// Contain all matching CSS rules from CSSOM
...flattenArray(
Array.from(document.styleSheets).map(
stylesheet => readStylesheet(stylesheet)
)
),
// Plus all matching HTML elements from DOM
...flattenArray(
Array.from(document.querySelectorAll('*')).map(
tag => readRule(tag)
)
)
]
if (selector === window) {
return events.forEach(event =>
registerEvent(window, event, rules)
)
} else {
return document.querySelectorAll(selector).forEach(tag =>
events.forEach(event =>
registerEvent(tag, event, rules)
)
)
}
}