You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. "use strict";
  2. var __assign = (this && this.__assign) || function () {
  3. __assign = Object.assign || function(t) {
  4. for (var s, i = 1, n = arguments.length; i < n; i++) {
  5. s = arguments[i];
  6. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
  7. t[p] = s[p];
  8. }
  9. return t;
  10. };
  11. return __assign.apply(this, arguments);
  12. };
  13. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
  14. if (k2 === undefined) k2 = k;
  15. Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
  16. }) : (function(o, m, k, k2) {
  17. if (k2 === undefined) k2 = k;
  18. o[k2] = m[k];
  19. }));
  20. var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
  21. Object.defineProperty(o, "default", { enumerable: true, value: v });
  22. }) : function(o, v) {
  23. o["default"] = v;
  24. });
  25. var __importStar = (this && this.__importStar) || function (mod) {
  26. if (mod && mod.__esModule) return mod;
  27. var result = {};
  28. if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
  29. __setModuleDefault(result, mod);
  30. return result;
  31. };
  32. var __spreadArray = (this && this.__spreadArray) || function (to, from) {
  33. for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
  34. to[j] = from[i];
  35. return to;
  36. };
  37. Object.defineProperty(exports, "__esModule", { value: true });
  38. exports.select = exports.filter = exports.some = exports.is = exports.aliases = exports.pseudos = exports.filters = void 0;
  39. var css_what_1 = require("css-what");
  40. var css_select_1 = require("css-select");
  41. var DomUtils = __importStar(require("domutils"));
  42. var helpers_1 = require("./helpers");
  43. var positionals_1 = require("./positionals");
  44. // Re-export pseudo extension points
  45. var css_select_2 = require("css-select");
  46. Object.defineProperty(exports, "filters", { enumerable: true, get: function () { return css_select_2.filters; } });
  47. Object.defineProperty(exports, "pseudos", { enumerable: true, get: function () { return css_select_2.pseudos; } });
  48. Object.defineProperty(exports, "aliases", { enumerable: true, get: function () { return css_select_2.aliases; } });
  49. /** Used to indicate a scope should be filtered. Might be ignored when filtering. */
  50. var SCOPE_PSEUDO = {
  51. type: "pseudo",
  52. name: "scope",
  53. data: null,
  54. };
  55. /** Used for actually filtering for scope. */
  56. var CUSTOM_SCOPE_PSEUDO = __assign({}, SCOPE_PSEUDO);
  57. var UNIVERSAL_SELECTOR = { type: "universal", namespace: null };
  58. function is(element, selector, options) {
  59. if (options === void 0) { options = {}; }
  60. return some([element], selector, options);
  61. }
  62. exports.is = is;
  63. function some(elements, selector, options) {
  64. if (options === void 0) { options = {}; }
  65. if (typeof selector === "function")
  66. return elements.some(selector);
  67. var _a = helpers_1.groupSelectors(css_what_1.parse(selector, options)), plain = _a[0], filtered = _a[1];
  68. return ((plain.length > 0 && elements.some(css_select_1._compileToken(plain, options))) ||
  69. filtered.some(function (sel) { return filterBySelector(sel, elements, options).length > 0; }));
  70. }
  71. exports.some = some;
  72. function filterByPosition(filter, elems, data, options) {
  73. var num = typeof data === "string" ? parseInt(data, 10) : NaN;
  74. switch (filter) {
  75. case "first":
  76. case "lt":
  77. // Already done in `getLimit`
  78. return elems;
  79. case "last":
  80. return elems.length > 0 ? [elems[elems.length - 1]] : elems;
  81. case "nth":
  82. case "eq":
  83. return isFinite(num) && Math.abs(num) < elems.length
  84. ? [num < 0 ? elems[elems.length + num] : elems[num]]
  85. : [];
  86. case "gt":
  87. return isFinite(num) ? elems.slice(num + 1) : [];
  88. case "even":
  89. return elems.filter(function (_, i) { return i % 2 === 0; });
  90. case "odd":
  91. return elems.filter(function (_, i) { return i % 2 === 1; });
  92. case "not": {
  93. var filtered_1 = new Set(filterParsed(data, elems, options));
  94. return elems.filter(function (e) { return !filtered_1.has(e); });
  95. }
  96. }
  97. }
  98. function filter(selector, elements, options) {
  99. if (options === void 0) { options = {}; }
  100. return filterParsed(css_what_1.parse(selector, options), elements, options);
  101. }
  102. exports.filter = filter;
  103. /**
  104. * Filter a set of elements by a selector.
  105. *
  106. * Will return elements in the original order.
  107. *
  108. * @param selector Selector to filter by.
  109. * @param elements Elements to filter.
  110. * @param options Options for selector.
  111. */
  112. function filterParsed(selector, elements, options) {
  113. if (elements.length === 0)
  114. return [];
  115. var _a = helpers_1.groupSelectors(selector), plainSelectors = _a[0], filteredSelectors = _a[1];
  116. var found;
  117. if (plainSelectors.length) {
  118. var filtered = filterElements(elements, plainSelectors, options);
  119. // If there are no filters, just return
  120. if (filteredSelectors.length === 0) {
  121. return filtered;
  122. }
  123. // Otherwise, we have to do some filtering
  124. if (filtered.length) {
  125. found = new Set(filtered);
  126. }
  127. }
  128. for (var i = 0; i < filteredSelectors.length && (found === null || found === void 0 ? void 0 : found.size) !== elements.length; i++) {
  129. var filteredSelector = filteredSelectors[i];
  130. var missing = found
  131. ? elements.filter(function (e) { return DomUtils.isTag(e) && !found.has(e); })
  132. : elements;
  133. if (missing.length === 0)
  134. break;
  135. var filtered = filterBySelector(filteredSelector, elements, options);
  136. if (filtered.length) {
  137. if (!found) {
  138. /*
  139. * If we haven't found anything before the last selector,
  140. * just return what we found now.
  141. */
  142. if (i === filteredSelectors.length - 1) {
  143. return filtered;
  144. }
  145. found = new Set(filtered);
  146. }
  147. else {
  148. filtered.forEach(function (el) { return found.add(el); });
  149. }
  150. }
  151. }
  152. return typeof found !== "undefined"
  153. ? (found.size === elements.length
  154. ? elements
  155. : // Filter elements to preserve order
  156. elements.filter(function (el) {
  157. return found.has(el);
  158. }))
  159. : [];
  160. }
  161. function filterBySelector(selector, elements, options) {
  162. var _a;
  163. if (selector.some(css_what_1.isTraversal)) {
  164. /*
  165. * Get root node, run selector with the scope
  166. * set to all of our nodes.
  167. */
  168. var root = (_a = options.root) !== null && _a !== void 0 ? _a : helpers_1.getDocumentRoot(elements[0]);
  169. var sel = __spreadArray(__spreadArray([], selector), [CUSTOM_SCOPE_PSEUDO]);
  170. return findFilterElements(root, sel, options, true, elements);
  171. }
  172. // Performance optimization: If we don't have to traverse, just filter set.
  173. return findFilterElements(elements, selector, options, false);
  174. }
  175. function select(selector, root, options) {
  176. if (options === void 0) { options = {}; }
  177. if (typeof selector === "function") {
  178. return find(root, selector);
  179. }
  180. var _a = helpers_1.groupSelectors(css_what_1.parse(selector, options)), plain = _a[0], filtered = _a[1];
  181. var results = filtered.map(function (sel) {
  182. return findFilterElements(root, sel, options, true);
  183. });
  184. // Plain selectors can be queried in a single go
  185. if (plain.length) {
  186. results.push(findElements(root, plain, options, Infinity));
  187. }
  188. // If there was only a single selector, just return the result
  189. if (results.length === 1) {
  190. return results[0];
  191. }
  192. // Sort results, filtering for duplicates
  193. return DomUtils.uniqueSort(results.reduce(function (a, b) { return __spreadArray(__spreadArray([], a), b); }));
  194. }
  195. exports.select = select;
  196. // Traversals that are treated differently in css-select.
  197. var specialTraversal = new Set(["descendant", "adjacent"]);
  198. function includesScopePseudo(t) {
  199. return (t !== SCOPE_PSEUDO &&
  200. t.type === "pseudo" &&
  201. (t.name === "scope" ||
  202. (Array.isArray(t.data) &&
  203. t.data.some(function (data) { return data.some(includesScopePseudo); }))));
  204. }
  205. function addContextIfScope(selector, options, scopeContext) {
  206. return scopeContext && selector.some(includesScopePseudo)
  207. ? __assign(__assign({}, options), { context: scopeContext }) : options;
  208. }
  209. /**
  210. *
  211. * @param root Element(s) to search from.
  212. * @param selector Selector to look for.
  213. * @param options Options for querying.
  214. * @param queryForSelector Query multiple levels deep for the initial selector, even if it doesn't contain a traversal.
  215. * @param scopeContext Optional context for a :scope.
  216. */
  217. function findFilterElements(root, selector, options, queryForSelector, scopeContext) {
  218. var filterIndex = selector.findIndex(positionals_1.isFilter);
  219. var sub = selector.slice(0, filterIndex);
  220. var filter = selector[filterIndex];
  221. /*
  222. * Set the number of elements to retrieve.
  223. * Eg. for :first, we only have to get a single element.
  224. */
  225. var limit = positionals_1.getLimit(filter.name, filter.data);
  226. if (limit === 0)
  227. return [];
  228. var subOpts = addContextIfScope(sub, options, scopeContext);
  229. /*
  230. * Skip `findElements` call if our selector starts with a positional
  231. * pseudo.
  232. */
  233. var elemsNoLimit = sub.length === 0 && !Array.isArray(root)
  234. ? DomUtils.getChildren(root).filter(DomUtils.isTag)
  235. : sub.length === 0 || (sub.length === 1 && sub[0] === SCOPE_PSEUDO)
  236. ? (Array.isArray(root) ? root : [root]).filter(DomUtils.isTag)
  237. : queryForSelector || sub.some(css_what_1.isTraversal)
  238. ? findElements(root, [sub], subOpts, limit)
  239. : filterElements(root, [sub], subOpts);
  240. var elems = elemsNoLimit.slice(0, limit);
  241. var result = filterByPosition(filter.name, elems, filter.data, options);
  242. if (result.length === 0 || selector.length === filterIndex + 1) {
  243. return result;
  244. }
  245. var remainingSelector = selector.slice(filterIndex + 1);
  246. var remainingHasTraversal = remainingSelector.some(css_what_1.isTraversal);
  247. var remainingOpts = addContextIfScope(remainingSelector, options, scopeContext);
  248. if (remainingHasTraversal) {
  249. /*
  250. * Some types of traversals have special logic when they start a selector
  251. * in css-select. If this is the case, add a universal selector in front of
  252. * the selector to avoid this behavior.
  253. */
  254. if (specialTraversal.has(remainingSelector[0].type)) {
  255. remainingSelector.unshift(UNIVERSAL_SELECTOR);
  256. }
  257. /*
  258. * Add a scope token in front of the remaining selector,
  259. * to make sure traversals don't match elements that aren't a
  260. * part of the considered tree.
  261. */
  262. remainingSelector.unshift(SCOPE_PSEUDO);
  263. }
  264. /*
  265. * If we have another filter, recursively call `findFilterElements`,
  266. * with the `recursive` flag disabled. We only have to look for more
  267. * elements when we see a traversal.
  268. *
  269. * Otherwise,
  270. */
  271. return remainingSelector.some(positionals_1.isFilter)
  272. ? findFilterElements(result, remainingSelector, options, false, scopeContext)
  273. : remainingHasTraversal
  274. ? // Query existing elements to resolve traversal.
  275. findElements(result, [remainingSelector], remainingOpts, Infinity)
  276. : // If we don't have any more traversals, simply filter elements.
  277. filterElements(result, [remainingSelector], remainingOpts);
  278. }
  279. function findElements(root, sel, options, limit) {
  280. if (limit === 0)
  281. return [];
  282. var query = css_select_1._compileToken(sel, options, root);
  283. return find(root, query, limit);
  284. }
  285. function find(root, query, limit) {
  286. if (limit === void 0) { limit = Infinity; }
  287. var elems = css_select_1.prepareContext(root, DomUtils, query.shouldTestNextSiblings);
  288. return DomUtils.find(function (node) { return DomUtils.isTag(node) && query(node); }, elems, true, limit);
  289. }
  290. function filterElements(elements, sel, options) {
  291. var els = (Array.isArray(elements) ? elements : [elements]).filter(DomUtils.isTag);
  292. if (els.length === 0)
  293. return els;
  294. var query = css_select_1._compileToken(sel, options);
  295. return els.filter(query);
  296. }