blob: 946cfee872d3bec2a6bfb2bbc2c813190b9ddbbb [file] [log] [blame]
module.exports = sortByProcedure;
/*
sort the parts of the passed selector,
as there is potential for optimization
(some types of selectors are faster than others)
*/
var procedure = require("./procedure.json");
var attributes = {
__proto__: null,
exists: 10,
equals: 8,
not: 7,
start: 6,
end: 6,
any: 5,
hyphen: 4,
element: 4
};
function sortByProcedure(arr) {
var procs = arr.map(getProcedure);
for (var i = 1; i < arr.length; i++) {
var procNew = procs[i];
if (procNew < 0) continue;
for (var j = i - 1; j >= 0 && procNew < procs[j]; j--) {
var token = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = token;
procs[j + 1] = procs[j];
procs[j] = procNew;
}
}
}
function getProcedure(token) {
var proc = procedure[token.type];
if (proc === procedure.attribute) {
proc = attributes[token.action];
if (proc === attributes.equals && token.name === "id") {
//prefer ID selectors (eg. #ID)
proc = 9;
}
if (token.ignoreCase) {
//ignoreCase adds some overhead, prefer "normal" token
//this is a binary operation, to ensure it's still an int
proc >>= 1;
}
} else if (proc === procedure.pseudo) {
if (!token.data) {
proc = 3;
} else if (token.name === "has" || token.name === "contains") {
proc = 0; //expensive in any case
} else if (token.name === "matches" || token.name === "not") {
proc = 0;
for (var i = 0; i < token.data.length; i++) {
//TODO better handling of complex selectors
if (token.data[i].length !== 1) continue;
var cur = getProcedure(token.data[i][0]);
//avoid executing :has or :contains
if (cur === 0) {
proc = 0;
break;
}
if (cur > proc) proc = cur;
}
if (token.data.length > 1 && proc > 0) proc -= 1;
} else {
proc = 1;
}
}
return proc;
}