node-scrapy
Version:
Simple, lightweight and expressive web scraping with Node.js
1,744 lines (1,459 loc) • 98.9 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('domutils')) :
typeof define === 'function' && define.amd ? define(['domutils'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.scrapy = factory(global.DomUtils));
}(this, (function (DomUtils) { 'use strict';
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var DomUtils__default = /*#__PURE__*/_interopDefaultLegacy(DomUtils);
var boolbase = {
trueFunc: function trueFunc() {
return true;
},
falseFunc: function falseFunc() {
return false;
}
};
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function createCommonjsModule(fn, basedir, module) {
return module = {
path: basedir,
exports: {},
require: function (path, base) {
return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
}
}, fn(module, module.exports), module.exports;
}
function commonjsRequire () {
throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
}
var parse_1 = createCommonjsModule(function (module, exports) {
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = parse;
var reName = /^[^\\]?(?:\\(?:[\da-f]{1,6}\s?|.)|[\w\-\u00b0-\uFFFF])+/;
var reEscape = /\\([\da-f]{1,6}\s?|(\s)|.)/gi; //modified version of https://github.com/jquery/sizzle/blob/master/src/sizzle.js#L87
var reAttr = /^\s*((?:\\.|[\w\u00b0-\uFFFF-])+)\s*(?:(\S?)=\s*(?:(['"])([^]*?)\3|(#?(?:\\.|[\w\u00b0-\uFFFF-])*)|)|)\s*(i)?\]/;
var actionTypes = {
undefined: "exists",
"": "equals",
"~": "element",
"^": "start",
$: "end",
"*": "any",
"!": "not",
"|": "hyphen"
};
var Traversals = {
">": "child",
"<": "parent",
"~": "sibling",
"+": "adjacent"
};
var attribSelectors = {
"#": ["id", "equals"],
".": ["class", "element"]
}; //pseudos, whose data-property is parsed as well
var unpackPseudos = new Set(["has", "not", "matches"]);
var stripQuotesFromPseudos = new Set(["contains", "icontains"]);
var quotes = new Set(['"', "'"]); //unescape function taken from https://github.com/jquery/sizzle/blob/master/src/sizzle.js#L152
function funescape(_, escaped, escapedWhitespace) {
var high = parseInt(escaped, 16) - 0x10000; // NaN means non-codepoint
return high !== high || escapedWhitespace ? escaped : high < 0 ? // BMP codepoint
String.fromCharCode(high + 0x10000) : // Supplemental Plane codepoint (surrogate pair)
String.fromCharCode(high >> 10 | 0xd800, high & 0x3ff | 0xdc00);
}
function unescapeCSS(str) {
return str.replace(reEscape, funescape);
}
function isWhitespace(c) {
return c === " " || c === "\n" || c === "\t" || c === "\f" || c === "\r";
}
function parse(selector, options) {
var subselects = [];
selector = parseSelector(subselects, "" + selector, options);
if (selector !== "") {
throw new Error("Unmatched selector: " + selector);
}
return subselects;
}
function parseSelector(subselects, selector, options) {
var tokens = [];
var sawWS = false;
function getName() {
var match = selector.match(reName);
if (!match) {
throw new Error("Expected name, found " + selector);
}
var sub = match[0];
selector = selector.substr(sub.length);
return unescapeCSS(sub);
}
function stripWhitespace(start) {
while (isWhitespace(selector.charAt(start))) start++;
selector = selector.substr(start);
}
function isEscaped(pos) {
var slashCount = 0;
while (selector.charAt(--pos) === "\\") slashCount++;
return (slashCount & 1) === 1;
}
stripWhitespace(0);
while (selector !== "") {
var firstChar = selector.charAt(0);
if (isWhitespace(firstChar)) {
sawWS = true;
stripWhitespace(1);
} else if (firstChar in Traversals) {
tokens.push({
type: Traversals[firstChar]
});
sawWS = false;
stripWhitespace(1);
} else if (firstChar === ",") {
if (tokens.length === 0) {
throw new Error("Empty sub-selector");
}
subselects.push(tokens);
tokens = [];
sawWS = false;
stripWhitespace(1);
} else {
if (sawWS) {
if (tokens.length > 0) {
tokens.push({
type: "descendant"
});
}
sawWS = false;
}
if (firstChar === "*") {
selector = selector.substr(1);
tokens.push({
type: "universal"
});
} else if (firstChar in attribSelectors) {
var _a = attribSelectors[firstChar],
name_1 = _a[0],
action = _a[1];
selector = selector.substr(1);
tokens.push({
type: "attribute",
name: name_1,
action: action,
value: getName(),
ignoreCase: false
});
} else if (firstChar === "[") {
selector = selector.substr(1);
var data = selector.match(reAttr);
if (!data) {
throw new Error("Malformed attribute selector: " + selector);
}
selector = selector.substr(data[0].length);
var name_2 = unescapeCSS(data[1]);
if (!options || ("lowerCaseAttributeNames" in options ? options.lowerCaseAttributeNames : !options.xmlMode)) {
name_2 = name_2.toLowerCase();
}
tokens.push({
type: "attribute",
name: name_2,
action: actionTypes[data[2]],
value: unescapeCSS(data[4] || data[5] || ""),
ignoreCase: !!data[6]
});
} else if (firstChar === ":") {
if (selector.charAt(1) === ":") {
selector = selector.substr(2);
tokens.push({
type: "pseudo-element",
name: getName().toLowerCase()
});
continue;
}
selector = selector.substr(1);
var name_3 = getName().toLowerCase();
var data = null;
if (selector.charAt(0) === "(") {
if (unpackPseudos.has(name_3)) {
var quot = selector.charAt(1);
var quoted = quotes.has(quot);
selector = selector.substr(quoted ? 2 : 1);
data = [];
selector = parseSelector(data, selector, options);
if (quoted) {
if (selector.charAt(0) !== quot) {
throw new Error("Unmatched quotes in :" + name_3);
} else {
selector = selector.substr(1);
}
}
if (selector.charAt(0) !== ")") {
throw new Error("Missing closing parenthesis in :" + name_3 + " (" + selector + ")");
}
selector = selector.substr(1);
} else {
var pos = 1;
var counter = 1;
for (; counter > 0 && pos < selector.length; pos++) {
if (selector.charAt(pos) === "(" && !isEscaped(pos)) counter++;else if (selector.charAt(pos) === ")" && !isEscaped(pos)) counter--;
}
if (counter) {
throw new Error("Parenthesis not matched");
}
data = selector.substr(1, pos - 2);
selector = selector.substr(pos);
if (stripQuotesFromPseudos.has(name_3)) {
var quot = data.charAt(0);
if (quot === data.slice(-1) && quotes.has(quot)) {
data = data.slice(1, -1);
}
data = unescapeCSS(data);
}
}
}
tokens.push({
type: "pseudo",
name: name_3,
data: data
});
} else if (reName.test(selector)) {
var name_4 = getName();
if (!options || ("lowerCaseTags" in options ? options.lowerCaseTags : !options.xmlMode)) {
name_4 = name_4.toLowerCase();
}
tokens.push({
type: "tag",
name: name_4
});
} else {
if (tokens.length && tokens[tokens.length - 1].type === "descendant") {
tokens.pop();
}
addToken(subselects, tokens);
return selector;
}
}
}
addToken(subselects, tokens);
return selector;
}
function addToken(subselects, tokens) {
if (subselects.length > 0 && tokens.length === 0) {
throw new Error("Empty sub-selector");
}
subselects.push(tokens);
}
});
var stringify_1 = createCommonjsModule(function (module, exports) {
Object.defineProperty(exports, "__esModule", {
value: true
});
var actionTypes = {
equals: "",
element: "~",
start: "^",
end: "$",
any: "*",
not: "!",
hyphen: "|"
};
function stringify(token) {
return token.map(stringifySubselector).join(", ");
}
exports.default = stringify;
function stringifySubselector(token) {
return token.map(stringifyToken).join("");
}
function stringifyToken(token) {
switch (token.type) {
// Simple types
case "child":
return " > ";
case "parent":
return " < ";
case "sibling":
return " ~ ";
case "adjacent":
return " + ";
case "descendant":
return " ";
case "universal":
return "*";
case "tag":
return escapeName(token.name);
case "pseudo-element":
return "::" + escapeName(token.name);
case "pseudo":
if (token.data === null) return ":" + escapeName(token.name);
if (typeof token.data === "string") {
return ":" + escapeName(token.name) + "(" + token.data + ")";
}
return ":" + escapeName(token.name) + "(" + stringify(token.data) + ")";
case "attribute":
if (token.action === "exists") {
return "[" + escapeName(token.name) + "]";
}
if (token.name === "id" && token.action === "equals" && !token.ignoreCase) {
return "#" + escapeName(token.value);
}
if (token.name === "class" && token.action === "element" && !token.ignoreCase) {
return "." + escapeName(token.value);
}
return "[" + escapeName(token.name) + actionTypes[token.action] + "='" + escapeName(token.value) + "'" + (token.ignoreCase ? "i" : "") + "]";
default:
throw new Error("Unknown type");
}
}
function escapeName(str) {
//TODO
return str;
}
});
var lib = createCommonjsModule(function (module, exports) {
var __createBinding = commonjsGlobal && commonjsGlobal.__createBinding || (Object.create ? function (o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, {
enumerable: true,
get: function () {
return m[k];
}
});
} : function (o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
});
var __exportStar = commonjsGlobal && commonjsGlobal.__exportStar || function (m, exports) {
for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", {
value: true
});
__exportStar(parse_1, exports);
var parse_1$1 = parse_1;
Object.defineProperty(exports, "parse", {
enumerable: true,
get: function () {
return parse_1$1.default;
}
});
Object.defineProperty(exports, "stringify", {
enumerable: true,
get: function () {
return stringify_1.default;
}
});
});
var universal = 50;
var tag = 30;
var attribute = 1;
var pseudo = 0;
var descendant = -1;
var child = -1;
var parent = -1;
var sibling = -1;
var adjacent = -1;
var procedure = {
universal: universal,
tag: tag,
attribute: attribute,
pseudo: pseudo,
descendant: descendant,
child: child,
parent: parent,
sibling: sibling,
adjacent: adjacent
};
var sort = sortByProcedure;
/*
sort the parts of the passed selector,
as there is potential for optimization
(some types of selectors are faster than others)
*/
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;
}
var falseFunc = 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);
};
}
};
var attributes$1 = {
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
};
var parse_1$1 = parse; //following http://www.w3.org/TR/css3-selectors/#nth-child-pseudo
//[ ['-'|'+']? INTEGER? {N} [ S* ['-'|'+'] S* INTEGER ]?
var re_nthElement = /^([+\-]?\d*n)?\s*(?:([+\-]?)\s*(\d+))?$/;
/*
parses a nth-check formula, returns an array of two numbers
*/
function parse(formula) {
formula = formula.trim().toLowerCase();
if (formula === "even") {
return [2, 0];
} else if (formula === "odd") {
return [2, 1];
} else {
var parsed = formula.match(re_nthElement);
if (!parsed) {
throw new SyntaxError("n-th rule couldn't be parsed ('" + formula + "')");
}
var a;
if (parsed[1]) {
a = parseInt(parsed[1], 10);
if (isNaN(a)) {
if (parsed[1].charAt(0) === "-") a = -1;else a = 1;
}
} else a = 0;
return [a, parsed[3] ? parseInt((parsed[2] || "") + parsed[3], 10) : 0];
}
}
var compile_1 = compile;
var trueFunc = boolbase.trueFunc,
falseFunc$1 = boolbase.falseFunc;
/*
returns a function that checks if an elements index matches the given rule
highly optimized to return the fastest solution
*/
function compile(parsed) {
var a = parsed[0],
b = parsed[1] - 1; //when b <= 0, a*n won't be possible for any matches when a < 0
//besides, the specification says that no element is matched when a and b are 0
if (b < 0 && a <= 0) return falseFunc$1; //when a is in the range -1..1, it matches any element (so only b is checked)
if (a === -1) return function (pos) {
return pos <= b;
};
if (a === 0) return function (pos) {
return pos === b;
}; //when b <= 0 and a === 1, they match any element
if (a === 1) return b < 0 ? trueFunc : function (pos) {
return pos >= b;
}; //when a > 0, modulo can be used to check if there is a match
var bMod = b % a;
if (bMod < 0) bMod += a;
if (a > 1) {
return function (pos) {
return pos >= b && pos % a === bMod;
};
}
a *= -1; //make `a` positive
return function (pos) {
return pos <= b && pos % a === bMod;
};
}
var nthCheck = function nthCheck(formula) {
return compile_1(parse_1$1(formula));
};
var parse_1$2 = parse_1$1;
var compile_1$1 = compile_1;
nthCheck.parse = parse_1$2;
nthCheck.compile = compile_1$1;
/*
pseudo selectors
---
they are available in two forms:
* filters called when the selector
is compiled and return a function
that needs to return next()
* pseudos get called on execution
they need to return a boolean
*/
var trueFunc$1 = boolbase.trueFunc;
var falseFunc$2 = boolbase.falseFunc;
var checkAttrib = attributes$1.rules.equals;
function getAttribFunc(name, value) {
var data = {
name: name,
value: value
};
return function attribFunc(next, rule, options) {
return checkAttrib(next, data, options);
};
}
function getChildFunc(next, adapter) {
return function (elem) {
return !!adapter.getParent(elem) && next(elem);
};
}
var filters = {
contains: function (next, text, options) {
var adapter = options.adapter;
return function contains(elem) {
return next(elem) && adapter.getText(elem).indexOf(text) >= 0;
};
},
icontains: function (next, text, options) {
var itext = text.toLowerCase();
var adapter = options.adapter;
return function icontains(elem) {
return next(elem) && adapter.getText(elem).toLowerCase().indexOf(itext) >= 0;
};
},
//location specific methods
"nth-child": function (next, rule, options) {
var func = nthCheck(rule);
var adapter = options.adapter;
if (func === falseFunc$2) return func;
if (func === trueFunc$1) return getChildFunc(next, adapter);
return function nthChild(elem) {
var siblings = adapter.getSiblings(elem);
for (var i = 0, pos = 0; i < siblings.length; i++) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) break;else pos++;
}
}
return func(pos) && next(elem);
};
},
"nth-last-child": function (next, rule, options) {
var func = nthCheck(rule);
var adapter = options.adapter;
if (func === falseFunc$2) return func;
if (func === trueFunc$1) return getChildFunc(next, adapter);
return function nthLastChild(elem) {
var siblings = adapter.getSiblings(elem);
for (var pos = 0, i = siblings.length - 1; i >= 0; i--) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) break;else pos++;
}
}
return func(pos) && next(elem);
};
},
"nth-of-type": function (next, rule, options) {
var func = nthCheck(rule);
var adapter = options.adapter;
if (func === falseFunc$2) return func;
if (func === trueFunc$1) return getChildFunc(next, adapter);
return function nthOfType(elem) {
var siblings = adapter.getSiblings(elem);
for (var pos = 0, i = 0; i < siblings.length; i++) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) break;
if (adapter.getName(siblings[i]) === adapter.getName(elem)) pos++;
}
}
return func(pos) && next(elem);
};
},
"nth-last-of-type": function (next, rule, options) {
var func = nthCheck(rule);
var adapter = options.adapter;
if (func === falseFunc$2) return func;
if (func === trueFunc$1) return getChildFunc(next, adapter);
return function nthLastOfType(elem) {
var siblings = adapter.getSiblings(elem);
for (var pos = 0, i = siblings.length - 1; i >= 0; i--) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) break;
if (adapter.getName(siblings[i]) === adapter.getName(elem)) pos++;
}
}
return func(pos) && next(elem);
};
},
//TODO determine the actual root element
root: function (next, rule, options) {
var adapter = options.adapter;
return function (elem) {
return !adapter.getParent(elem) && next(elem);
};
},
scope: function (next, rule, options, context) {
var adapter = options.adapter;
if (!context || context.length === 0) {
//equivalent to :root
return filters.root(next, rule, options);
}
function equals(a, b) {
if (typeof adapter.equals === "function") return adapter.equals(a, b);
return a === b;
}
if (context.length === 1) {
//NOTE: can't be unpacked, as :has uses this for side-effects
return function (elem) {
return equals(context[0], elem) && next(elem);
};
}
return function (elem) {
return context.indexOf(elem) >= 0 && next(elem);
};
},
//jQuery extensions (others follow as pseudos)
checkbox: getAttribFunc("type", "checkbox"),
file: getAttribFunc("type", "file"),
password: getAttribFunc("type", "password"),
radio: getAttribFunc("type", "radio"),
reset: getAttribFunc("type", "reset"),
image: getAttribFunc("type", "image"),
submit: getAttribFunc("type", "submit"),
//dynamic state pseudos. These depend on optional Adapter methods.
hover: function (next, rule, options) {
var adapter = options.adapter;
if (typeof adapter.isHovered === 'function') {
return function hover(elem) {
return next(elem) && adapter.isHovered(elem);
};
}
return falseFunc$2;
},
visited: function (next, rule, options) {
var adapter = options.adapter;
if (typeof adapter.isVisited === 'function') {
return function visited(elem) {
return next(elem) && adapter.isVisited(elem);
};
}
return falseFunc$2;
},
active: function (next, rule, options) {
var adapter = options.adapter;
if (typeof adapter.isActive === 'function') {
return function active(elem) {
return next(elem) && adapter.isActive(elem);
};
}
return falseFunc$2;
}
}; //helper methods
function getFirstElement(elems, adapter) {
for (var i = 0; elems && i < elems.length; i++) {
if (adapter.isTag(elems[i])) return elems[i];
}
} //while filters are precompiled, pseudos get called when they are needed
var pseudos = {
empty: function (elem, adapter) {
return !adapter.getChildren(elem).some(function (elem) {
return adapter.isTag(elem) || elem.type === "text";
});
},
"first-child": function (elem, adapter) {
return getFirstElement(adapter.getSiblings(elem), adapter) === elem;
},
"last-child": function (elem, adapter) {
var siblings = adapter.getSiblings(elem);
for (var i = siblings.length - 1; i >= 0; i--) {
if (siblings[i] === elem) return true;
if (adapter.isTag(siblings[i])) break;
}
return false;
},
"first-of-type": function (elem, adapter) {
var siblings = adapter.getSiblings(elem);
for (var i = 0; i < siblings.length; i++) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) return true;
if (adapter.getName(siblings[i]) === adapter.getName(elem)) break;
}
}
return false;
},
"last-of-type": function (elem, adapter) {
var siblings = adapter.getSiblings(elem);
for (var i = siblings.length - 1; i >= 0; i--) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) return true;
if (adapter.getName(siblings[i]) === adapter.getName(elem)) break;
}
}
return false;
},
"only-of-type": function (elem, adapter) {
var siblings = adapter.getSiblings(elem);
for (var i = 0, j = siblings.length; i < j; i++) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) continue;
if (adapter.getName(siblings[i]) === adapter.getName(elem)) {
return false;
}
}
}
return true;
},
"only-child": function (elem, adapter) {
var siblings = adapter.getSiblings(elem);
for (var i = 0; i < siblings.length; i++) {
if (adapter.isTag(siblings[i]) && siblings[i] !== elem) return false;
}
return true;
},
//:matches(a, area, link)[href]
link: function (elem, adapter) {
return adapter.hasAttrib(elem, "href");
},
//TODO: :any-link once the name is finalized (as an alias of :link)
//forms
//to consider: :target
//:matches([selected], select:not([multiple]):not(> option[selected]) > option:first-of-type)
selected: function (elem, adapter) {
if (adapter.hasAttrib(elem, "selected")) return true;else if (adapter.getName(elem) !== "option") return false; //the first <option> in a <select> is also selected
var parent = adapter.getParent(elem);
if (!parent || adapter.getName(parent) !== "select" || adapter.hasAttrib(parent, "multiple")) {
return false;
}
var siblings = adapter.getChildren(parent);
var sawElem = false;
for (var i = 0; i < siblings.length; i++) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) {
sawElem = true;
} else if (!sawElem) {
return false;
} else if (adapter.hasAttrib(siblings[i], "selected")) {
return false;
}
}
}
return sawElem;
},
//https://html.spec.whatwg.org/multipage/scripting.html#disabled-elements
//:matches(
// :matches(button, input, select, textarea, menuitem, optgroup, option)[disabled],
// optgroup[disabled] > option),
// fieldset[disabled] * //TODO not child of first <legend>
//)
disabled: function (elem, adapter) {
return adapter.hasAttrib(elem, "disabled");
},
enabled: function (elem, adapter) {
return !adapter.hasAttrib(elem, "disabled");
},
//:matches(:matches(:radio, :checkbox)[checked], :selected) (TODO menuitem)
checked: function (elem, adapter) {
return adapter.hasAttrib(elem, "checked") || pseudos.selected(elem, adapter);
},
//:matches(input, select, textarea)[required]
required: function (elem, adapter) {
return adapter.hasAttrib(elem, "required");
},
//:matches(input, select, textarea):not([required])
optional: function (elem, adapter) {
return !adapter.hasAttrib(elem, "required");
},
//jQuery extensions
//:not(:empty)
parent: function (elem, adapter) {
return !pseudos.empty(elem, adapter);
},
//:matches(h1, h2, h3, h4, h5, h6)
header: namePseudo(["h1", "h2", "h3", "h4", "h5", "h6"]),
//:matches(button, input[type=button])
button: function (elem, adapter) {
var name = adapter.getName(elem);
return name === "button" || name === "input" && adapter.getAttributeValue(elem, "type") === "button";
},
//:matches(input, textarea, select, button)
input: namePseudo(["input", "textarea", "select", "button"]),
//input:matches(:not([type!='']), [type='text' i])
text: function (elem, adapter) {
var attr;
return adapter.getName(elem) === "input" && (!(attr = adapter.getAttributeValue(elem, "type")) || attr.toLowerCase() === "text");
}
};
function namePseudo(names) {
if (typeof Set !== "undefined") {
// eslint-disable-next-line no-undef
var nameSet = new Set(names);
return function (elem, adapter) {
return nameSet.has(adapter.getName(elem));
};
}
return function (elem, adapter) {
return names.indexOf(adapter.getName(elem)) >= 0;
};
}
function verifyArgs(func, name, subselect) {
if (subselect === null) {
if (func.length > 2 && name !== "scope") {
throw new Error("pseudo-selector :" + name + " requires an argument");
}
} else {
if (func.length === 2) {
throw new Error("pseudo-selector :" + name + " doesn't have any arguments");
}
}
} //FIXME this feels hacky
var re_CSS3 = /^(?:(?:nth|last|first|only)-(?:child|of-type)|root|empty|(?:en|dis)abled|checked|not)$/;
var pseudos_1 = {
compile: function (next, data, options, context) {
var name = data.name;
var subselect = data.data;
var adapter = options.adapter;
if (options && options.strict && !re_CSS3.test(name)) {
throw new Error(":" + name + " isn't part of CSS3");
}
if (typeof filters[name] === "function") {
return filters[name](next, subselect, options, context);
} else if (typeof pseudos[name] === "function") {
var func = pseudos[name];
verifyArgs(func, name, subselect);
if (func === falseFunc$2) {
return func;
}
if (next === trueFunc$1) {
return function pseudoRoot(elem) {
return func(elem, adapter, subselect);
};
}
return function pseudoArgs(elem) {
return func(elem, adapter, subselect) && next(elem);
};
} else {
throw new Error("unmatched pseudo-class :" + name);
}
},
filters: filters,
pseudos: pseudos
};
/*
all available rules
*/
var general = {
__proto__: null,
attribute: attributes$1.compile,
pseudo: pseudos_1.compile,
//tags
tag: function (next, data, options) {
var name = data.name;
var adapter = options.adapter;
return function tag(elem) {
return adapter.getName(elem) === name && next(elem);
};
},
//traversal
descendant: function (next, data, options) {
// eslint-disable-next-line no-undef
var isFalseCache = typeof WeakSet !== "undefined" ? new WeakSet() : null;
var adapter = options.adapter;
return function descendant(elem) {
var found = false;
while (!found && (elem = adapter.getParent(elem))) {
if (!isFalseCache || !isFalseCache.has(elem)) {
found = next(elem);
if (!found && isFalseCache) {
isFalseCache.add(elem);
}
}
}
return found;
};
},
_flexibleDescendant: function (next, data, options) {
var adapter = options.adapter; // Include element itself, only used while querying an array
return function descendant(elem) {
var found = next(elem);
while (!found && (elem = adapter.getParent(elem))) {
found = next(elem);
}
return found;
};
},
parent: function (next, data, options) {
if (options && options.strict) {
throw new Error("Parent selector isn't part of CSS3");
}
var adapter = options.adapter;
return function parent(elem) {
return adapter.getChildren(elem).some(test);
};
function test(elem) {
return adapter.isTag(elem) && next(elem);
}
},
child: function (next, data, options) {
var adapter = options.adapter;
return function child(elem) {
var parent = adapter.getParent(elem);
return !!parent && next(parent);
};
},
sibling: function (next, data, options) {
var adapter = options.adapter;
return function sibling(elem) {
var siblings = adapter.getSiblings(elem);
for (var i = 0; i < siblings.length; i++) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) break;
if (next(siblings[i])) return true;
}
}
return false;
};
},
adjacent: function (next, data, options) {
var adapter = options.adapter;
return function adjacent(elem) {
var siblings = adapter.getSiblings(elem),
lastElement;
for (var i = 0; i < siblings.length; i++) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) break;
lastElement = siblings[i];
}
}
return !!lastElement && next(lastElement);
};
},
universal: function (next) {
return next;
}
};
/*
compiles a selector to an executable function
*/
var compile_1$2 = compile$1;
var parse$1 = lib.parse;
var trueFunc$2 = boolbase.trueFunc;
var falseFunc$3 = boolbase.falseFunc;
var filters$1 = pseudos_1.filters;
function compile$1(selector, options, context) {
var next = compileUnsafe(selector, options, context);
return wrap(next, options);
}
function wrap(next, options) {
var adapter = options.adapter;
return function base(elem) {
return adapter.isTag(elem) && next(elem);
};
}
function compileUnsafe(selector, options, context) {
var token = parse$1(selector, options);
return compileToken(token, options, context);
}
function includesScopePseudo(t) {
return t.type === "pseudo" && (t.name === "scope" || Array.isArray(t.data) && t.data.some(function (data) {
return data.some(includesScopePseudo);
}));
}
var DESCENDANT_TOKEN = {
type: "descendant"
};
var FLEXIBLE_DESCENDANT_TOKEN = {
type: "_flexibleDescendant"
};
var SCOPE_TOKEN = {
type: "pseudo",
name: "scope"
};
var PLACEHOLDER_ELEMENT = {}; //CSS 4 Spec (Draft): 3.3.1. Absolutizing a Scope-relative Selector
//http://www.w3.org/TR/selectors4/#absolutizing
function absolutize(token, options, context) {
var adapter = options.adapter; //TODO better check if context is document
var hasContext = !!context && !!context.length && context.every(function (e) {
return e === PLACEHOLDER_ELEMENT || !!adapter.getParent(e);
});
token.forEach(function (t) {
if (t.length > 0 && isTraversal(t[0]) && t[0].type !== "descendant") ; else if (hasContext && !(Array.isArray(t) ? t.some(includesScopePseudo) : includesScopePseudo(t))) {
t.unshift(DESCENDANT_TOKEN);
} else {
return;
}
t.unshift(SCOPE_TOKEN);
});
}
function compileToken(token, options, context) {
token = token.filter(function (t) {
return t.length > 0;
});
token.forEach(sort);
var isArrayContext = Array.isArray(context);
context = options && options.context || context;
if (context && !isArrayContext) context = [context];
absolutize(token, options, context);
var shouldTestNextSiblings = false;
var query = token.map(function (rules) {
if (rules[0] && rules[1] && rules[0].name === "scope") {
var ruleType = rules[1].type;
if (isArrayContext && ruleType === "descendant") {
rules[1] = FLEXIBLE_DESCENDANT_TOKEN;
} else if (ruleType === "adjacent" || ruleType === "sibling") {
shouldTestNextSiblings = true;
}
}
return compileRules(rules, options, context);
}).reduce(reduceRules, falseFunc$3);
query.shouldTestNextSiblings = shouldTestNextSiblings;
return query;
}
function isTraversal(t) {
return procedure[t.type] < 0;
}
function compileRules(rules, options, context) {
return rules.reduce(function (func, rule) {
if (func === falseFunc$3) return func;
if (!(rule.type in general)) {
throw new Error("Rule type " + rule.type + " is not supported by css-select");
}
return general[rule.type](func, rule, options, context);
}, options && options.rootFunc || trueFunc$2);
}
function reduceRules(a, b) {
if (b === falseFunc$3 || a === trueFunc$2) {
return a;
}
if (a === falseFunc$3 || b === trueFunc$2) {
return b;
}
return function combine(elem) {
return a(elem) || b(elem);
};
}
function containsTraversal(t) {
return t.some(isTraversal);
} //:not, :has and :matches have to compile selectors
//doing this in lib/pseudos.js would lead to circular dependencies,
//so we add them here
filters$1.not = function (next, token, options, context) {
var opts = {
xmlMode: !!(options && options.xmlMode),
strict: !!(options && options.strict),
adapter: options.adapter
};
if (opts.strict) {
if (token.length > 1 || token.some(containsTraversal)) {
throw new Error("complex selectors in :not aren't allowed in strict mode");
}
}
var func = compileToken(token, opts, context);
if (func === falseFunc$3) return next;
if (func === trueFunc$2) return falseFunc$3;
return function not(elem) {
return !func(elem) && next(elem);
};
};
filters$1.has = function (next, token, options) {
var adapter = options.adapter;
var opts = {
xmlMode: !!(options && options.xmlMode),
strict: !!(options && options.strict),
adapter: adapter
}; //FIXME: Uses an array as a pointer to the current element (side effects)
var context = token.some(containsTraversal) ? [PLACEHOLDER_ELEMENT] : null;
var func = compileToken(token, opts, context);
if (func === falseFunc$3) return falseFunc$3;
if (func === trueFunc$2) {
return function hasChild(elem) {
return adapter.getChildren(elem).some(adapter.isTag) && next(elem);
};
}
func = wrap(func, options);
if (context) {
return function has(elem) {
return next(elem) && (context[0] = elem, adapter.existsOne(func, adapter.getChildren(elem)));
};
}
return function has(elem) {
return next(elem) && adapter.existsOne(func, adapter.getChildren(elem));
};
};
filters$1.matches = function (next, token, options, context) {
var opts = {
xmlMode: !!(options && options.xmlMode),
strict: !!(options && options.strict),
rootFunc: next,
adapter: options.adapter
};
return compileToken(token, opts, context);
};
compile$1.compileToken = compileToken;
compile$1.compileUnsafe = compileUnsafe;
compile$1.Pseudos = pseudos_1;
var cssSelect = CSSselect;
var falseFunc$4 = boolbase.falseFunc;
function wrapCompile(func) {
return function addAdapter(selector, options, context) {
options = options || {};
options.adapter = options.adapter || DomUtils__default['default'];
return func(selector, options, context);
};
}
var compile$2 = wrapCompile(compile_1$2);
var compileUnsafe$1 = wrapCompile(compile_1$2.compileUnsafe);
function getSelectorFunc(searchFunc) {
return function select(query, elems, options) {
options = options || {};
options.adapter = options.adapter || DomUtils__default['default'];
if (typeof query !== "function") {
query = compileUnsafe$1(query, options, elems);
}
if (query.shouldTestNextSiblings) {
elems = appendNextSiblings(options && options.context || elems, options.adapter);
}
if (!Array.isArray(elems)) elems = options.adapter.getChildren(elems);else elems = options.adapter.removeSubsets(elems);
return searchFunc(query, elems, options);
};
}
function getNextSiblings(elem, adapter) {
var siblings = adapter.getSiblings(elem);
if (!Array.isArray(siblings)) return [];
siblings = siblings.slice(0);
while (siblings.shift() !== elem);
return siblings;
}
function appendNextSiblings(elems, adapter) {
// Order matters because jQuery seems to check the children before the siblings
if (!Array.isArray(elems)) elems = [elems];
var newElems = elems.slice(0);
for (var i = 0, len = elems.length; i < len; i++) {
var nextSiblings = getNextSiblings(newElems[i], adapter);
newElems.push.apply(newElems, nextSiblings);
}
return newElems;
}
var selectAll = getSelectorFunc(function selectAll(query, elems, options) {
return query === falseFunc$4 || !elems || elems.length === 0 ? [] : options.adapter.findAll(query, elems);
});
var selectOne = getSelectorFunc(function selectOne(query, elems, options) {
return query === falseFunc$4 || !elems || elems.length === 0 ? null : options.adapter.findOne(query, elems);
});
function is(elem, query, options) {
options = options || {};
options.adapter = options.adapter || DomUtils__default['default'];
return (typeof query === "function" ? query : compile$2(query, options))(elem);
}
/*
the exported interface
*/
function CSSselect(query, elems, options) {
return selectAll(query, elems, options);
}
CSSselect.compile = compile$2;
CSSselect.filters = compile_1$2.Pseudos.filters;
CSSselect.pseudos = compile_1$2.Pseudos.pseudos;
CSSselect.selectAll = selectAll;
CSSselect.selectOne = selectOne;
CSSselect.is = is; //legacy methods (might be removed)
CSSselect.parse = compile$2;
CSSselect.iterate = selectAll; //hooks
CSSselect._compileUnsafe = compileUnsafe$1;
CSSselect._compileToken = compile_1$2.compileToken;
const EMPTY_OBJECT = {};
function isTag(elem) {
return elem.nodeType === 1;
}
function getChildren(elem) {
return elem.childNodes ? Array.prototype.slice.call(elem.childNodes, 0) : [];
}
function getParent(elem) {
return elem.parentNode;
}
function removeSubsets(nodes) {
var idx = nodes.length,
node,
ancestor,
replace; // Check if each node (or one of its ancestors) is already contained in the
// array.
while (--idx > -1) {
node = ancestor = nodes[idx]; // Temporarily remove the node under consideration
nodes[idx] = null;
replace = true;
while (ancestor) {
if (nodes.indexOf(ancestor) > -1) {
replace = false;
nodes.splice(idx, 1);
break;
}
ancestor = getParent(ancestor);
} // If the node has been found to be unique, re-insert it.
if (replace) {
nodes[idx] = node;
}
}
return nodes;
}
var adapter = {
isTag: isTag,
existsOne: function (test, elems) {
return elems.some(function (elem) {
return isTag(elem) ? test(elem) || adapter.existsOne(test, getChildren(elem)) : false;
});
},
getSiblings: function (elem) {
var parent = getParent(elem);
return parent ? getChildren(parent) : [elem];
},
getChildren: getChildren,
getParent: getParent,
getAttributeValue: function (elem, name) {
if (elem.attributes && name in elem.attributes) {
var attr = elem.attributes[name];
return typeof attr === "string" ? attr : attr.value;
} else if (name === "class" && elem.classList) {
return Array.from(elem.classList).join(" ");
}
},
hasAttrib: function (elem, name) {
return name in (elem.attributes || EMPTY_OBJECT);
},
removeSubsets: removeSubsets,
getName: function (elem) {
return (elem.tagName || "").toLowerCase();
},
findOne: function findOne(test, arr) {
var elem = null;
for (var i = 0, l = arr.length; i < l && !elem; i++) {
if (test(arr[i])) {
elem = arr[i];
} else {
var childs = getChildren(arr[i]);
if (childs && childs.length > 0) {
elem = findOne(test, childs);
}
}
}
return elem;
},
findAll: function findAll(test, elems) {
var result = [];
for (var i = 0, j = elems.length; i < j; i++) {
if (!isTag(elems[i])) continue;
if (test(elems[i])) result.push(elems[i]);
var childs = getChildren(elems[i]);
if (childs) result = result.concat(findAll(test, childs));
}
return result;
},
getText: function getText(elem) {
if (Array.isArray(elem)) return elem.map(getText).join("");
if (isTag(elem)) return getText(getChildren(elem));
if (elem.nodeType === 3) return elem.nodeValue;
return "";
}
};
var cssSelectBrowserAdapter = adapter;
function _typeof(obj) {
"@babel/helpers - typeof";
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function (obj) {
return typeof obj;
};
} else {
_typeof = function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymb