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 |