| var falseFunc = require("boolbase").falseFunc; |
| |
| //https://github.com/slevithan/XRegExp/blob/master/src/xregexp.js#L469 |
| var reChars = /[-[\]{}()*+?.,\\^$|#\s]/g; |
| |
| /* |
| attribute selectors |
| */ |
| var attributeRules = { |
| __proto__: null, |
| equals: function(next, data, options) { |
| var name = data.name; |
| var value = data.value; |
| var adapter = options.adapter; |
| |
| if (data.ignoreCase) { |
| value = value.toLowerCase(); |
| |
| return function equalsIC(elem) { |
| var attr = adapter.getAttributeValue(elem, name); |
| return attr != null && attr.toLowerCase() === value && next(elem); |
| }; |
| } |
| |
| return function equals(elem) { |
| return adapter.getAttributeValue(elem, name) === value && next(elem); |
| }; |
| }, |
| hyphen: function(next, data, options) { |
| var name = data.name; |
| var value = data.value; |
| var len = value.length; |
| var adapter = options.adapter; |
| |
| if (data.ignoreCase) { |
| value = value.toLowerCase(); |
| |
| return function hyphenIC(elem) { |
| var attr = adapter.getAttributeValue(elem, name); |
| return ( |
| attr != null && |
| (attr.length === len || attr.charAt(len) === "-") && |
| attr.substr(0, len).toLowerCase() === value && |
| next(elem) |
| ); |
| }; |
| } |
| |
| return function hyphen(elem) { |
| var attr = adapter.getAttributeValue(elem, name); |
| return ( |
| attr != null && |
| attr.substr(0, len) === value && |
| (attr.length === len || attr.charAt(len) === "-") && |
| next(elem) |
| ); |
| }; |
| }, |
| element: function(next, data, options) { |
| var name = data.name; |
| var value = data.value; |
| var adapter = options.adapter; |
| |
| if (/\s/.test(value)) { |
| return falseFunc; |
| } |
| |
| value = value.replace(reChars, "\\$&"); |
| |
| var pattern = "(?:^|\\s)" + value + "(?:$|\\s)", |
| flags = data.ignoreCase ? "i" : "", |
| regex = new RegExp(pattern, flags); |
| |
| return function element(elem) { |
| var attr = adapter.getAttributeValue(elem, name); |
| return attr != null && regex.test(attr) && next(elem); |
| }; |
| }, |
| exists: function(next, data, options) { |
| var name = data.name; |
| var adapter = options.adapter; |
| |
| return function exists(elem) { |
| return adapter.hasAttrib(elem, name) && next(elem); |
| }; |
| }, |
| start: function(next, data, options) { |
| var name = data.name; |
| var value = data.value; |
| var len = value.length; |
| var adapter = options.adapter; |
| |
| if (len === 0) { |
| return falseFunc; |
| } |
| |
| if (data.ignoreCase) { |
| value = value.toLowerCase(); |
| |
| return function startIC(elem) { |
| var attr = adapter.getAttributeValue(elem, name); |
| return attr != null && attr.substr(0, len).toLowerCase() === value && next(elem); |
| }; |
| } |
| |
| return function start(elem) { |
| var attr = adapter.getAttributeValue(elem, name); |
| return attr != null && attr.substr(0, len) === value && next(elem); |
| }; |
| }, |
| end: function(next, data, options) { |
| var name = data.name; |
| var value = data.value; |
| var len = -value.length; |
| var adapter = options.adapter; |
| |
| if (len === 0) { |
| return falseFunc; |
| } |
| |
| if (data.ignoreCase) { |
| value = value.toLowerCase(); |
| |
| return function endIC(elem) { |
| var attr = adapter.getAttributeValue(elem, name); |
| return attr != null && attr.substr(len).toLowerCase() === value && next(elem); |
| }; |
| } |
| |
| return function end(elem) { |
| var attr = adapter.getAttributeValue(elem, name); |
| return attr != null && attr.substr(len) === value && next(elem); |
| }; |
| }, |
| any: function(next, data, options) { |
| var name = data.name; |
| var value = data.value; |
| var adapter = options.adapter; |
| |
| if (value === "") { |
| return falseFunc; |
| } |
| |
| if (data.ignoreCase) { |
| var regex = new RegExp(value.replace(reChars, "\\$&"), "i"); |
| |
| return function anyIC(elem) { |
| var attr = adapter.getAttributeValue(elem, name); |
| return attr != null && regex.test(attr) && next(elem); |
| }; |
| } |
| |
| return function any(elem) { |
| var attr = adapter.getAttributeValue(elem, name); |
| return attr != null && attr.indexOf(value) >= 0 && next(elem); |
| }; |
| }, |
| not: function(next, data, options) { |
| var name = data.name; |
| var value = data.value; |
| var adapter = options.adapter; |
| |
| if (value === "") { |
| return function notEmpty(elem) { |
| return !!adapter.getAttributeValue(elem, name) && next(elem); |
| }; |
| } else if (data.ignoreCase) { |
| value = value.toLowerCase(); |
| |
| return function notIC(elem) { |
| var attr = adapter.getAttributeValue(elem, name); |
| return attr != null && attr.toLowerCase() !== value && next(elem); |
| }; |
| } |
| |
| return function not(elem) { |
| return adapter.getAttributeValue(elem, name) !== value && next(elem); |
| }; |
| } |
| }; |
| |
| module.exports = { |
| compile: function(next, data, options) { |
| if (options && options.strict && (data.ignoreCase || data.action === "not")) { |
| throw new Error("Unsupported attribute selector"); |
| } |
| return attributeRules[data.action](next, data, options); |
| }, |
| rules: attributeRules |
| }; |