-
Notifications
You must be signed in to change notification settings - Fork 14
/
utils.js
202 lines (160 loc) · 5.1 KB
/
utils.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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
import popular from './popular'
const shorts = Object.create(null)
export const cssProperties = ['float'].concat(Object.keys(
findWidth(document.documentElement.style)
).filter(p => p.indexOf('-') === -1 && p !== 'length'))
function findWidth(obj) {
return obj.hasOwnProperty('width')
? obj
: findWidth(Object.getPrototypeOf(obj))
}
const memoize = (fn, cache = {}) => item =>
item in cache
? cache[item]
: cache[item] = fn(item)
const stringToObject = memoize(string => {
let last = ''
, prev
return string.trim().replace(/;/g, '\n').split('\n').reduce((acc, line) => {
line = last + line.trim()
last = line.slice(-1) === ',' ? line : ''
if (last)
return acc
if (line.charAt(0) === ',') {
acc[prev] += line
return acc
}
const tokens = line.split(/[:\s]/)
if (tokens.length > 1) {
const key = hyphenToCamelCase(tokens.shift().trim())
prev = shorts[key] || key
add(acc, prev, tokens.filter(a => a).reduce((acc, t) => acc + addPx(prev, t.trim()) + ' ', '').trim())
}
return acc
}, {})
})
export function add(style, prop, value) {
if (!(prop in style))
return style[prop] = value
if (!style._fallback)
Object.defineProperty(style, '_fallback', { configurable: true, value: Object.create(null, {}) })
add(style._fallback, prop, value)
}
export const vendorMap = Object.create(null, {})
export const vendorValuePrefix = Object.create(null, {})
export const vendorRegex = /^(o|O|ms|MS|Ms|moz|Moz|webkit|Webkit|WebKit)([A-Z])/
export function parse(input, value) {
if (typeof input === 'string') {
if (typeof value === 'string' || typeof value === 'number')
return ({ [input] : value })
return stringToObject(input)
} else if (Array.isArray(input) && Array.isArray(input.raw)) {
arguments[0] = { raw: input }
return stringToObject(String.raw.apply(null, arguments))
}
return input.style || sanitize(input)
}
const appendPx = memoize(prop => {
const el = document.createElement('div')
try {
el.style[prop] = '1px'
el.style.setProperty(prop, '1px')
return el.style[prop].slice(-3) === '1px' ? 'px' : ''
} catch (err) {
return ''
}
}, {
flex: ''
})
export function lowercaseFirst(string) {
return string.charAt(0).toLowerCase() + string.slice(1)
}
function sanitize(styles) {
return Object.keys(styles).reduce((acc, key) => {
const value = styles[key]
if (!value && value !== 0 && value !== '')
return acc
if (key === 'content' && value.charAt(0) !== '"')
acc[key] = '"' + value + '"'
else
acc[key in vendorMap ? vendorMap[key] : key] = formatValue(key, value)
return acc
}, {})
}
export function assign(obj, obj2) {
if (obj2._fallback) {
obj._fallback
? assign(obj._fallback, obj2._fallback)
: Object.defineProperty(obj, '_fallback', { value: obj2._fallback })
}
for (const key in obj2) {
if (obj2.hasOwnProperty(key))
obj[key] = obj2[key]
}
}
export function hyphenToCamelCase(hyphen) {
return hyphen.slice(hyphen.charAt(0) === '-' ? 1 : 0).replace(/-([a-z])/g, function(match) {
return match[1].toUpperCase()
})
}
export function camelCaseToHyphen(camelCase) {
return camelCase.replace(/([A-Z])/g, '-$1').toLowerCase()
}
export function initials(camelCase) {
return camelCase.charAt(0) + (camelCase.match(/([A-Z])/g) || []).join('').toLowerCase()
}
export function short(prop) {
const acronym = initials(prop)
, short = popular[acronym] && popular[acronym] !== prop ? prop : acronym
shorts[short] = prop
return short
}
export function getStyleSheetText(classes) {
return Object.keys(classes).sort().reduce((acc, key) =>
acc + key.replace(/CLASS_NAME/g, classes[key])
, '')
}
export function objectToRules(style, suffix = '') {
let hasBase = false
, rules = []
Object.keys(style).forEach(prop => {
if (prop.charAt(0) === '@') {
rules.push(prop + '{' + objectToRules(style[prop]) + '}')
delete style[prop]
} else if (typeof style[prop] === 'object') {
rules = rules.concat(objectToRules(style[prop], suffix + prop))
delete style[prop]
} else {
hasBase = true
}
})
if (hasBase)
rules.unshift(selectorBlock((suffix.charAt(0) === ' ' ? '' : ':root ') + '.$' + suffix, style))
return rules
}
export function selectorBlock(selector, style) {
return selector + '{'
+ stylesToCss((typeof style === 'string' ? stringToObject(style) : style))
+ '}'
}
export const selectorSplit = /,(?=(?:(?:[^"]*"){2})*[^"]*$)/
export function stylesToCss(style) {
return Object.keys(style).reduce((acc, prop) =>
acc + propToString(prop, style[prop])
, '') + (style._fallback ? stylesToCss(style._fallback) : '')
}
export function readClasses(sheet) {
throw new Error('not implemented')
}
function propToString(prop, value) {
return (vendorRegex.test(prop) ? '-' : '')
+ camelCaseToHyphen(prop) + ':' + value + ';'
}
export function formatValue(key, value) {
return value in vendorValuePrefix
? vendorValuePrefix[value]
: addPx(key, value)
}
export function addPx(key, value) {
return value + (isNaN(value) ? '' : appendPx(key))
}