151 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			151 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| let { list } = require('postcss')
 | |
| 
 | |
| let Browsers = require('./browsers')
 | |
| let OldSelector = require('./old-selector')
 | |
| let Prefixer = require('./prefixer')
 | |
| let utils = require('./utils')
 | |
| 
 | |
| class Selector extends Prefixer {
 | |
|   constructor(name, prefixes, all) {
 | |
|     super(name, prefixes, all)
 | |
|     this.regexpCache = new Map()
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Clone and add prefixes for at-rule
 | |
|    */
 | |
|   add(rule, prefix) {
 | |
|     let prefixeds = this.prefixeds(rule)
 | |
| 
 | |
|     if (this.already(rule, prefixeds, prefix)) {
 | |
|       return
 | |
|     }
 | |
| 
 | |
|     let cloned = this.clone(rule, { selector: prefixeds[this.name][prefix] })
 | |
|     rule.parent.insertBefore(rule, cloned)
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Is rule already prefixed before
 | |
|    */
 | |
|   already(rule, prefixeds, prefix) {
 | |
|     let index = rule.parent.index(rule) - 1
 | |
| 
 | |
|     while (index >= 0) {
 | |
|       let before = rule.parent.nodes[index]
 | |
| 
 | |
|       if (before.type !== 'rule') {
 | |
|         return false
 | |
|       }
 | |
| 
 | |
|       let some = false
 | |
|       for (let key in prefixeds[this.name]) {
 | |
|         let prefixed = prefixeds[this.name][key]
 | |
|         if (before.selector === prefixed) {
 | |
|           if (prefix === key) {
 | |
|             return true
 | |
|           } else {
 | |
|             some = true
 | |
|             break
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|       if (!some) {
 | |
|         return false
 | |
|       }
 | |
| 
 | |
|       index -= 1
 | |
|     }
 | |
| 
 | |
|     return false
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Is rule selectors need to be prefixed
 | |
|    */
 | |
|   check(rule) {
 | |
|     if (rule.selector.includes(this.name)) {
 | |
|       return !!rule.selector.match(this.regexp())
 | |
|     }
 | |
| 
 | |
|     return false
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Return function to fast find prefixed selector
 | |
|    */
 | |
|   old(prefix) {
 | |
|     return new OldSelector(this, prefix)
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * All possible prefixes
 | |
|    */
 | |
|   possible() {
 | |
|     return Browsers.prefixes()
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Return prefixed version of selector
 | |
|    */
 | |
|   prefixed(prefix) {
 | |
|     return this.name.replace(/^(\W*)/, `$1${prefix}`)
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Return all possible selector prefixes
 | |
|    */
 | |
|   prefixeds(rule) {
 | |
|     if (rule._autoprefixerPrefixeds) {
 | |
|       if (rule._autoprefixerPrefixeds[this.name]) {
 | |
|         return rule._autoprefixerPrefixeds
 | |
|       }
 | |
|     } else {
 | |
|       rule._autoprefixerPrefixeds = {}
 | |
|     }
 | |
| 
 | |
|     let prefixeds = {}
 | |
|     if (rule.selector.includes(',')) {
 | |
|       let ruleParts = list.comma(rule.selector)
 | |
|       let toProcess = ruleParts.filter(el => el.includes(this.name))
 | |
| 
 | |
|       for (let prefix of this.possible()) {
 | |
|         prefixeds[prefix] = toProcess
 | |
|           .map(el => this.replace(el, prefix))
 | |
|           .join(', ')
 | |
|       }
 | |
|     } else {
 | |
|       for (let prefix of this.possible()) {
 | |
|         prefixeds[prefix] = this.replace(rule.selector, prefix)
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     rule._autoprefixerPrefixeds[this.name] = prefixeds
 | |
|     return rule._autoprefixerPrefixeds
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Lazy loadRegExp for name
 | |
|    */
 | |
|   regexp(prefix) {
 | |
|     if (!this.regexpCache.has(prefix)) {
 | |
|       let name = prefix ? this.prefixed(prefix) : this.name
 | |
|       this.regexpCache.set(
 | |
|         prefix,
 | |
|         new RegExp(`(^|[^:"'=])${utils.escapeRegexp(name)}`, 'gi')
 | |
|       )
 | |
|     }
 | |
| 
 | |
|     return this.regexpCache.get(prefix)
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Replace selectors by prefixed one
 | |
|    */
 | |
|   replace(selector, prefix) {
 | |
|     return selector.replace(this.regexp(), `$1${this.prefixed(prefix)}`)
 | |
|   }
 | |
| }
 | |
| 
 | |
| module.exports = Selector
 |