var DomUtils = require("domutils"), hasAttrib = DomUtils.hasAttrib, getAttributeValue = DomUtils.getAttributeValue, 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){ var name = data.name, value = data.value; if(data.ignoreCase){ value = value.toLowerCase(); return function equalsIC(elem){ var attr = getAttributeValue(elem, name); return attr != null && attr.toLowerCase() === value && next(elem); }; } return function equals(elem){ return getAttributeValue(elem, name) === value && next(elem); }; }, hyphen: function(next, data){ var name = data.name, value = data.value, len = value.length; if(data.ignoreCase){ value = value.toLowerCase(); return function hyphenIC(elem){ var attr = 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 = getAttributeValue(elem, name); return attr != null && attr.substr(0, len) === value && (attr.length === len || attr.charAt(len) === "-") && next(elem); }; }, element: function(next, data){ var name = data.name, value = data.value; 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 = getAttributeValue(elem, name); return attr != null && regex.test(attr) && next(elem); }; }, exists: function(next, data){ var name = data.name; return function exists(elem){ return hasAttrib(elem, name) && next(elem); }; }, start: function(next, data){ var name = data.name, value = data.value, len = value.length; if(len === 0){ return falseFunc; } if(data.ignoreCase){ value = value.toLowerCase(); return function startIC(elem){ var attr = getAttributeValue(elem, name); return attr != null && attr.substr(0, len).toLowerCase() === value && next(elem); }; } return function start(elem){ var attr = getAttributeValue(elem, name); return attr != null && attr.substr(0, len) === value && next(elem); }; }, end: function(next, data){ var name = data.name, value = data.value, len = -value.length; if(len === 0){ return falseFunc; } if(data.ignoreCase){ value = value.toLowerCase(); return function endIC(elem){ var attr = getAttributeValue(elem, name); return attr != null && attr.substr(len).toLowerCase() === value && next(elem); }; } return function end(elem){ var attr = getAttributeValue(elem, name); return attr != null && attr.substr(len) === value && next(elem); }; }, any: function(next, data){ var name = data.name, value = data.value; if(value === ""){ return falseFunc; } if(data.ignoreCase){ var regex = new RegExp(value.replace(reChars, "\\$&"), "i"); return function anyIC(elem){ var attr = getAttributeValue(elem, name); return attr != null && regex.test(attr) && next(elem); }; } return function any(elem){ var attr = getAttributeValue(elem, name); return attr != null && attr.indexOf(value) >= 0 && next(elem); }; }, not: function(next, data){ var name = data.name, value = data.value; if(value === ""){ return function notEmpty(elem){ return !!getAttributeValue(elem, name) && next(elem); }; } else if(data.ignoreCase){ value = value.toLowerCase(); return function notIC(elem){ var attr = getAttributeValue(elem, name); return attr != null && attr.toLowerCase() !== value && next(elem); }; } return function not(elem){ return getAttributeValue(elem, name) !== value && next(elem); }; } }; module.exports = { compile: function(next, data, options){ if(options && options.strict && ( data.ignoreCase || data.action === "not" )) throw SyntaxError("Unsupported attribute selector"); return attributeRules[data.action](next, data); }, rules: attributeRules };