150 lines
3.0 KiB
JavaScript
150 lines
3.0 KiB
JavaScript
const selectorReg = /^([^#\.\[]+)?(?:#([^\.\[]+))?(?:\.((?:[^\[\]\\]|\\.)+))?((?:\[[^\]]*\])+)?$/; // eslint-disable-line
|
|
const attributeReg = /^([a-zA-Z0-9_-]*[^~|^$*!=])(?:([~|^$*!]?)=['"]?([^'"]*)['"]?)?$/;
|
|
const splitReg = /\s*,\s*/;
|
|
|
|
function expandMatcher(matcher) {
|
|
if (typeof matcher === "string") {
|
|
const match = matcher.match(selectorReg);
|
|
|
|
if (match) {
|
|
matcher = {};
|
|
const tag = match[1];
|
|
const id = match[2];
|
|
const className = match[3];
|
|
const attrs = match[4];
|
|
let attributes;
|
|
|
|
if (tag) {
|
|
matcher.tag = tag;
|
|
}
|
|
|
|
if (attrs) {
|
|
attributes = expandAttributes(attrs);
|
|
} else if (id || className) {
|
|
attributes = {};
|
|
}
|
|
|
|
if (id) {
|
|
attributes.id = id;
|
|
}
|
|
|
|
if (className) {
|
|
attributes.class = new RegExp(getCombinations(className.split(".")).map((order) => {
|
|
return "(?:^|\\s)" + order.join("\\s(?:.*?\\s)?") + "(?:\\s|$)";
|
|
}).join("|"));
|
|
}
|
|
|
|
if (attributes) {
|
|
matcher.attrs = attributes;
|
|
}
|
|
} else {
|
|
matcher = {tag: matcher};
|
|
}
|
|
}
|
|
|
|
return matcher;
|
|
}
|
|
|
|
function cssAttrToRegExp(value, operator) {
|
|
let reg;
|
|
|
|
switch (operator) {
|
|
case "~":
|
|
reg = "(?:^|\\s)" + value + "(?:\\s|$)";
|
|
break;
|
|
|
|
case "|":
|
|
reg = "^" + value + "(?:-|$)";
|
|
break;
|
|
|
|
case "^":
|
|
reg = "^" + value;
|
|
break;
|
|
|
|
case "$":
|
|
reg = value + "$";
|
|
break;
|
|
|
|
case "*":
|
|
reg = value;
|
|
break;
|
|
|
|
case "!":
|
|
reg = "^((?!" + value + ")[\\s\\S])*$";
|
|
break;
|
|
|
|
default:
|
|
reg = "^" + value + "$";
|
|
break;
|
|
}
|
|
|
|
return new RegExp(reg);
|
|
}
|
|
|
|
function expandAttributes(attrs) {
|
|
attrs = attrs.slice(1, -1);
|
|
|
|
if (attrs.length > 0) {
|
|
attrs = attrs.split("][");
|
|
const attrObject = {};
|
|
let l = attrs.length;
|
|
let attrMatch;
|
|
let name;
|
|
let operator;
|
|
let value;
|
|
|
|
while (l--) {
|
|
attrMatch = attrs[l].match(attributeReg);
|
|
|
|
if (attrMatch) {
|
|
name = attrMatch[1];
|
|
operator = attrMatch[2];
|
|
value = attrMatch[3];
|
|
|
|
if (!value) {
|
|
value = true;
|
|
}
|
|
|
|
attrObject[name] = (operator) ? cssAttrToRegExp(value, operator) : value;
|
|
}
|
|
}
|
|
|
|
return attrObject;
|
|
}
|
|
}
|
|
|
|
function getCombinations(values, subresult) {
|
|
subresult = subresult || [];
|
|
let result = [];
|
|
|
|
for (const value of values) {
|
|
if (subresult.indexOf(value) < 0) {
|
|
const _subresult = subresult.concat([value]);
|
|
|
|
if (_subresult.length < values.length) {
|
|
result = result.concat(getCombinations(values, _subresult));
|
|
} else {
|
|
result.push(_subresult);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
export default function (matcher) {
|
|
if (typeof matcher === "string") {
|
|
if (matcher.match(splitReg)) {
|
|
matcher = matcher.split(splitReg);
|
|
} else {
|
|
return expandMatcher(matcher);
|
|
}
|
|
}
|
|
|
|
if (Array.isArray(matcher)) {
|
|
return matcher.map(expandMatcher);
|
|
}
|
|
|
|
return matcher;
|
|
}
|