From 99ffffa424161f2bae5f6771a6664b50243eee07 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 01/10] update functions using wrapper function --- lib/api/traversing.js | 220 ++++++++++++++++------------------------- test/api/traversing.js | 27 ++++- 2 files changed, 109 insertions(+), 138 deletions(-) diff --git a/lib/api/traversing.js b/lib/api/traversing.js index e9e5c3b192..2d601bc6af 100644 --- a/lib/api/traversing.js +++ b/lib/api/traversing.js @@ -12,6 +12,47 @@ var uniqueSort = require('htmlparser2').DomUtils.uniqueSort; var isTag = utils.isTag; 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 (selector) { + matched = + typeof selector === 'string' + ? select.filter(selector, matched, this.options) + : uniqueSort(matched.filter(getFilterFn(selector))); + } else { + matched = uniqueSort(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. @@ -75,26 +116,10 @@ exports.find = function (selectorOrHaystack) { * * @returns {Cheerio} The parents. */ -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 @@ -111,26 +136,13 @@ exports.parent = function (selector) { * * @returns {Cheerio} The 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 @@ -241,25 +253,11 @@ exports.closest = function (selector) { * * @returns {Cheerio} The next nodes. */ -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 @@ -276,24 +274,13 @@ exports.next = function (selector) { * * @returns {Cheerio} The next nodes. */ -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 @@ -359,25 +346,11 @@ exports.nextUntil = function (selector, filterSelector) { * * @returns {Cheerio} The previous nodes. */ -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 @@ -394,24 +367,13 @@ exports.prev = function (selector) { * * @returns {Cheerio} The previous nodes. */ -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 @@ -479,20 +441,16 @@ exports.prevUntil = function (selector, filterSelector) { * * @returns {Cheerio} The 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. @@ -509,15 +467,9 @@ exports.siblings = function (selector) { * * @returns {Cheerio} The 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 424ca5d436..0f1b47e77b 100644 --- a/test/api/traversing.js +++ b/test/api/traversing.js @@ -225,6 +225,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); @@ -359,6 +364,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'); @@ -511,10 +521,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]); }); }); @@ -858,6 +868,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 5beb54c0ffa8b5200b311eb05755cb0950d36621 Mon Sep 17 00:00:00 2001 From: 5saviahv <5saviahv@users.noreply.github.com> Date: Sun, 10 Jan 2021 06:50:09 +0200 Subject: [PATCH 02/10] fix docs --- lib/api/traversing.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/api/traversing.js b/lib/api/traversing.js index 2d601bc6af..145abd744f 100644 --- a/lib/api/traversing.js +++ b/lib/api/traversing.js @@ -111,6 +111,7 @@ exports.find = function (selectorOrHaystack) { * $('.pear').parent().attr('id'); * //=> fruits * + * @function * @param {string} [selector] - If specified filter for parent. * @see {@link https://api.jquery.com/parent/} * @@ -131,6 +132,7 @@ exports.parent = _matcher(function (elem) { * $('.orange').parents('#fruits').length; * // => 1 * + * @function * @param {string} [selector] - If specified filter for parents. * @see {@link https://api.jquery.com/parents/} * @@ -248,6 +250,7 @@ exports.closest = function (selector) { * $('.apple').next().hasClass('orange'); * //=> true * + * @function * @param {string} [selector] - If specified filter for sibling. * @see {@link https://api.jquery.com/next/} * @@ -269,6 +272,7 @@ exports.next = _matcher(function (elem) { * $('.apple').nextAll('.orange'); * //=> [
  • Orange
  • ] * + * @function * @param {string} [selector] - If specified filter for siblings. * @see {@link https://api.jquery.com/nextAll/} * @@ -341,6 +345,7 @@ exports.nextUntil = function (selector, filterSelector) { * $('.orange').prev().hasClass('apple'); * //=> true * + * @function * @param {string} [selector] - If specified filter for siblings. * @see {@link https://api.jquery.com/prev/} * @@ -362,6 +367,7 @@ exports.prev = _matcher(function (elem) { * $('.pear').prevAll('.orange'); * //=> [
  • Orange
  • ] * + * @function * @param {string} [selector] - If specified filter for siblings. * @see {@link https://api.jquery.com/prevAll/} * @@ -436,6 +442,7 @@ exports.prevUntil = function (selector, filterSelector) { * $('.pear').siblings('.orange').length; * //=> 1 * + * @function * @param {string} [selector] - If specified filter for siblings. * @see {@link https://api.jquery.com/siblings/} * @@ -462,6 +469,7 @@ exports.siblings = _matcher(function (elem) { * $('#fruits').children('.pear').text(); * //=> Pear * + * @function * @param {string} [selector] - If specified filter for children. * @see {@link https://api.jquery.com/children/} * From 8e8e553d9180afb2b54fedb29852032c26a0df33 Mon Sep 17 00:00:00 2001 From: 5saviahv <5saviahv@users.noreply.github.com> Date: Wed, 27 Jan 2021 01:49:18 +0200 Subject: [PATCH 03/10] make condition clearer --- lib/api/traversing.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/api/traversing.js b/lib/api/traversing.js index 145abd744f..9c687bf139 100644 --- a/lib/api/traversing.js +++ b/lib/api/traversing.js @@ -33,13 +33,12 @@ function _matcher(fn, reverse) { var matched = matchMap(this, fn, selector); // select.filter uses uniqueSort already internally - if (selector) { - matched = - typeof selector === 'string' - ? select.filter(selector, matched, this.options) - : uniqueSort(matched.filter(getFilterFn(selector))); + if (typeof selector === 'string') { + matched = select.filter(selector, matched, this.options); } else { - matched = uniqueSort(matched); + matched = uniqueSort( + selector ? matched.filter(getFilterFn(selector)) : matched + ); } // Reverse order for parents* and prev-derivatives From aa2fea612b1343767fa604bda54a7144f2b5b575 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 04/10] added some tests --- test/__fixtures__/fixtures.js | 18 ++++++++++++++++++ test/api/traversing.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) 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 = [ '