let Prefixer = require('./prefixer') let Browsers = require('./browsers') let utils = require('./utils') class Declaration extends Prefixer { /** * Always true, because we already get prefixer by property name */ check(/* decl */) { return true } /** * Return prefixed version of property */ prefixed(prop, prefix) { return prefix + prop } /** * Return unprefixed version of property */ normalize(prop) { return prop } /** * Check `value`, that it contain other prefixes, rather than `prefix` */ otherPrefixes(value, prefix) { for (let other of Browsers.prefixes()) { if (other === prefix) { continue } if (value.includes(other)) { return true } } return false } /** * Set prefix to declaration */ set(decl, prefix) { decl.prop = this.prefixed(decl.prop, prefix) return decl } /** * Should we use visual cascade for prefixes */ needCascade(decl) { if (!decl._autoprefixerCascade) { decl._autoprefixerCascade = this.all.options.cascade !== false && decl.raw('before').includes('\n') } return decl._autoprefixerCascade } /** * Return maximum length of possible prefixed property */ maxPrefixed(prefixes, decl) { if (decl._autoprefixerMax) { return decl._autoprefixerMax } let max = 0 for (let prefix of prefixes) { prefix = utils.removeNote(prefix) if (prefix.length > max) { max = prefix.length } } decl._autoprefixerMax = max return decl._autoprefixerMax } /** * Calculate indentation to create visual cascade */ calcBefore(prefixes, decl, prefix = '') { let max = this.maxPrefixed(prefixes, decl) let diff = max - utils.removeNote(prefix).length let before = decl.raw('before') if (diff > 0) { before += Array(diff).fill(' ').join('') } return before } /** * Remove visual cascade */ restoreBefore(decl) { let lines = decl.raw('before').split('\n') let min = lines[lines.length - 1] this.all.group(decl).up(prefixed => { let array = prefixed.raw('before').split('\n') let last = array[array.length - 1] if (last.length < min.length) { min = last } }) lines[lines.length - 1] = min decl.raws.before = lines.join('\n') } /** * Clone and insert new declaration */ insert(decl, prefix, prefixes) { let cloned = this.set(this.clone(decl), prefix) if (!cloned) return undefined let already = decl.parent.some( i => i.prop === cloned.prop && i.value === cloned.value ) if (already) { return undefined } if (this.needCascade(decl)) { cloned.raws.before = this.calcBefore(prefixes, decl, prefix) } return decl.parent.insertBefore(decl, cloned) } /** * Did this declaration has this prefix above */ isAlready(decl, prefixed) { let already = this.all.group(decl).up(i => i.prop === prefixed) if (!already) { already = this.all.group(decl).down(i => i.prop === prefixed) } return already } /** * Clone and add prefixes for declaration */ add(decl, prefix, prefixes, result) { let prefixed = this.prefixed(decl.prop, prefix) if ( this.isAlready(decl, prefixed) || this.otherPrefixes(decl.value, prefix) ) { return undefined } return this.insert(decl, prefix, prefixes, result) } /** * Add spaces for visual cascade */ process(decl, result) { if (!this.needCascade(decl)) { super.process(decl, result) return } let prefixes = super.process(decl, result) if (!prefixes || !prefixes.length) { return } this.restoreBefore(decl) decl.raws.before = this.calcBefore(prefixes, decl) } /** * Return list of prefixed properties to clean old prefixes */ old(prop, prefix) { return [this.prefixed(prop, prefix)] } } module.exports = Declaration