| 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; |
| } |