source: node_modules/autoprefixer/lib/transition.js@ 2058e5c

Last change on this file since 2058e5c was 2058e5c, checked in by istevanoska <ilinastevanoska@…>, 6 months ago

Working / before login

  • Property mode set to 100644
File size: 8.0 KB
Line 
1let { list } = require('postcss')
2let parser = require('postcss-value-parser')
3
4let Browsers = require('./browsers')
5let vendor = require('./vendor')
6
7class Transition {
8 constructor(prefixes) {
9 this.props = ['transition', 'transition-property']
10 this.prefixes = prefixes
11 }
12
13 /**
14 * Process transition and add prefixes for all necessary properties
15 */
16 add(decl, result) {
17 let prefix, prop
18 let add = this.prefixes.add[decl.prop]
19 let vendorPrefixes = this.ruleVendorPrefixes(decl)
20 let declPrefixes = vendorPrefixes || (add && add.prefixes) || []
21
22 let params = this.parse(decl.value)
23 let names = params.map(i => this.findProp(i))
24 let added = []
25
26 if (names.some(i => i[0] === '-')) {
27 return
28 }
29
30 for (let param of params) {
31 prop = this.findProp(param)
32 if (prop[0] === '-') continue
33
34 let prefixer = this.prefixes.add[prop]
35 if (!prefixer || !prefixer.prefixes) continue
36
37 for (prefix of prefixer.prefixes) {
38 if (vendorPrefixes && !vendorPrefixes.some(p => prefix.includes(p))) {
39 continue
40 }
41
42 let prefixed = this.prefixes.prefixed(prop, prefix)
43 if (prefixed !== '-ms-transform' && !names.includes(prefixed)) {
44 if (!this.disabled(prop, prefix)) {
45 added.push(this.clone(prop, prefixed, param))
46 }
47 }
48 }
49 }
50
51 params = params.concat(added)
52 let value = this.stringify(params)
53
54 let webkitClean = this.stringify(
55 this.cleanFromUnprefixed(params, '-webkit-')
56 )
57 if (declPrefixes.includes('-webkit-')) {
58 this.cloneBefore(decl, `-webkit-${decl.prop}`, webkitClean)
59 }
60 this.cloneBefore(decl, decl.prop, webkitClean)
61 if (declPrefixes.includes('-o-')) {
62 let operaClean = this.stringify(this.cleanFromUnprefixed(params, '-o-'))
63 this.cloneBefore(decl, `-o-${decl.prop}`, operaClean)
64 }
65
66 for (prefix of declPrefixes) {
67 if (prefix !== '-webkit-' && prefix !== '-o-') {
68 let prefixValue = this.stringify(
69 this.cleanOtherPrefixes(params, prefix)
70 )
71 this.cloneBefore(decl, prefix + decl.prop, prefixValue)
72 }
73 }
74
75 if (value !== decl.value && !this.already(decl, decl.prop, value)) {
76 this.checkForWarning(result, decl)
77 decl.cloneBefore()
78 decl.value = value
79 }
80 }
81
82 /**
83 * Does we already have this declaration
84 */
85 already(decl, prop, value) {
86 return decl.parent.some(i => i.prop === prop && i.value === value)
87 }
88
89 /**
90 * Show transition-property warning
91 */
92 checkForWarning(result, decl) {
93 if (decl.prop !== 'transition-property') {
94 return
95 }
96
97 let isPrefixed = false
98 let hasAssociatedProp = false
99
100 decl.parent.each(i => {
101 if (i.type !== 'decl') {
102 return undefined
103 }
104 if (i.prop.indexOf('transition-') !== 0) {
105 return undefined
106 }
107 let values = list.comma(i.value)
108 // check if current Rule's transition-property comma separated value list needs prefixes
109 if (i.prop === 'transition-property') {
110 values.forEach(value => {
111 let lookup = this.prefixes.add[value]
112 if (lookup && lookup.prefixes && lookup.prefixes.length > 0) {
113 isPrefixed = true
114 }
115 })
116 return undefined
117 }
118 // check if another transition-* prop in current Rule has comma separated value list
119 hasAssociatedProp = hasAssociatedProp || values.length > 1
120 return false
121 })
122
123 if (isPrefixed && hasAssociatedProp) {
124 decl.warn(
125 result,
126 'Replace transition-property to transition, ' +
127 'because Autoprefixer could not support ' +
128 'any cases of transition-property ' +
129 'and other transition-*'
130 )
131 }
132 }
133
134 /**
135 * Remove all non-webkit prefixes and unprefixed params if we have prefixed
136 */
137 cleanFromUnprefixed(params, prefix) {
138 let remove = params
139 .map(i => this.findProp(i))
140 .filter(i => i.slice(0, prefix.length) === prefix)
141 .map(i => this.prefixes.unprefixed(i))
142
143 let result = []
144 for (let param of params) {
145 let prop = this.findProp(param)
146 let p = vendor.prefix(prop)
147 if (!remove.includes(prop) && (p === prefix || p === '')) {
148 result.push(param)
149 }
150 }
151 return result
152 }
153
154 cleanOtherPrefixes(params, prefix) {
155 return params.filter(param => {
156 let current = vendor.prefix(this.findProp(param))
157 return current === '' || current === prefix
158 })
159 }
160
161 /**
162 * Return new param array with different name
163 */
164 clone(origin, name, param) {
165 let result = []
166 let changed = false
167 for (let i of param) {
168 if (!changed && i.type === 'word' && i.value === origin) {
169 result.push({ type: 'word', value: name })
170 changed = true
171 } else {
172 result.push(i)
173 }
174 }
175 return result
176 }
177
178 /**
179 * Add declaration if it is not exist
180 */
181 cloneBefore(decl, prop, value) {
182 if (!this.already(decl, prop, value)) {
183 decl.cloneBefore({ prop, value })
184 }
185 }
186
187 /**
188 * Check property for disabled by option
189 */
190 disabled(prop, prefix) {
191 let other = ['order', 'justify-content', 'align-self', 'align-content']
192 if (prop.includes('flex') || other.includes(prop)) {
193 if (this.prefixes.options.flexbox === false) {
194 return true
195 }
196
197 if (this.prefixes.options.flexbox === 'no-2009') {
198 return prefix.includes('2009')
199 }
200 }
201 return undefined
202 }
203
204 /**
205 * Find or create separator
206 */
207 div(params) {
208 for (let param of params) {
209 for (let node of param) {
210 if (node.type === 'div' && node.value === ',') {
211 return node
212 }
213 }
214 }
215 return { after: ' ', type: 'div', value: ',' }
216 }
217
218 /**
219 * Find property name
220 */
221 findProp(param) {
222 let prop = param[0].value
223 if (/^\d/.test(prop)) {
224 for (let [i, token] of param.entries()) {
225 if (i !== 0 && token.type === 'word') {
226 return token.value
227 }
228 }
229 }
230 return prop
231 }
232
233 /**
234 * Parse properties list to array
235 */
236 parse(value) {
237 let ast = parser(value)
238 let result = []
239 let param = []
240 for (let node of ast.nodes) {
241 param.push(node)
242 if (node.type === 'div' && node.value === ',') {
243 result.push(param)
244 param = []
245 }
246 }
247 result.push(param)
248 return result.filter(i => i.length > 0)
249 }
250
251 /**
252 * Process transition and remove all unnecessary properties
253 */
254 remove(decl) {
255 let params = this.parse(decl.value)
256 params = params.filter(i => {
257 let prop = this.prefixes.remove[this.findProp(i)]
258 return !prop || !prop.remove
259 })
260 let value = this.stringify(params)
261
262 if (decl.value === value) {
263 return
264 }
265
266 if (params.length === 0) {
267 decl.remove()
268 return
269 }
270
271 let double = decl.parent.some(i => {
272 return i.prop === decl.prop && i.value === value
273 })
274 let smaller = decl.parent.some(i => {
275 return i !== decl && i.prop === decl.prop && i.value.length > value.length
276 })
277
278 if (double || smaller) {
279 decl.remove()
280 return
281 }
282
283 decl.value = value
284 }
285
286 /**
287 * Check if transition prop is inside vendor specific rule
288 */
289 ruleVendorPrefixes(decl) {
290 let { parent } = decl
291
292 if (parent.type !== 'rule') {
293 return false
294 } else if (!parent.selector.includes(':-')) {
295 return false
296 }
297
298 let selectors = Browsers.prefixes().filter(s =>
299 parent.selector.includes(':' + s)
300 )
301
302 return selectors.length > 0 ? selectors : false
303 }
304
305 /**
306 * Return properties string from array
307 */
308 stringify(params) {
309 if (params.length === 0) {
310 return ''
311 }
312 let nodes = []
313 for (let param of params) {
314 if (param[param.length - 1].type !== 'div') {
315 param.push(this.div(params))
316 }
317 nodes = nodes.concat(param)
318 }
319 if (nodes[0].type === 'div') {
320 nodes = nodes.slice(1)
321 }
322 if (nodes[nodes.length - 1].type === 'div') {
323 nodes = nodes.slice(0, +-2 + 1 || undefined)
324 }
325 return parser.stringify({ nodes })
326 }
327}
328
329module.exports = Transition
Note: See TracBrowser for help on using the repository browser.