traversing.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866
  1. "use strict";
  2. /**
  3. * Methods for traversing the DOM structure.
  4. *
  5. * @module cheerio/traversing
  6. */
  7. Object.defineProperty(exports, "__esModule", { value: true });
  8. exports.addBack = exports.add = exports.end = exports.slice = exports.index = exports.toArray = exports.get = exports.eq = exports.last = exports.first = exports.has = exports.not = exports.is = exports.filterArray = exports.filter = exports.map = exports.each = exports.contents = exports.children = exports.siblings = exports.prevUntil = exports.prevAll = exports.prev = exports.nextUntil = exports.nextAll = exports.next = exports.closest = exports.parentsUntil = exports.parents = exports.parent = exports.find = void 0;
  9. var tslib_1 = require("tslib");
  10. var domhandler_1 = require("domhandler");
  11. var select = tslib_1.__importStar(require("cheerio-select"));
  12. var utils_1 = require("../utils");
  13. var static_1 = require("../static");
  14. var htmlparser2_1 = require("htmlparser2");
  15. var uniqueSort = htmlparser2_1.DomUtils.uniqueSort;
  16. var reSiblingSelector = /^\s*[~+]/;
  17. /**
  18. * Get the descendants of each element in the current set of matched elements,
  19. * filtered by a selector, jQuery object, or element.
  20. *
  21. * @category Traversing
  22. * @example
  23. *
  24. * ```js
  25. * $('#fruits').find('li').length;
  26. * //=> 3
  27. * $('#fruits').find($('.apple')).length;
  28. * //=> 1
  29. * ```
  30. *
  31. * @param selectorOrHaystack - Element to look for.
  32. * @returns The found elements.
  33. * @see {@link https://api.jquery.com/find/}
  34. */
  35. function find(selectorOrHaystack) {
  36. var _a;
  37. if (!selectorOrHaystack) {
  38. return this._make([]);
  39. }
  40. var context = this.toArray();
  41. if (typeof selectorOrHaystack !== 'string') {
  42. var haystack = utils_1.isCheerio(selectorOrHaystack)
  43. ? selectorOrHaystack.toArray()
  44. : [selectorOrHaystack];
  45. return this._make(haystack.filter(function (elem) { return context.some(function (node) { return static_1.contains(node, elem); }); }));
  46. }
  47. var elems = reSiblingSelector.test(selectorOrHaystack)
  48. ? context
  49. : this.children().toArray();
  50. var options = {
  51. context: context,
  52. root: (_a = this._root) === null || _a === void 0 ? void 0 : _a[0],
  53. xmlMode: this.options.xmlMode,
  54. };
  55. return this._make(select.select(selectorOrHaystack, elems, options));
  56. }
  57. exports.find = find;
  58. /**
  59. * Creates a matcher, using a particular mapping function. Matchers provide a
  60. * function that finds elements using a generating function, supporting filtering.
  61. *
  62. * @private
  63. * @param matchMap - Mapping function.
  64. * @returns - Function for wrapping generating functions.
  65. */
  66. function _getMatcher(matchMap) {
  67. return function (fn) {
  68. var postFns = [];
  69. for (var _i = 1; _i < arguments.length; _i++) {
  70. postFns[_i - 1] = arguments[_i];
  71. }
  72. return function (selector) {
  73. var _a;
  74. var matched = matchMap(fn, this);
  75. if (selector) {
  76. matched = filterArray(matched, selector, this.options.xmlMode, (_a = this._root) === null || _a === void 0 ? void 0 : _a[0]);
  77. }
  78. return this._make(
  79. // Post processing is only necessary if there is more than one element.
  80. this.length > 1 && matched.length > 1
  81. ? postFns.reduce(function (elems, fn) { return fn(elems); }, matched)
  82. : matched);
  83. };
  84. };
  85. }
  86. /** Matcher that adds multiple elements for each entry in the input. */
  87. var _matcher = _getMatcher(function (fn, elems) {
  88. var _a;
  89. var ret = [];
  90. for (var i = 0; i < elems.length; i++) {
  91. var value = fn(elems[i]);
  92. ret.push(value);
  93. }
  94. return (_a = new Array()).concat.apply(_a, ret);
  95. });
  96. /** Matcher that adds at most one element for each entry in the input. */
  97. var _singleMatcher = _getMatcher(function (fn, elems) {
  98. var ret = [];
  99. for (var i = 0; i < elems.length; i++) {
  100. var value = fn(elems[i]);
  101. if (value !== null) {
  102. ret.push(value);
  103. }
  104. }
  105. return ret;
  106. });
  107. /**
  108. * Matcher that supports traversing until a condition is met.
  109. *
  110. * @returns A function usable for `*Until` methods.
  111. */
  112. function _matchUntil(nextElem) {
  113. var postFns = [];
  114. for (var _i = 1; _i < arguments.length; _i++) {
  115. postFns[_i - 1] = arguments[_i];
  116. }
  117. // We use a variable here that is used from within the matcher.
  118. var matches = null;
  119. var innerMatcher = _getMatcher(function (nextElem, elems) {
  120. var matched = [];
  121. utils_1.domEach(elems, function (elem) {
  122. for (var next_1; (next_1 = nextElem(elem)); elem = next_1) {
  123. // FIXME: `matched` might contain duplicates here and the index is too large.
  124. if (matches === null || matches === void 0 ? void 0 : matches(next_1, matched.length))
  125. break;
  126. matched.push(next_1);
  127. }
  128. });
  129. return matched;
  130. }).apply(void 0, tslib_1.__spreadArray([nextElem], postFns));
  131. return function (selector, filterSelector) {
  132. var _this = this;
  133. // Override `matches` variable with the new target.
  134. matches =
  135. typeof selector === 'string'
  136. ? function (elem) { return select.is(elem, selector, _this.options); }
  137. : selector
  138. ? getFilterFn(selector)
  139. : null;
  140. var ret = innerMatcher.call(this, filterSelector);
  141. // Set `matches` to `null`, so we don't waste memory.
  142. matches = null;
  143. return ret;
  144. };
  145. }
  146. function _removeDuplicates(elems) {
  147. return Array.from(new Set(elems));
  148. }
  149. /**
  150. * Get the parent of each element in the current set of matched elements,
  151. * optionally filtered by a selector.
  152. *
  153. * @category Traversing
  154. * @example
  155. *
  156. * ```js
  157. * $('.pear').parent().attr('id');
  158. * //=> fruits
  159. * ```
  160. *
  161. * @param selector - If specified filter for parent.
  162. * @returns The parents.
  163. * @see {@link https://api.jquery.com/parent/}
  164. */
  165. exports.parent = _singleMatcher(function (_a) {
  166. var parent = _a.parent;
  167. return (parent && !domhandler_1.isDocument(parent) ? parent : null);
  168. }, _removeDuplicates);
  169. /**
  170. * Get a set of parents filtered by `selector` of each element in the current
  171. * set of match elements.
  172. *
  173. * @category Traversing
  174. * @example
  175. *
  176. * ```js
  177. * $('.orange').parents().length;
  178. * //=> 2
  179. * $('.orange').parents('#fruits').length;
  180. * //=> 1
  181. * ```
  182. *
  183. * @param selector - If specified filter for parents.
  184. * @returns The parents.
  185. * @see {@link https://api.jquery.com/parents/}
  186. */
  187. exports.parents = _matcher(function (elem) {
  188. var matched = [];
  189. while (elem.parent && !domhandler_1.isDocument(elem.parent)) {
  190. matched.push(elem.parent);
  191. elem = elem.parent;
  192. }
  193. return matched;
  194. }, uniqueSort, function (elems) { return elems.reverse(); });
  195. /**
  196. * Get the ancestors of each element in the current set of matched elements, up
  197. * to but not including the element matched by the selector, DOM node, or cheerio object.
  198. *
  199. * @category Traversing
  200. * @example
  201. *
  202. * ```js
  203. * $('.orange').parentsUntil('#food').length;
  204. * //=> 1
  205. * ```
  206. *
  207. * @param selector - Selector for element to stop at.
  208. * @param filterSelector - Optional filter for parents.
  209. * @returns The parents.
  210. * @see {@link https://api.jquery.com/parentsUntil/}
  211. */
  212. exports.parentsUntil = _matchUntil(function (_a) {
  213. var parent = _a.parent;
  214. return (parent && !domhandler_1.isDocument(parent) ? parent : null);
  215. }, uniqueSort, function (elems) { return elems.reverse(); });
  216. /**
  217. * For each element in the set, get the first element that matches the selector
  218. * by testing the element itself and traversing up through its ancestors in the DOM tree.
  219. *
  220. * @category Traversing
  221. * @example
  222. *
  223. * ```js
  224. * $('.orange').closest();
  225. * //=> []
  226. *
  227. * $('.orange').closest('.apple');
  228. * // => []
  229. *
  230. * $('.orange').closest('li');
  231. * //=> [<li class="orange">Orange</li>]
  232. *
  233. * $('.orange').closest('#fruits');
  234. * //=> [<ul id="fruits"> ... </ul>]
  235. * ```
  236. *
  237. * @param selector - Selector for the element to find.
  238. * @returns The closest nodes.
  239. * @see {@link https://api.jquery.com/closest/}
  240. */
  241. function closest(selector) {
  242. var _this = this;
  243. var set = [];
  244. if (!selector) {
  245. return this._make(set);
  246. }
  247. utils_1.domEach(this, function (elem) {
  248. var _a;
  249. while (elem && elem.type !== 'root') {
  250. if (!selector ||
  251. filterArray([elem], selector, _this.options.xmlMode, (_a = _this._root) === null || _a === void 0 ? void 0 : _a[0])
  252. .length) {
  253. // Do not add duplicate elements to the set
  254. if (elem && !set.includes(elem)) {
  255. set.push(elem);
  256. }
  257. break;
  258. }
  259. elem = elem.parent;
  260. }
  261. });
  262. return this._make(set);
  263. }
  264. exports.closest = closest;
  265. /**
  266. * Gets the next sibling of the first selected element, optionally filtered by a selector.
  267. *
  268. * @category Traversing
  269. * @example
  270. *
  271. * ```js
  272. * $('.apple').next().hasClass('orange');
  273. * //=> true
  274. * ```
  275. *
  276. * @param selector - If specified filter for sibling.
  277. * @returns The next nodes.
  278. * @see {@link https://api.jquery.com/next/}
  279. */
  280. exports.next = _singleMatcher(function (elem) { return htmlparser2_1.DomUtils.nextElementSibling(elem); });
  281. /**
  282. * Gets all the following siblings of the first selected element, optionally
  283. * filtered by a selector.
  284. *
  285. * @category Traversing
  286. * @example
  287. *
  288. * ```js
  289. * $('.apple').nextAll();
  290. * //=> [<li class="orange">Orange</li>, <li class="pear">Pear</li>]
  291. * $('.apple').nextAll('.orange');
  292. * //=> [<li class="orange">Orange</li>]
  293. * ```
  294. *
  295. * @param selector - If specified filter for siblings.
  296. * @returns The next nodes.
  297. * @see {@link https://api.jquery.com/nextAll/}
  298. */
  299. exports.nextAll = _matcher(function (elem) {
  300. var matched = [];
  301. while (elem.next) {
  302. elem = elem.next;
  303. if (utils_1.isTag(elem))
  304. matched.push(elem);
  305. }
  306. return matched;
  307. }, _removeDuplicates);
  308. /**
  309. * Gets all the following siblings up to but not including the element matched
  310. * by the selector, optionally filtered by another selector.
  311. *
  312. * @category Traversing
  313. * @example
  314. *
  315. * ```js
  316. * $('.apple').nextUntil('.pear');
  317. * //=> [<li class="orange">Orange</li>]
  318. * ```
  319. *
  320. * @param selector - Selector for element to stop at.
  321. * @param filterSelector - If specified filter for siblings.
  322. * @returns The next nodes.
  323. * @see {@link https://api.jquery.com/nextUntil/}
  324. */
  325. exports.nextUntil = _matchUntil(function (el) { return htmlparser2_1.DomUtils.nextElementSibling(el); }, _removeDuplicates);
  326. /**
  327. * Gets the previous sibling of the first selected element optionally filtered
  328. * by a selector.
  329. *
  330. * @category Traversing
  331. * @example
  332. *
  333. * ```js
  334. * $('.orange').prev().hasClass('apple');
  335. * //=> true
  336. * ```
  337. *
  338. * @param selector - If specified filter for siblings.
  339. * @returns The previous nodes.
  340. * @see {@link https://api.jquery.com/prev/}
  341. */
  342. exports.prev = _singleMatcher(function (elem) { return htmlparser2_1.DomUtils.prevElementSibling(elem); });
  343. /**
  344. * Gets all the preceding siblings of the first selected element, optionally
  345. * filtered by a selector.
  346. *
  347. * @category Traversing
  348. * @example
  349. *
  350. * ```js
  351. * $('.pear').prevAll();
  352. * //=> [<li class="orange">Orange</li>, <li class="apple">Apple</li>]
  353. *
  354. * $('.pear').prevAll('.orange');
  355. * //=> [<li class="orange">Orange</li>]
  356. * ```
  357. *
  358. * @param selector - If specified filter for siblings.
  359. * @returns The previous nodes.
  360. * @see {@link https://api.jquery.com/prevAll/}
  361. */
  362. exports.prevAll = _matcher(function (elem) {
  363. var matched = [];
  364. while (elem.prev) {
  365. elem = elem.prev;
  366. if (utils_1.isTag(elem))
  367. matched.push(elem);
  368. }
  369. return matched;
  370. }, _removeDuplicates);
  371. /**
  372. * Gets all the preceding siblings up to but not including the element matched
  373. * by the selector, optionally filtered by another selector.
  374. *
  375. * @category Traversing
  376. * @example
  377. *
  378. * ```js
  379. * $('.pear').prevUntil('.apple');
  380. * //=> [<li class="orange">Orange</li>]
  381. * ```
  382. *
  383. * @param selector - Selector for element to stop at.
  384. * @param filterSelector - If specified filter for siblings.
  385. * @returns The previous nodes.
  386. * @see {@link https://api.jquery.com/prevUntil/}
  387. */
  388. exports.prevUntil = _matchUntil(function (el) { return htmlparser2_1.DomUtils.prevElementSibling(el); }, _removeDuplicates);
  389. /**
  390. * Get the siblings of each element (excluding the element) in the set of
  391. * matched elements, optionally filtered by a selector.
  392. *
  393. * @category Traversing
  394. * @example
  395. *
  396. * ```js
  397. * $('.pear').siblings().length;
  398. * //=> 2
  399. *
  400. * $('.pear').siblings('.orange').length;
  401. * //=> 1
  402. * ```
  403. *
  404. * @param selector - If specified filter for siblings.
  405. * @returns The siblings.
  406. * @see {@link https://api.jquery.com/siblings/}
  407. */
  408. exports.siblings = _matcher(function (elem) {
  409. return htmlparser2_1.DomUtils.getSiblings(elem).filter(function (el) { return utils_1.isTag(el) && el !== elem; });
  410. }, uniqueSort);
  411. /**
  412. * Gets the children of the first selected element.
  413. *
  414. * @category Traversing
  415. * @example
  416. *
  417. * ```js
  418. * $('#fruits').children().length;
  419. * //=> 3
  420. *
  421. * $('#fruits').children('.pear').text();
  422. * //=> Pear
  423. * ```
  424. *
  425. * @param selector - If specified filter for children.
  426. * @returns The children.
  427. * @see {@link https://api.jquery.com/children/}
  428. */
  429. exports.children = _matcher(function (elem) { return htmlparser2_1.DomUtils.getChildren(elem).filter(utils_1.isTag); }, _removeDuplicates);
  430. /**
  431. * Gets the children of each element in the set of matched elements, including
  432. * text and comment nodes.
  433. *
  434. * @category Traversing
  435. * @example
  436. *
  437. * ```js
  438. * $('#fruits').contents().length;
  439. * //=> 3
  440. * ```
  441. *
  442. * @returns The children.
  443. * @see {@link https://api.jquery.com/contents/}
  444. */
  445. function contents() {
  446. var elems = this.toArray().reduce(function (newElems, elem) {
  447. return domhandler_1.hasChildren(elem) ? newElems.concat(elem.children) : newElems;
  448. }, []);
  449. return this._make(elems);
  450. }
  451. exports.contents = contents;
  452. /**
  453. * Iterates over a cheerio object, executing a function for each matched
  454. * element. When the callback is fired, the function is fired in the context of
  455. * the DOM element, so `this` refers to the current element, which is equivalent
  456. * to the function parameter `element`. To break out of the `each` loop early,
  457. * return with `false`.
  458. *
  459. * @category Traversing
  460. * @example
  461. *
  462. * ```js
  463. * const fruits = [];
  464. *
  465. * $('li').each(function (i, elem) {
  466. * fruits[i] = $(this).text();
  467. * });
  468. *
  469. * fruits.join(', ');
  470. * //=> Apple, Orange, Pear
  471. * ```
  472. *
  473. * @param fn - Function to execute.
  474. * @returns The instance itself, useful for chaining.
  475. * @see {@link https://api.jquery.com/each/}
  476. */
  477. function each(fn) {
  478. var i = 0;
  479. var len = this.length;
  480. while (i < len && fn.call(this[i], i, this[i]) !== false)
  481. ++i;
  482. return this;
  483. }
  484. exports.each = each;
  485. /**
  486. * Pass each element in the current matched set through a function, producing a
  487. * new Cheerio object containing the return values. The function can return an
  488. * individual data item or an array of data items to be inserted into the
  489. * resulting set. If an array is returned, the elements inside the array are
  490. * inserted into the set. If the function returns null or undefined, no element
  491. * will be inserted.
  492. *
  493. * @category Traversing
  494. * @example
  495. *
  496. * ```js
  497. * $('li')
  498. * .map(function (i, el) {
  499. * // this === el
  500. * return $(this).text();
  501. * })
  502. * .toArray()
  503. * .join(' ');
  504. * //=> "apple orange pear"
  505. * ```
  506. *
  507. * @param fn - Function to execute.
  508. * @returns The mapped elements, wrapped in a Cheerio collection.
  509. * @see {@link https://api.jquery.com/map/}
  510. */
  511. function map(fn) {
  512. var elems = [];
  513. for (var i = 0; i < this.length; i++) {
  514. var el = this[i];
  515. var val = fn.call(el, i, el);
  516. if (val != null) {
  517. elems = elems.concat(val);
  518. }
  519. }
  520. return this._make(elems);
  521. }
  522. exports.map = map;
  523. /**
  524. * Creates a function to test if a filter is matched.
  525. *
  526. * @param match - A filter.
  527. * @returns A function that determines if a filter has been matched.
  528. */
  529. function getFilterFn(match) {
  530. if (typeof match === 'function') {
  531. return function (el, i) { return match.call(el, i, el); };
  532. }
  533. if (utils_1.isCheerio(match)) {
  534. return function (el) { return Array.prototype.includes.call(match, el); };
  535. }
  536. return function (el) {
  537. return match === el;
  538. };
  539. }
  540. function filter(match) {
  541. var _a;
  542. return this._make(filterArray(this.toArray(), match, this.options.xmlMode, (_a = this._root) === null || _a === void 0 ? void 0 : _a[0]));
  543. }
  544. exports.filter = filter;
  545. function filterArray(nodes, match, xmlMode, root) {
  546. return typeof match === 'string'
  547. ? select.filter(match, nodes, { xmlMode: xmlMode, root: root })
  548. : nodes.filter(getFilterFn(match));
  549. }
  550. exports.filterArray = filterArray;
  551. /**
  552. * Checks the current list of elements and returns `true` if *any* of the
  553. * elements match the selector. If using an element or Cheerio selection,
  554. * returns `true` if *any* of the elements match. If using a predicate function,
  555. * the function is executed in the context of the selected element, so `this`
  556. * refers to the current element.
  557. *
  558. * @category Attributes
  559. * @param selector - Selector for the selection.
  560. * @returns Whether or not the selector matches an element of the instance.
  561. * @see {@link https://api.jquery.com/is/}
  562. */
  563. function is(selector) {
  564. var nodes = this.toArray();
  565. return typeof selector === 'string'
  566. ? select.some(nodes.filter(utils_1.isTag), selector, this.options)
  567. : selector
  568. ? nodes.some(getFilterFn(selector))
  569. : false;
  570. }
  571. exports.is = is;
  572. /**
  573. * Remove elements from the set of matched elements. Given a Cheerio object that
  574. * represents a set of DOM elements, the `.not()` method constructs a new
  575. * Cheerio object from a subset of the matching elements. The supplied selector
  576. * is tested against each element; the elements that don't match the selector
  577. * will be included in the result.
  578. *
  579. * The `.not()` method can take a function as its argument in the same way that
  580. * `.filter()` does. Elements for which the function returns `true` are excluded
  581. * from the filtered set; all other elements are included.
  582. *
  583. * @category Traversing
  584. * @example <caption>Selector</caption>
  585. *
  586. * ```js
  587. * $('li').not('.apple').length;
  588. * //=> 2
  589. * ```
  590. *
  591. * @example <caption>Function</caption>
  592. *
  593. * ```js
  594. * $('li').not(function (i, el) {
  595. * // this === el
  596. * return $(this).attr('class') === 'orange';
  597. * }).length; //=> 2
  598. * ```
  599. *
  600. * @param match - Value to look for, following the rules above.
  601. * @param container - Optional node to filter instead.
  602. * @returns The filtered collection.
  603. * @see {@link https://api.jquery.com/not/}
  604. */
  605. function not(match) {
  606. var nodes = this.toArray();
  607. if (typeof match === 'string') {
  608. var matches_1 = new Set(select.filter(match, nodes, this.options));
  609. nodes = nodes.filter(function (el) { return !matches_1.has(el); });
  610. }
  611. else {
  612. var filterFn_1 = getFilterFn(match);
  613. nodes = nodes.filter(function (el, i) { return !filterFn_1(el, i); });
  614. }
  615. return this._make(nodes);
  616. }
  617. exports.not = not;
  618. /**
  619. * Filters the set of matched elements to only those which have the given DOM
  620. * element as a descendant or which have a descendant that matches the given
  621. * selector. Equivalent to `.filter(':has(selector)')`.
  622. *
  623. * @category Traversing
  624. * @example <caption>Selector</caption>
  625. *
  626. * ```js
  627. * $('ul').has('.pear').attr('id');
  628. * //=> fruits
  629. * ```
  630. *
  631. * @example <caption>Element</caption>
  632. *
  633. * ```js
  634. * $('ul').has($('.pear')[0]).attr('id');
  635. * //=> fruits
  636. * ```
  637. *
  638. * @param selectorOrHaystack - Element to look for.
  639. * @returns The filtered collection.
  640. * @see {@link https://api.jquery.com/has/}
  641. */
  642. function has(selectorOrHaystack) {
  643. var _this = this;
  644. return this.filter(typeof selectorOrHaystack === 'string'
  645. ? // Using the `:has` selector here short-circuits searches.
  646. ":has(" + selectorOrHaystack + ")"
  647. : function (_, el) { return _this._make(el).find(selectorOrHaystack).length > 0; });
  648. }
  649. exports.has = has;
  650. /**
  651. * Will select the first element of a cheerio object.
  652. *
  653. * @category Traversing
  654. * @example
  655. *
  656. * ```js
  657. * $('#fruits').children().first().text();
  658. * //=> Apple
  659. * ```
  660. *
  661. * @returns The first element.
  662. * @see {@link https://api.jquery.com/first/}
  663. */
  664. function first() {
  665. return this.length > 1 ? this._make(this[0]) : this;
  666. }
  667. exports.first = first;
  668. /**
  669. * Will select the last element of a cheerio object.
  670. *
  671. * @category Traversing
  672. * @example
  673. *
  674. * ```js
  675. * $('#fruits').children().last().text();
  676. * //=> Pear
  677. * ```
  678. *
  679. * @returns The last element.
  680. * @see {@link https://api.jquery.com/last/}
  681. */
  682. function last() {
  683. return this.length > 0 ? this._make(this[this.length - 1]) : this;
  684. }
  685. exports.last = last;
  686. /**
  687. * Reduce the set of matched elements to the one at the specified index. Use
  688. * `.eq(-i)` to count backwards from the last selected element.
  689. *
  690. * @category Traversing
  691. * @example
  692. *
  693. * ```js
  694. * $('li').eq(0).text();
  695. * //=> Apple
  696. *
  697. * $('li').eq(-1).text();
  698. * //=> Pear
  699. * ```
  700. *
  701. * @param i - Index of the element to select.
  702. * @returns The element at the `i`th position.
  703. * @see {@link https://api.jquery.com/eq/}
  704. */
  705. function eq(i) {
  706. var _a;
  707. i = +i;
  708. // Use the first identity optimization if possible
  709. if (i === 0 && this.length <= 1)
  710. return this;
  711. if (i < 0)
  712. i = this.length + i;
  713. return this._make((_a = this[i]) !== null && _a !== void 0 ? _a : []);
  714. }
  715. exports.eq = eq;
  716. function get(i) {
  717. if (i == null) {
  718. return this.toArray();
  719. }
  720. return this[i < 0 ? this.length + i : i];
  721. }
  722. exports.get = get;
  723. /**
  724. * Retrieve all the DOM elements contained in the jQuery set as an array.
  725. *
  726. * @example
  727. *
  728. * ```js
  729. * $('li').toArray();
  730. * //=> [ {...}, {...}, {...} ]
  731. * ```
  732. *
  733. * @returns The contained items.
  734. */
  735. function toArray() {
  736. return Array.prototype.slice.call(this);
  737. }
  738. exports.toArray = toArray;
  739. /**
  740. * Search for a given element from among the matched elements.
  741. *
  742. * @category Traversing
  743. * @example
  744. *
  745. * ```js
  746. * $('.pear').index();
  747. * //=> 2 $('.orange').index('li');
  748. * //=> 1
  749. * $('.apple').index($('#fruit, li'));
  750. * //=> 1
  751. * ```
  752. *
  753. * @param selectorOrNeedle - Element to look for.
  754. * @returns The index of the element.
  755. * @see {@link https://api.jquery.com/index/}
  756. */
  757. function index(selectorOrNeedle) {
  758. var $haystack;
  759. var needle;
  760. if (selectorOrNeedle == null) {
  761. $haystack = this.parent().children();
  762. needle = this[0];
  763. }
  764. else if (typeof selectorOrNeedle === 'string') {
  765. $haystack = this._make(selectorOrNeedle);
  766. needle = this[0];
  767. }
  768. else {
  769. $haystack = this;
  770. needle = utils_1.isCheerio(selectorOrNeedle)
  771. ? selectorOrNeedle[0]
  772. : selectorOrNeedle;
  773. }
  774. return Array.prototype.indexOf.call($haystack, needle);
  775. }
  776. exports.index = index;
  777. /**
  778. * Gets the elements matching the specified range (0-based position).
  779. *
  780. * @category Traversing
  781. * @example
  782. *
  783. * ```js
  784. * $('li').slice(1).eq(0).text();
  785. * //=> 'Orange'
  786. *
  787. * $('li').slice(1, 2).length;
  788. * //=> 1
  789. * ```
  790. *
  791. * @param start - An position at which the elements begin to be selected. If
  792. * negative, it indicates an offset from the end of the set.
  793. * @param end - An position at which the elements stop being selected. If
  794. * negative, it indicates an offset from the end of the set. If omitted, the
  795. * range continues until the end of the set.
  796. * @returns The elements matching the specified range.
  797. * @see {@link https://api.jquery.com/slice/}
  798. */
  799. function slice(start, end) {
  800. return this._make(Array.prototype.slice.call(this, start, end));
  801. }
  802. exports.slice = slice;
  803. /**
  804. * End the most recent filtering operation in the current chain and return the
  805. * set of matched elements to its previous state.
  806. *
  807. * @category Traversing
  808. * @example
  809. *
  810. * ```js
  811. * $('li').eq(0).end().length;
  812. * //=> 3
  813. * ```
  814. *
  815. * @returns The previous state of the set of matched elements.
  816. * @see {@link https://api.jquery.com/end/}
  817. */
  818. function end() {
  819. var _a;
  820. return (_a = this.prevObject) !== null && _a !== void 0 ? _a : this._make([]);
  821. }
  822. exports.end = end;
  823. /**
  824. * Add elements to the set of matched elements.
  825. *
  826. * @category Traversing
  827. * @example
  828. *
  829. * ```js
  830. * $('.apple').add('.orange').length;
  831. * //=> 2
  832. * ```
  833. *
  834. * @param other - Elements to add.
  835. * @param context - Optionally the context of the new selection.
  836. * @returns The combined set.
  837. * @see {@link https://api.jquery.com/add/}
  838. */
  839. function add(other, context) {
  840. var selection = this._make(other, context);
  841. var contents = uniqueSort(tslib_1.__spreadArray(tslib_1.__spreadArray([], this.get()), selection.get()));
  842. return this._make(contents);
  843. }
  844. exports.add = add;
  845. /**
  846. * Add the previous set of elements on the stack to the current set, optionally
  847. * filtered by a selector.
  848. *
  849. * @category Traversing
  850. * @example
  851. *
  852. * ```js
  853. * $('li').eq(0).addBack('.orange').length;
  854. * //=> 2
  855. * ```
  856. *
  857. * @param selector - Selector for the elements to add.
  858. * @returns The combined set.
  859. * @see {@link https://api.jquery.com/addBack/}
  860. */
  861. function addBack(selector) {
  862. return this.prevObject
  863. ? this.add(selector ? this.prevObject.filter(selector) : this.prevObject)
  864. : this;
  865. }
  866. exports.addBack = addBack;