Demo for query storing
Change-Id: I947bcac841992c3f6cfd01ab337c265b0d01cb70
diff --git a/node_modules/css-select/lib/pseudos.js b/node_modules/css-select/lib/pseudos.js
new file mode 100644
index 0000000..93eb6bf
--- /dev/null
+++ b/node_modules/css-select/lib/pseudos.js
@@ -0,0 +1,453 @@
+/*
+ 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 getNCheck = require("nth-check");
+var BaseFuncs = require("boolbase");
+var attributes = require("./attributes.js");
+var trueFunc = BaseFuncs.trueFunc;
+var falseFunc = BaseFuncs.falseFunc;
+
+var checkAttrib = attributes.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 = getNCheck(rule);
+ var adapter = options.adapter;
+
+ if (func === falseFunc) return func;
+ if (func === trueFunc) 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 = getNCheck(rule);
+ var adapter = options.adapter;
+
+ if (func === falseFunc) return func;
+ if (func === trueFunc) 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 = getNCheck(rule);
+ var adapter = options.adapter;
+
+ if (func === falseFunc) return func;
+ if (func === trueFunc) 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 = getNCheck(rule);
+ var adapter = options.adapter;
+
+ if (func === falseFunc) return func;
+ if (func === trueFunc) 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;
+ },
+ 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;
+ },
+ 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;
+ }
+};
+
+//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)$/;
+
+module.exports = {
+ 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) {
+ return func;
+ }
+
+ if (next === trueFunc) {
+ 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
+};