You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
175 lines
3.7 KiB
175 lines
3.7 KiB
const blank = { |
|
' ': true, |
|
'\n': true, |
|
'\t': true, |
|
'\r': true, |
|
'\f': true |
|
} |
|
|
|
function Parser () { |
|
this.styles = [] |
|
this.selectors = [] |
|
} |
|
|
|
/** |
|
* @description 解析 css 字符串 |
|
* @param {string} content css 内容 |
|
*/ |
|
Parser.prototype.parse = function (content) { |
|
new Lexer(this).parse(content) |
|
return this.styles |
|
} |
|
|
|
/** |
|
* @description 解析到一个选择器 |
|
* @param {string} name 名称 |
|
*/ |
|
Parser.prototype.onSelector = function (name) { |
|
// 不支持的选择器 |
|
if (name.includes('[') || name.includes('*') || name.includes('@')) return |
|
const selector = {} |
|
// 伪类 |
|
if (name.includes(':')) { |
|
const info = name.split(':') |
|
const pseudo = info.pop() |
|
if (pseudo === 'before' || pseudo === 'after') { |
|
selector.pseudo = pseudo |
|
name = info[0] |
|
} else return |
|
} |
|
|
|
// 分割交集选择器 |
|
function splitItem (str) { |
|
const arr = [] |
|
let i, start |
|
for (i = 1, start = 0; i < str.length; i++) { |
|
if (str[i] === '.' || str[i] === '#') { |
|
arr.push(str.substring(start, i)) |
|
start = i |
|
} |
|
} |
|
if (!arr.length) { |
|
return str |
|
} else { |
|
arr.push(str.substring(start, i)) |
|
return arr |
|
} |
|
} |
|
|
|
// 后代选择器 |
|
if (name.includes(' ')) { |
|
selector.list = [] |
|
const list = name.split(' ') |
|
for (let i = 0; i < list.length; i++) { |
|
if (list[i].length) { |
|
// 拆分子选择器 |
|
const arr = list[i].split('>') |
|
for (let j = 0; j < arr.length; j++) { |
|
selector.list.push(splitItem(arr[j])) |
|
if (j < arr.length - 1) { |
|
selector.list.push('>') |
|
} |
|
} |
|
} |
|
} |
|
} else { |
|
selector.key = splitItem(name) |
|
} |
|
|
|
this.selectors.push(selector) |
|
} |
|
|
|
/** |
|
* @description 解析到选择器内容 |
|
* @param {string} content 内容 |
|
*/ |
|
Parser.prototype.onContent = function (content) { |
|
// 并集选择器 |
|
for (let i = 0; i < this.selectors.length; i++) { |
|
this.selectors[i].style = content |
|
} |
|
this.styles = this.styles.concat(this.selectors) |
|
this.selectors = [] |
|
} |
|
|
|
/** |
|
* @description css 词法分析器 |
|
* @param {object} handler 高层处理器 |
|
*/ |
|
function Lexer (handler) { |
|
this.selector = '' |
|
this.style = '' |
|
this.handler = handler |
|
} |
|
|
|
Lexer.prototype.parse = function (content) { |
|
this.i = 0 |
|
this.content = content |
|
this.state = this.blank |
|
for (let len = content.length; this.i < len; this.i++) { |
|
this.state(content[this.i]) |
|
} |
|
} |
|
|
|
Lexer.prototype.comment = function () { |
|
this.i = this.content.indexOf('*/', this.i) + 1 |
|
if (!this.i) { |
|
this.i = this.content.length |
|
} |
|
} |
|
|
|
Lexer.prototype.blank = function (c) { |
|
if (!blank[c]) { |
|
if (c === '/' && this.content[this.i + 1] === '*') { |
|
this.comment() |
|
return |
|
} |
|
this.selector += c |
|
this.state = this.name |
|
} |
|
} |
|
|
|
Lexer.prototype.name = function (c) { |
|
if (c === '/' && this.content[this.i + 1] === '*') { |
|
this.comment() |
|
return |
|
} |
|
if (c === '{' || c === ',' || c === ';') { |
|
this.handler.onSelector(this.selector.trimEnd()) |
|
this.selector = '' |
|
if (c !== '{') { |
|
while (blank[this.content[++this.i]]); |
|
} |
|
if (this.content[this.i] === '{') { |
|
this.floor = 1 |
|
this.state = this.val |
|
} else { |
|
this.selector += this.content[this.i] |
|
} |
|
} else if (blank[c]) { |
|
this.selector += ' ' |
|
} else { |
|
this.selector += c |
|
} |
|
} |
|
|
|
Lexer.prototype.val = function (c) { |
|
if (c === '/' && this.content[this.i + 1] === '*') { |
|
this.comment() |
|
return |
|
} |
|
if (c === '{') { |
|
this.floor++ |
|
} else if (c === '}') { |
|
this.floor-- |
|
if (!this.floor) { |
|
this.handler.onContent(this.style) |
|
this.style = '' |
|
this.state = this.blank |
|
return |
|
} |
|
} |
|
this.style += c |
|
} |
|
|
|
export default Parser
|
|
|