From 3ddae7eabba67f8cc0a7db203dd21a2e46b17282 Mon Sep 17 00:00:00 2001 From: 5saviahv <5saviahv@users.noreply.github.com> Date: Thu, 7 Jan 2021 22:50:13 +0200 Subject: [PATCH 1/8] update functions using wrapper function --- lib/api/traversing.js | 227 +++++++++++++++++------------------------ test/api/traversing.js | 27 ++++- 2 files changed, 116 insertions(+), 138 deletions(-) diff --git a/lib/api/traversing.js b/lib/api/traversing.js index 325d9072a2..3bd5e3741d 100644 --- a/lib/api/traversing.js +++ b/lib/api/traversing.js @@ -13,6 +13,46 @@ var isTag = utils.isTag; var slice = Array.prototype.slice; var reSiblingSelector = /^\s*[~+]/; +function _matcher(fn, reverse) { + // customized Map, discards null elements + function matchMap(elems, cb, arg) { + var len = elems.length; + var value; + var i = 0; + var ret = []; + for (; i < len; i++) { + value = cb(elems[i], i, arg); + if (value !== null) { + ret.push(value); + } + } + return Array.prototype.concat.apply([], ret); + } + + return function (selector) { + if (this[0]) { + var matched = matchMap(this, fn, selector); + + // select.filter uses uniqueSort already internally + if (typeof selector === 'string') { + matched = select.filter(selector, matched, this.options); + } else { + matched = uniqueSort( + selector ? matched.filter(getFilterFn(selector)) : matched + ); + } + + // Reverse order for parents* and prev-derivatives + if (reverse) { + matched.reverse(); + } + + return this._make(matched); + } + return this; + }; +} + /** * Get the descendants of each element in the current set of matched elements, * filtered by a selector, jQuery object, or element. @@ -70,30 +110,15 @@ exports.find = function (selectorOrHaystack) { * $('.pear').parent().attr('id'); * //=> fruits * + * @function * @param {string} [selector] - If specified filter for parent. * @returns {Cheerio} The parents. * @see {@link https://api.jquery.com/parent/} */ -exports.parent = function (selector) { - var set = []; - - domEach(this, function (_, elem) { - var parentElem = elem.parent; - if ( - parentElem && - parentElem.type !== 'root' && - set.indexOf(parentElem) < 0 - ) { - set.push(parentElem); - } - }); - - if (selector) { - set = exports.filter.call(set, selector, this); - } - - return this._make(set); -}; +exports.parent = _matcher(function (elem) { + var parent = elem.parent; + return parent && parent.type !== 'root' ? parent : null; +}); /** * Get a set of parents filtered by `selector` of each element in the current @@ -105,30 +130,18 @@ exports.parent = function (selector) { * $('.orange').parents('#fruits').length; * // => 1 * + * @function * @param {string} [selector] - If specified filter for parents. * @returns {Cheerio} The parents. * @see {@link https://api.jquery.com/parents/} */ -exports.parents = function (selector) { - var parentNodes = []; - - // When multiple DOM elements are in the original set, the resulting set will - // be in *reverse* order of the original elements as well, with duplicates - // removed. - this.get() - .reverse() - .forEach(function (elem) { - traverseParents(this, elem.parent, selector, Infinity).forEach(function ( - node - ) { - if (parentNodes.indexOf(node) === -1) { - parentNodes.push(node); - } - }); - }, this); - - return this._make(parentNodes); -}; +exports.parents = _matcher(function (elem) { + var matched = []; + while ((elem = elem.parent) && elem.type !== 'root') { + matched.push(elem); + } + return matched; +}, true); /** * Get the ancestors of each element in the current set of matched elements, up @@ -227,29 +240,16 @@ exports.closest = function (selector) { * $('.apple').next().hasClass('orange'); * //=> true * + * @function * @param {string} [selector] - If specified filter for sibling. * @returns {Cheerio} The next nodes. * @see {@link https://api.jquery.com/next/} */ -exports.next = function (selector) { - if (!this[0]) { - return this; - } - var elems = []; - - domEach(this, function (_, elem) { - while ((elem = elem.next)) { - if (isTag(elem)) { - elems.push(elem); - return; - } - } - }); - - return selector - ? exports.filter.call(elems, selector, this) - : this._make(elems); -}; +exports.next = _matcher(function (elem) { + // eslint-disable-next-line no-empty + while ((elem = elem.next) && !isTag(elem)) {} + return elem; +}); /** * Gets all the following siblings of the first selected element, optionally @@ -261,28 +261,18 @@ exports.next = function (selector) { * $('.apple').nextAll('.orange'); * //=> [
  • Orange
  • ] * + * @function * @param {string} [selector] - If specified filter for siblings. * @returns {Cheerio} The next nodes. * @see {@link https://api.jquery.com/nextAll/} */ -exports.nextAll = function (selector) { - if (!this[0]) { - return this; +exports.nextAll = _matcher(function (elem) { + var matched = []; + while ((elem = elem.next)) { + if (isTag(elem)) matched.push(elem); } - var elems = []; - - domEach(this, function (_, elem) { - while ((elem = elem.next)) { - if (isTag(elem) && elems.indexOf(elem) === -1) { - elems.push(elem); - } - } - }); - - return selector - ? exports.filter.call(elems, selector, this) - : this._make(elems); -}; + return matched; +}); /** * Gets all the following siblings up to but not including the element matched @@ -342,29 +332,16 @@ exports.nextUntil = function (selector, filterSelector) { * $('.orange').prev().hasClass('apple'); * //=> true * + * @function * @param {string} [selector] - If specified filter for siblings. * @returns {Cheerio} The previous nodes. * @see {@link https://api.jquery.com/prev/} */ -exports.prev = function (selector) { - if (!this[0]) { - return this; - } - var elems = []; - - domEach(this, function (_, elem) { - while ((elem = elem.prev)) { - if (isTag(elem)) { - elems.push(elem); - return; - } - } - }); - - return selector - ? exports.filter.call(elems, selector, this) - : this._make(elems); -}; +exports.prev = _matcher(function (elem) { + // eslint-disable-next-line no-empty + while ((elem = elem.prev) && !isTag(elem)) {} + return elem; +}, true); /** * Gets all the preceding siblings of the first selected element, optionally @@ -376,28 +353,18 @@ exports.prev = function (selector) { * $('.pear').prevAll('.orange'); * //=> [
  • Orange
  • ] * + * @function * @param {string} [selector] - If specified filter for siblings. * @returns {Cheerio} The previous nodes. * @see {@link https://api.jquery.com/prevAll/} */ -exports.prevAll = function (selector) { - if (!this[0]) { - return this; +exports.prevAll = _matcher(function (elem) { + var matched = []; + while ((elem = elem.prev)) { + if (isTag(elem)) matched.push(elem); } - var elems = []; - - domEach(this, function (_, elem) { - while ((elem = elem.prev)) { - if (isTag(elem) && elems.indexOf(elem) === -1) { - elems.push(elem); - } - } - }); - - return selector - ? exports.filter.call(elems, selector, this) - : this._make(elems); -}; + return matched; +}, true); /** * Gets all the preceding siblings up to but not including the element matched @@ -459,24 +426,21 @@ exports.prevUntil = function (selector, filterSelector) { * $('.pear').siblings('.orange').length; * //=> 1 * + * @function * @param {string} [selector] - If specified filter for siblings. * @returns {Cheerio} The siblings. * @see {@link https://api.jquery.com/siblings/} */ -exports.siblings = function (selector) { - var parent = this.parent(); - - var elems = (parent ? parent.children() : this.siblingsAndMe()) - .toArray() - .filter(function (elem) { - return isTag(elem) && !this.is(elem); - }, this); - - if (selector !== undefined) { - return exports.filter.call(elems, selector, this); +exports.siblings = _matcher(function (elem) { + var node = elem.parent && elem.parent.firstChild; + var matched = []; + for (; node; node = node.next) { + if (isTag(node) && node !== elem) { + matched.push(node); + } } - return this._make(elems); -}; + return matched; +}); /** * Gets the children of the first selected element. @@ -488,19 +452,14 @@ exports.siblings = function (selector) { * $('#fruits').children('.pear').text(); * //=> Pear * + * @function * @param {string} [selector] - If specified filter for children. * @returns {Cheerio} The children. * @see {@link https://api.jquery.com/children/} */ -exports.children = function (selector) { - var elems = this.toArray().reduce(function (newElems, elem) { - return newElems.concat(elem.children.filter(isTag)); - }, []); - - if (selector === undefined) return this._make(elems); - - return exports.filter.call(elems, selector, this); -}; +exports.children = _matcher(function (elem) { + return elem.children.filter(isTag); +}); /** * Gets the children of each element in the set of matched elements, including diff --git a/test/api/traversing.js b/test/api/traversing.js index be91bdbf3c..977f477de0 100644 --- a/test/api/traversing.js +++ b/test/api/traversing.js @@ -226,6 +226,11 @@ describe('$(...)', function () { expect(elems.nextAll()).toHaveLength(2); }); + it('() : should not contain text elements', function () { + var elems = $('.apple', fruits.replace(/>\n<')); + expect(elems.nextAll()).toHaveLength(2); + }); + describe('(selector) :', function () { it('should filter according to the provided selector', function () { expect($('.apple').nextAll('.pear')).toHaveLength(1); @@ -367,6 +372,11 @@ describe('$(...)', function () { expect(elems.prevAll()).toHaveLength(2); }); + it('() : should not contain text elements', function () { + var elems = $('.pear', fruits.replace(/>\n<')); + expect(elems.prevAll()).toHaveLength(2); + }); + describe('(selector) :', function () { it('should filter returned elements', function () { var elems = $('.pear').prevAll('.apple'); @@ -526,10 +536,10 @@ describe('$(...)', function () { expect($parents).toHaveLength(5); expect($parents[0]).toBe($('#vegetables')[0]); - expect($parents[1]).toBe($('#food')[0]); - expect($parents[2]).toBe($('body')[0]); - expect($parents[3]).toBe($('html')[0]); - expect($parents[4]).toBe($('#fruits')[0]); + expect($parents[2]).toBe($('#food')[0]); + expect($parents[3]).toBe($('body')[0]); + expect($parents[4]).toBe($('html')[0]); + expect($parents[1]).toBe($('#fruits')[0]); }); }); @@ -886,6 +896,15 @@ describe('$(...)', function () { expect($notOrange[0]).toBe($fruits[0]); expect($notOrange[1]).toBe($fruits[2]); }); + + it('(selector, container) : should reduce the set of matched elements to those that are mot contained in the provided selection', function () { + var $fruits = $('li'); + var $notOrange = $('ul').not('.orange', $fruits); + + expect($notOrange).toHaveLength(2); + expect($notOrange[0]).toBe($fruits[0]); + expect($notOrange[1]).toBe($fruits[2]); + }); }); describe('.has', function () { From 0f5a8f22d9624bcf118f863adcf8ce385a366b56 Mon Sep 17 00:00:00 2001 From: 5saviahv <5saviahv@users.noreply.github.com> Date: Wed, 27 Jan 2021 05:34:21 +0200 Subject: [PATCH 2/8] added some tests --- test/__fixtures__/fixtures.js | 18 +++++++++ test/api/traversing.js | 69 ++++++++++++++++++++++++++++------- 2 files changed, 73 insertions(+), 14 deletions(-) diff --git a/test/__fixtures__/fixtures.js b/test/__fixtures__/fixtures.js index e232515c1c..7d00e5715b 100644 --- a/test/__fixtures__/fixtures.js +++ b/test/__fixtures__/fixtures.js @@ -46,6 +46,24 @@ exports.drinks = [ '', ].join(''); +exports.eleven = [ + '\n\n\n\n\n\n\n\n', +].join('\n'); + exports.food = [ '