traversing.js 26 KB

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