130 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			130 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
/**
 | 
						||
 * @fileoverview style 插件
 | 
						||
 */
 | 
						||
// #ifndef APP-PLUS-NVUE
 | 
						||
const Parser = require('./parser')
 | 
						||
// #endif
 | 
						||
 | 
						||
function Style () {
 | 
						||
  this.styles = []
 | 
						||
}
 | 
						||
 | 
						||
// #ifndef APP-PLUS-NVUE
 | 
						||
Style.prototype.onParse = function (node, vm) {
 | 
						||
  // 获取样式
 | 
						||
  if (node.name === 'style' && node.children.length && node.children[0].type === 'text') {
 | 
						||
    this.styles = this.styles.concat(new Parser().parse(node.children[0].text))
 | 
						||
  } else if (node.name) {
 | 
						||
    // 匹配样式(对非文本标签)
 | 
						||
    // 存储不同优先级的样式 name < class < id < 后代
 | 
						||
    let matched = ['', '', '', '']
 | 
						||
    for (let i = 0, len = this.styles.length; i < len; i++) {
 | 
						||
      const item = this.styles[i]
 | 
						||
      let res = match(node, item.key || item.list[item.list.length - 1])
 | 
						||
      let j
 | 
						||
      if (res) {
 | 
						||
        // 后代选择器
 | 
						||
        if (!item.key) {
 | 
						||
          j = item.list.length - 2
 | 
						||
          for (let k = vm.stack.length; j >= 0 && k--;) {
 | 
						||
            // 子选择器
 | 
						||
            if (item.list[j] === '>') {
 | 
						||
              // 错误情况
 | 
						||
              if (j < 1 || j > item.list.length - 2) break
 | 
						||
              if (match(vm.stack[k], item.list[j - 1])) {
 | 
						||
                j -= 2
 | 
						||
              } else {
 | 
						||
                j++
 | 
						||
              }
 | 
						||
            } else if (match(vm.stack[k], item.list[j])) {
 | 
						||
              j--
 | 
						||
            }
 | 
						||
          }
 | 
						||
          res = 4
 | 
						||
        }
 | 
						||
        if (item.key || j < 0) {
 | 
						||
          // 添加伪类
 | 
						||
          if (item.pseudo && node.children) {
 | 
						||
            let text
 | 
						||
            item.style = item.style.replace(/content:([^;]+)/, (_, $1) => {
 | 
						||
              text = $1.replace(/['"]/g, '')
 | 
						||
                // 处理 attr 函数
 | 
						||
                .replace(/attr\((.+?)\)/, (_, $1) => node.attrs[$1.trim()] || '')
 | 
						||
                // 编码 \xxx
 | 
						||
                .replace(/\\(\w{4})/, (_, $1) => String.fromCharCode(parseInt($1, 16)))
 | 
						||
              return ''
 | 
						||
            })
 | 
						||
            const pseudo = {
 | 
						||
              name: 'span',
 | 
						||
              attrs: {
 | 
						||
                style: item.style
 | 
						||
              },
 | 
						||
              children: [{
 | 
						||
                type: 'text',
 | 
						||
                text
 | 
						||
              }]
 | 
						||
            }
 | 
						||
            if (item.pseudo === 'before') {
 | 
						||
              node.children.unshift(pseudo)
 | 
						||
            } else {
 | 
						||
              node.children.push(pseudo)
 | 
						||
            }
 | 
						||
          } else {
 | 
						||
            matched[res - 1] += item.style + (item.style[item.style.length - 1] === ';' ? '' : ';')
 | 
						||
          }
 | 
						||
        }
 | 
						||
      }
 | 
						||
    }
 | 
						||
    matched = matched.join('')
 | 
						||
    if (matched.length > 2) {
 | 
						||
      node.attrs.style = matched + (node.attrs.style || '')
 | 
						||
    }
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * @description 匹配样式
 | 
						||
 * @param {object} node 要匹配的标签
 | 
						||
 * @param {string|string[]} keys 选择器
 | 
						||
 * @returns {number} 0:不匹配;1:name 匹配;2:class 匹配;3:id 匹配
 | 
						||
 */
 | 
						||
function match (node, keys) {
 | 
						||
  function matchItem (key) {
 | 
						||
    if (key[0] === '#') {
 | 
						||
      // 匹配 id
 | 
						||
      if (node.attrs.id && node.attrs.id.trim() === key.substr(1)) return 3
 | 
						||
    } else if (key[0] === '.') {
 | 
						||
      // 匹配 class
 | 
						||
      key = key.substr(1)
 | 
						||
      const selectors = (node.attrs.class || '').split(' ')
 | 
						||
      for (let i = 0; i < selectors.length; i++) {
 | 
						||
        if (selectors[i].trim() === key) return 2
 | 
						||
      }
 | 
						||
    } else if (node.name === key) {
 | 
						||
      // 匹配 name
 | 
						||
      return 1
 | 
						||
    }
 | 
						||
    return 0
 | 
						||
  }
 | 
						||
 | 
						||
  // 多选择器交集
 | 
						||
  if (keys instanceof Array) {
 | 
						||
    let res = 0
 | 
						||
    for (let j = 0; j < keys.length; j++) {
 | 
						||
      const tmp = matchItem(keys[j])
 | 
						||
      // 任意一个不匹配就失败
 | 
						||
      if (!tmp) return 0
 | 
						||
      // 优先级最大的一个作为最终优先级
 | 
						||
      if (tmp > res) {
 | 
						||
        res = tmp
 | 
						||
      }
 | 
						||
    }
 | 
						||
    return res
 | 
						||
  }
 | 
						||
 | 
						||
  return matchItem(keys)
 | 
						||
}
 | 
						||
// #endif
 | 
						||
 | 
						||
module.exports = Style
 |