blob: 9d7159145ecda1510223a90502ecb59d046e78ab [file] [log] [blame]
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
};