UNPKG

13 kBJavaScriptView Raw
1let vendor = require('./vendor')
2let Declaration = require('./declaration')
3let Resolution = require('./resolution')
4let Transition = require('./transition')
5let Processor = require('./processor')
6let Supports = require('./supports')
7let Browsers = require('./browsers')
8let Selector = require('./selector')
9let AtRule = require('./at-rule')
10let Value = require('./value')
11let utils = require('./utils')
12let hackFullscreen = require('./hacks/fullscreen')
13let hackPlaceholder = require('./hacks/placeholder')
14let hackPlaceholderShown = require('./hacks/placeholder-shown')
15let hackFileSelectorButton = require('./hacks/file-selector-button')
16let hackFlex = require('./hacks/flex')
17let hackOrder = require('./hacks/order')
18let hackFilter = require('./hacks/filter')
19let hackGridEnd = require('./hacks/grid-end')
20let hackAnimation = require('./hacks/animation')
21let hackFlexFlow = require('./hacks/flex-flow')
22let hackFlexGrow = require('./hacks/flex-grow')
23let hackFlexWrap = require('./hacks/flex-wrap')
24let hackGridArea = require('./hacks/grid-area')
25let hackPlaceSelf = require('./hacks/place-self')
26let hackGridStart = require('./hacks/grid-start')
27let hackAlignSelf = require('./hacks/align-self')
28let hackAppearance = require('./hacks/appearance')
29let hackFlexBasis = require('./hacks/flex-basis')
30let hackMaskBorder = require('./hacks/mask-border')
31let hackMaskComposite = require('./hacks/mask-composite')
32let hackAlignItems = require('./hacks/align-items')
33let hackUserSelect = require('./hacks/user-select')
34let hackFlexShrink = require('./hacks/flex-shrink')
35let hackBreakProps = require('./hacks/break-props')
36let hackColorAdjust = require('./hacks/color-adjust')
37let hackWritingMode = require('./hacks/writing-mode')
38let hackBorderImage = require('./hacks/border-image')
39let hackAlignContent = require('./hacks/align-content')
40let hackBorderRadius = require('./hacks/border-radius')
41let hackBlockLogical = require('./hacks/block-logical')
42let hackGridTemplate = require('./hacks/grid-template')
43let hackInlineLogical = require('./hacks/inline-logical')
44let hackGridRowAlign = require('./hacks/grid-row-align')
45let hackTransformDecl = require('./hacks/transform-decl')
46let hackFlexDirection = require('./hacks/flex-direction')
47let hackImageRendering = require('./hacks/image-rendering')
48let hackBackdropFilter = require('./hacks/backdrop-filter')
49let hackBackgroundClip = require('./hacks/background-clip')
50let hackTextDecoration = require('./hacks/text-decoration')
51let hackJustifyContent = require('./hacks/justify-content')
52let hackBackgroundSize = require('./hacks/background-size')
53let hackGridRowColumn = require('./hacks/grid-row-column')
54let hackGridRowsColumns = require('./hacks/grid-rows-columns')
55let hackGridColumnAlign = require('./hacks/grid-column-align')
56let hackOverscrollBehavior = require('./hacks/overscroll-behavior')
57let hackGridTemplateAreas = require('./hacks/grid-template-areas')
58let hackTextEmphasisPosition = require('./hacks/text-emphasis-position')
59let hackTextDecorationSkipInk = require('./hacks/text-decoration-skip-ink')
60let hackGradient = require('./hacks/gradient')
61let hackIntrinsic = require('./hacks/intrinsic')
62let hackPixelated = require('./hacks/pixelated')
63let hackImageSet = require('./hacks/image-set')
64let hackCrossFade = require('./hacks/cross-fade')
65let hackDisplayFlex = require('./hacks/display-flex')
66let hackDisplayGrid = require('./hacks/display-grid')
67let hackFilterValue = require('./hacks/filter-value')
68
69Selector.hack(hackFullscreen)
70Selector.hack(hackPlaceholder)
71Selector.hack(hackPlaceholderShown)
72Selector.hack(hackFileSelectorButton)
73Declaration.hack(hackFlex)
74Declaration.hack(hackOrder)
75Declaration.hack(hackFilter)
76Declaration.hack(hackGridEnd)
77Declaration.hack(hackAnimation)
78Declaration.hack(hackFlexFlow)
79Declaration.hack(hackFlexGrow)
80Declaration.hack(hackFlexWrap)
81Declaration.hack(hackGridArea)
82Declaration.hack(hackPlaceSelf)
83Declaration.hack(hackGridStart)
84Declaration.hack(hackAlignSelf)
85Declaration.hack(hackAppearance)
86Declaration.hack(hackFlexBasis)
87Declaration.hack(hackMaskBorder)
88Declaration.hack(hackMaskComposite)
89Declaration.hack(hackAlignItems)
90Declaration.hack(hackUserSelect)
91Declaration.hack(hackFlexShrink)
92Declaration.hack(hackBreakProps)
93Declaration.hack(hackColorAdjust)
94Declaration.hack(hackWritingMode)
95Declaration.hack(hackBorderImage)
96Declaration.hack(hackAlignContent)
97Declaration.hack(hackBorderRadius)
98Declaration.hack(hackBlockLogical)
99Declaration.hack(hackGridTemplate)
100Declaration.hack(hackInlineLogical)
101Declaration.hack(hackGridRowAlign)
102Declaration.hack(hackTransformDecl)
103Declaration.hack(hackFlexDirection)
104Declaration.hack(hackImageRendering)
105Declaration.hack(hackBackdropFilter)
106Declaration.hack(hackBackgroundClip)
107Declaration.hack(hackTextDecoration)
108Declaration.hack(hackJustifyContent)
109Declaration.hack(hackBackgroundSize)
110Declaration.hack(hackGridRowColumn)
111Declaration.hack(hackGridRowsColumns)
112Declaration.hack(hackGridColumnAlign)
113Declaration.hack(hackOverscrollBehavior)
114Declaration.hack(hackGridTemplateAreas)
115Declaration.hack(hackTextEmphasisPosition)
116Declaration.hack(hackTextDecorationSkipInk)
117Value.hack(hackGradient)
118Value.hack(hackIntrinsic)
119Value.hack(hackPixelated)
120Value.hack(hackImageSet)
121Value.hack(hackCrossFade)
122Value.hack(hackDisplayFlex)
123Value.hack(hackDisplayGrid)
124Value.hack(hackFilterValue)
125
126let declsCache = new Map()
127
128class Prefixes {
129 constructor(data, browsers, options = {}) {
130 this.data = data
131 this.browsers = browsers
132 this.options = options
133 ;[this.add, this.remove] = this.preprocess(this.select(this.data))
134 this.transition = new Transition(this)
135 this.processor = new Processor(this)
136 }
137
138 /**
139 * Return clone instance to remove all prefixes
140 */
141 cleaner() {
142 if (this.cleanerCache) {
143 return this.cleanerCache
144 }
145
146 if (this.browsers.selected.length) {
147 let empty = new Browsers(this.browsers.data, [])
148 this.cleanerCache = new Prefixes(this.data, empty, this.options)
149 } else {
150 return this
151 }
152
153 return this.cleanerCache
154 }
155
156 /**
157 * Select prefixes from data, which is necessary for selected browsers
158 */
159 select(list) {
160 let selected = { add: {}, remove: {} }
161
162 for (let name in list) {
163 let data = list[name]
164 let add = data.browsers.map(i => {
165 let params = i.split(' ')
166 return {
167 browser: `${params[0]} ${params[1]}`,
168 note: params[2]
169 }
170 })
171
172 let notes = add
173 .filter(i => i.note)
174 .map(i => `${this.browsers.prefix(i.browser)} ${i.note}`)
175 notes = utils.uniq(notes)
176
177 add = add
178 .filter(i => this.browsers.isSelected(i.browser))
179 .map(i => {
180 let prefix = this.browsers.prefix(i.browser)
181 if (i.note) {
182 return `${prefix} ${i.note}`
183 } else {
184 return prefix
185 }
186 })
187 add = this.sort(utils.uniq(add))
188
189 if (this.options.flexbox === 'no-2009') {
190 add = add.filter(i => !i.includes('2009'))
191 }
192
193 let all = data.browsers.map(i => this.browsers.prefix(i))
194 if (data.mistakes) {
195 all = all.concat(data.mistakes)
196 }
197 all = all.concat(notes)
198 all = utils.uniq(all)
199
200 if (add.length) {
201 selected.add[name] = add
202 if (add.length < all.length) {
203 selected.remove[name] = all.filter(i => !add.includes(i))
204 }
205 } else {
206 selected.remove[name] = all
207 }
208 }
209
210 return selected
211 }
212
213 /**
214 * Sort vendor prefixes
215 */
216 sort(prefixes) {
217 return prefixes.sort((a, b) => {
218 let aLength = utils.removeNote(a).length
219 let bLength = utils.removeNote(b).length
220
221 if (aLength === bLength) {
222 return b.length - a.length
223 } else {
224 return bLength - aLength
225 }
226 })
227 }
228
229 /**
230 * Cache prefixes data to fast CSS processing
231 */
232 preprocess(selected) {
233 let add = {
234 'selectors': [],
235 '@supports': new Supports(Prefixes, this)
236 }
237 for (let name in selected.add) {
238 let prefixes = selected.add[name]
239 if (name === '@keyframes' || name === '@viewport') {
240 add[name] = new AtRule(name, prefixes, this)
241 } else if (name === '@resolution') {
242 add[name] = new Resolution(name, prefixes, this)
243 } else if (this.data[name].selector) {
244 add.selectors.push(Selector.load(name, prefixes, this))
245 } else {
246 let props = this.data[name].props
247
248 if (props) {
249 let value = Value.load(name, prefixes, this)
250 for (let prop of props) {
251 if (!add[prop]) {
252 add[prop] = { values: [] }
253 }
254 add[prop].values.push(value)
255 }
256 } else {
257 let values = (add[name] && add[name].values) || []
258 add[name] = Declaration.load(name, prefixes, this)
259 add[name].values = values
260 }
261 }
262 }
263
264 let remove = { selectors: [] }
265 for (let name in selected.remove) {
266 let prefixes = selected.remove[name]
267 if (this.data[name].selector) {
268 let selector = Selector.load(name, prefixes)
269 for (let prefix of prefixes) {
270 remove.selectors.push(selector.old(prefix))
271 }
272 } else if (name === '@keyframes' || name === '@viewport') {
273 for (let prefix of prefixes) {
274 let prefixed = `@${prefix}${name.slice(1)}`
275 remove[prefixed] = { remove: true }
276 }
277 } else if (name === '@resolution') {
278 remove[name] = new Resolution(name, prefixes, this)
279 } else {
280 let props = this.data[name].props
281 if (props) {
282 let value = Value.load(name, [], this)
283 for (let prefix of prefixes) {
284 let old = value.old(prefix)
285 if (old) {
286 for (let prop of props) {
287 if (!remove[prop]) {
288 remove[prop] = {}
289 }
290 if (!remove[prop].values) {
291 remove[prop].values = []
292 }
293 remove[prop].values.push(old)
294 }
295 }
296 }
297 } else {
298 for (let p of prefixes) {
299 let olds = this.decl(name).old(name, p)
300 if (name === 'align-self') {
301 let a = add[name] && add[name].prefixes
302 if (a) {
303 if (p === '-webkit- 2009' && a.includes('-webkit-')) {
304 continue
305 } else if (p === '-webkit-' && a.includes('-webkit- 2009')) {
306 continue
307 }
308 }
309 }
310 for (let prefixed of olds) {
311 if (!remove[prefixed]) {
312 remove[prefixed] = {}
313 }
314 remove[prefixed].remove = true
315 }
316 }
317 }
318 }
319 }
320
321 return [add, remove]
322 }
323
324 /**
325 * Declaration loader with caching
326 */
327 decl(prop) {
328 if (!declsCache.has(prop)) {
329 declsCache.set(prop, Declaration.load(prop))
330 }
331
332 return declsCache.get(prop)
333 }
334
335 /**
336 * Return unprefixed version of property
337 */
338 unprefixed(prop) {
339 let value = this.normalize(vendor.unprefixed(prop))
340 if (value === 'flex-direction') {
341 value = 'flex-flow'
342 }
343 return value
344 }
345
346 /**
347 * Normalize prefix for remover
348 */
349 normalize(prop) {
350 return this.decl(prop).normalize(prop)
351 }
352
353 /**
354 * Return prefixed version of property
355 */
356 prefixed(prop, prefix) {
357 prop = vendor.unprefixed(prop)
358 return this.decl(prop).prefixed(prop, prefix)
359 }
360
361 /**
362 * Return values, which must be prefixed in selected property
363 */
364 values(type, prop) {
365 let data = this[type]
366
367 let global = data['*'] && data['*'].values
368 let values = data[prop] && data[prop].values
369
370 if (global && values) {
371 return utils.uniq(global.concat(values))
372 } else {
373 return global || values || []
374 }
375 }
376
377 /**
378 * Group declaration by unprefixed property to check them
379 */
380 group(decl) {
381 let rule = decl.parent
382 let index = rule.index(decl)
383 let { length } = rule.nodes
384 let unprefixed = this.unprefixed(decl.prop)
385
386 let checker = (step, callback) => {
387 index += step
388 while (index >= 0 && index < length) {
389 let other = rule.nodes[index]
390 if (other.type === 'decl') {
391 if (step === -1 && other.prop === unprefixed) {
392 if (!Browsers.withPrefix(other.value)) {
393 break
394 }
395 }
396
397 if (this.unprefixed(other.prop) !== unprefixed) {
398 break
399 } else if (callback(other) === true) {
400 return true
401 }
402
403 if (step === +1 && other.prop === unprefixed) {
404 if (!Browsers.withPrefix(other.value)) {
405 break
406 }
407 }
408 }
409
410 index += step
411 }
412 return false
413 }
414
415 return {
416 up(callback) {
417 return checker(-1, callback)
418 },
419 down(callback) {
420 return checker(+1, callback)
421 }
422 }
423 }
424}
425
426module.exports = Prefixes