| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 | let { list } = require('postcss')let OldSelector = require('./old-selector')let Prefixer = require('./prefixer')let Browsers = require('./browsers')let utils = require('./utils')class Selector extends Prefixer {  constructor(name, prefixes, all) {    super(name, prefixes, all)    this.regexpCache = new Map()  }  /**   * Is rule selectors need to be prefixed   */  check(rule) {    if (rule.selector.includes(this.name)) {      return !!rule.selector.match(this.regexp())    }    return false  }  /**   * Return prefixed version of selector   */  prefixed(prefix) {    return this.name.replace(/^(\W*)/, `$1${prefix}`)  }  /**   * 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)  }  /**   * All possible prefixes   */  possible() {    return Browsers.prefixes()  }  /**   * 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  }  /**   * 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  }  /**   * Replace selectors by prefixed one   */  replace(selector, prefix) {    return selector.replace(this.regexp(), `$1${this.prefixed(prefix)}`)  }  /**   * 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)  }  /**   * Return function to fast find prefixed selector   */  old(prefix) {    return new OldSelector(this, prefix)  }}module.exports = Selector
 |