traversing.js 26 KB

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