')[0]);
- var $apple = $fruits.eq(0);
- var $orange = $fruits.eq(1);
- var $pear = $fruits.eq(2);
+ const $apple = $fruits.eq(0);
+ const $orange = $fruits.eq(1);
+ const $pear = $fruits.eq(2);
expect($apple.find('.third')[0]).toBe($apple.contents()[0]);
expect($orange.find('.third')[0]).toBe($orange.contents()[0]);
expect($pear.find('.third')[0]).toBe($pear.contents()[0]);
});
- it('($(...)) : should remove from root element', function () {
- var $plum = $('
Plum');
- var root = $plum[0].parent;
- expect(root.type).toBe('root');
+ it('($(...)) : should remove from root element', () => {
+ const $plum = $('
Plum');
+ const root = $plum[0].parent;
+ expect(root?.type).toBe('root');
$fruits.prepend($plum);
- expect($plum[0].parent.type).not.toBe('root');
- expect(root.childNodes).not.toContain($plum[0]);
+ expect($plum[0].parent?.type).not.toBe('root');
+ expect(root?.childNodes).not.toContain($plum[0]);
});
});
- describe('.appendTo', function () {
- it('(html) : should add element as last child', function () {
- var $plum = $('
Plum').appendTo(fruits);
+ describe('.appendTo', () => {
+ it('(html) : should add element as last child', () => {
+ const $plum = $('
Plum').appendTo(fruits);
expect($plum.parent().children().eq(3).hasClass('plum')).toBe(true);
});
- it('($(...)) : should add element as last child', function () {
+ it('($(...)) : should add element as last child', () => {
$('
Plum').appendTo($fruits);
expect($fruits.children().eq(3).hasClass('plum')).toBe(true);
});
- it('(Node) : should add element as last child', function () {
+ it('(Node) : should add element as last child', () => {
$('
Plum').appendTo($fruits[0]);
expect($fruits.children().eq(3).hasClass('plum')).toBe(true);
});
- it('(selector) : should add element as last child', function () {
+ it('(selector) : should add element as last child', () => {
$('
Plum').appendTo('#fruits');
expect($fruits.children().eq(3).hasClass('plum')).toBe(true);
});
- it('(Array) : should add element as last child of all elements in the array', function () {
- var $multiple = $('
');
+ it('(Array) : should add element as last child of all elements in the array', () => {
+ const $multiple = $('
');
$('
Plum').appendTo($multiple.get());
expect($multiple.first().children().eq(1).hasClass('plum')).toBe(true);
expect($multiple.last().children().eq(1).hasClass('plum')).toBe(true);
});
});
- describe('.prependTo', function () {
- it('(html) : should add element as first child', function () {
- var $plum = $('
Plum').prependTo(fruits);
+ describe('.prependTo', () => {
+ it('(html) : should add element as first child', () => {
+ const $plum = $('
Plum').prependTo(fruits);
expect($plum.parent().children().eq(0).hasClass('plum')).toBe(true);
});
- it('($(...)) : should add element as first child', function () {
+ it('($(...)) : should add element as first child', () => {
$('
Plum').prependTo($fruits);
expect($fruits.children().eq(0).hasClass('plum')).toBe(true);
});
- it('(Node) : should add node as first child', function () {
+ it('(Node) : should add node as first child', () => {
$('
Plum').prependTo($fruits[0]);
expect($fruits.children().eq(0).hasClass('plum')).toBe(true);
});
- it('(selector) : should add element as first child', function () {
+ it('(selector) : should add element as first child', () => {
$('
Plum').prependTo('#fruits');
expect($fruits.children().eq(0).hasClass('plum')).toBe(true);
});
- it('(Array) : should add element as first child of all elements in the array', function () {
- var $multiple = $('
');
+ it('(Array) : should add element as first child of all elements in the array', () => {
+ const $multiple = $('
');
$('
Plum').prependTo($multiple.get());
expect($multiple.first().children().eq(0).hasClass('plum')).toBe(true);
expect($multiple.last().children().eq(0).hasClass('plum')).toBe(true);
});
});
- describe('.after', function () {
- it('() : should do nothing', function () {
+ describe('.after', () => {
+ it('() : should do nothing', () => {
expect($fruits.after()[0].tagName).toBe('ul');
});
- it('(html) : should add element as next sibling', function () {
- var grape = '
Grape';
+ it('(html) : should add element as next sibling', () => {
+ const grape = '
Grape';
$('.apple').after(grape);
expect($('.apple').next().hasClass('grape')).toBe(true);
});
- it('(Array) : should add all elements in the array as next sibling', function () {
- var more = $(
+ it('(Array) : should add all elements in the array as next sibling', () => {
+ const more = $(
'
PlumGrape'
).get();
$('.apple').after(more);
@@ -935,36 +961,36 @@ describe('$(...)', function () {
expect($fruits.children().eq(2).hasClass('grape')).toBe(true);
});
- it('($(...)) : should add element as next sibling', function () {
- var $plum = $('
Plum');
+ it('($(...)) : should add element as next sibling', () => {
+ const $plum = $('
Plum');
$('.apple').after($plum);
expect($('.apple').next().hasClass('plum')).toBe(true);
});
- it('(Node) : should add element as next sibling', function () {
- var plum = $('
Plum')[0];
+ it('(Node) : should add element as next sibling', () => {
+ const plum = $('
Plum')[0];
$('.apple').after(plum);
expect($('.apple').next().hasClass('plum')).toBe(true);
});
- it('(existing Node) : should remove existing nodes from previous locations', function () {
- var pear = $fruits.children()[2];
+ it('(existing Node) : should remove existing nodes from previous locations', () => {
+ const pear = $fruits.children()[2];
$('.apple').after(pear);
- var $children = $fruits.children();
+ const $children = $fruits.children();
expect($children).toHaveLength(3);
expect($children[1]).toBe(pear);
});
- it('(existing Node) : should update original direct siblings', function () {
+ it('(existing Node) : should update original direct siblings', () => {
$('.pear').after($('.orange'));
expect($('.apple').next()[0]).toBe($('.pear')[0]);
expect($('.pear').prev()[0]).toBe($('.apple')[0]);
});
- it('(existing Node) : should clone all but the last occurrence', function () {
- var $originalApple = $('.apple');
+ it('(existing Node) : should clone all but the last occurrence', () => {
+ const $originalApple = $('.apple');
$('.orange, .pear').after($originalApple);
expect($('.apple')).toHaveLength(2);
@@ -976,31 +1002,32 @@ describe('$(...)', function () {
expect($('.apple')[1]).toStrictEqual($originalApple[0]);
});
- it('(elem) : should handle if removed', function () {
- var $apple = $('.apple');
- var $plum = $('
Plum');
+ it('(elem) : should handle if removed', () => {
+ const $apple = $('.apple');
+ const $plum = $('
Plum');
$apple.remove();
$apple.after($plum);
expect($plum.prev()).toHaveLength(0);
});
- it('($(...), html) : should add multiple elements as next siblings', function () {
- var $plum = $('
Plum');
- var grape = '
Grape';
+ it('($(...), html) : should add multiple elements as next siblings', () => {
+ const $plum = $('
Plum');
+ const grape = '
Grape';
$('.apple').after($plum, grape);
expect($('.apple').next().hasClass('plum')).toBe(true);
expect($('.plum').next().hasClass('grape')).toBe(true);
});
- it('(fn) : should invoke the callback with the correct arguments and context', function () {
- var args = [];
- var thisValues = [];
+ it('(fn) : should invoke the callback with the correct arguments and context', () => {
+ const args: [number, string][] = [];
+ const thisValues: Node[] = [];
$fruits = $fruits.children();
- $fruits.after(function () {
- args.push(Array.from(arguments));
+ $fruits.after(function (...myArgs) {
+ args.push(myArgs);
thisValues.push(this);
+ return this;
});
expect(args).toStrictEqual([
@@ -1011,96 +1038,102 @@ describe('$(...)', function () {
expect(thisValues).toStrictEqual([$fruits[0], $fruits[1], $fruits[2]]);
});
- it('(fn) : should add returned string as next sibling', function () {
+ it('(fn) : should add returned string as next sibling', () => {
$fruits = $fruits.children();
- $fruits.after(function () {
- return '
';
- });
+ $fruits.after(() => '');
expect($('.first')[0]).toBe($('#fruits').contents()[1]);
expect($('.first')[1]).toBe($('#fruits').contents()[3]);
expect($('.first')[2]).toBe($('#fruits').contents()[5]);
});
- it('(fn) : should add returned Cheerio object as next sibling', function () {
+ it('(fn) : should add returned Cheerio object as next sibling', () => {
$fruits = $fruits.children();
- $fruits.after(function () {
- return $('');
- });
+ $fruits.after(() => $(''));
expect($('.second')[0]).toBe($('#fruits').contents()[1]);
expect($('.second')[1]).toBe($('#fruits').contents()[3]);
expect($('.second')[2]).toBe($('#fruits').contents()[5]);
});
- it('(fn) : should add returned element as next sibling', function () {
+ it('(fn) : should add returned element as next sibling', () => {
$fruits = $fruits.children();
- $fruits.after(function () {
- return $('')[0];
- });
+ $fruits.after(() => $('')[0]);
expect($('.third')[0]).toBe($('#fruits').contents()[1]);
expect($('.third')[1]).toBe($('#fruits').contents()[3]);
expect($('.third')[2]).toBe($('#fruits').contents()[5]);
});
- it('($(...)) : should remove from root element', function () {
- var $plum = $('Plum');
- var root = $plum[0].parent;
- expect(root.type).toBe('root');
+ it('(fn) : should support text nodes', () => {
+ const $text = load(mixedText);
+
+ $text($text('body')[0].children).after(
+ (_, content) => `
${content}added`
+ );
+
+ expect($text('body').html()).toBe(
+ '
11addedTEXT
22added'
+ );
+ });
+
+ it('($(...)) : should remove from root element', () => {
+ const $plum = $('
Plum');
+ const root = $plum[0].parent;
+ expect(root?.type).toBe('root');
$fruits.after($plum);
- expect($plum[0].parent.type).not.toBe('root');
- expect(root.childNodes).not.toContain($plum[0]);
+ expect($plum[0].parent?.type).not.toBe('root');
+ expect(root?.childNodes).not.toContain($plum[0]);
});
});
- describe('.insertAfter', function () {
- it('(selector) : should create element and add as next sibling', function () {
- var grape = $('
Grape');
+ describe('.insertAfter', () => {
+ it('(selector) : should create element and add as next sibling', () => {
+ const grape = $('
Grape');
grape.insertAfter('.apple');
expect($('.apple').next().hasClass('grape')).toBe(true);
});
- it('(selector) : should create element and add as next sibling of multiple elements', function () {
- var grape = $('
Grape');
+ it('(selector) : should create element and add as next sibling of multiple elements', () => {
+ const grape = $('
Grape');
grape.insertAfter('.apple, .pear');
expect($('.apple').next().hasClass('grape')).toBe(true);
expect($('.pear').next().hasClass('grape')).toBe(true);
});
- it('($(...)) : should create element and add as next sibling', function () {
- var grape = $('
Grape');
+ it('($(...)) : should create element and add as next sibling', () => {
+ const grape = $('
Grape');
grape.insertAfter($('.apple'));
expect($('.apple').next().hasClass('grape')).toBe(true);
});
- it('($(...)) : should create element and add as next sibling of multiple elements', function () {
- var grape = $('
Grape');
+ it('($(...)) : should create element and add as next sibling of multiple elements', () => {
+ const grape = $('
Grape');
grape.insertAfter($('.apple, .pear'));
expect($('.apple').next().hasClass('grape')).toBe(true);
expect($('.pear').next().hasClass('grape')).toBe(true);
});
- it('($(...)) : should create all elements in the array and add as next siblings', function () {
- var more = $('
PlumGrape');
+ it('($(...)) : should create all elements in the array and add as next siblings', () => {
+ const more = $('
PlumGrape');
more.insertAfter($('.apple'));
expect($fruits.children().eq(0).hasClass('apple')).toBe(true);
expect($fruits.children().eq(1).hasClass('plum')).toBe(true);
expect($fruits.children().eq(2).hasClass('grape')).toBe(true);
});
- it('(existing Node) : should remove existing nodes from previous locations', function () {
+ it('(existing Node) : should remove existing nodes from previous locations', () => {
$('.orange').insertAfter('.pear');
expect($fruits.children().eq(1).hasClass('orange')).toBe(false);
expect($fruits.children().length).toBe(3);
expect($('.orange').length).toBe(1);
});
- it('(existing Node) : should update original direct siblings', function () {
+ it('(existing Node) : should update original direct siblings', () => {
$('.orange').insertAfter('.pear');
expect($('.apple').next().hasClass('pear')).toBe(true);
expect($('.pear').prev().hasClass('apple')).toBe(true);
@@ -1108,37 +1141,37 @@ describe('$(...)', function () {
expect($('.orange').next()).toHaveLength(0);
});
- it('(existing Node) : should update original direct siblings of multiple elements', function () {
+ it('(existing Node) : should update original direct siblings of multiple elements', () => {
$('.apple').insertAfter('.orange, .pear');
expect($('.orange').prev()).toHaveLength(0);
expect($('.orange').next().hasClass('apple')).toBe(true);
expect($('.pear').next().hasClass('apple')).toBe(true);
expect($('.pear').prev().hasClass('apple')).toBe(true);
expect($fruits.children().length).toBe(4);
- var apples = $('.apple');
+ const apples = $('.apple');
expect(apples.length).toBe(2);
expect(apples.eq(0).prev().hasClass('orange')).toBe(true);
expect(apples.eq(1).prev().hasClass('pear')).toBe(true);
});
- it('(elem) : should handle if removed', function () {
- var $apple = $('.apple');
- var $plum = $('
Plum');
+ it('(elem) : should handle if removed', () => {
+ const $apple = $('.apple');
+ const $plum = $('
Plum');
$apple.remove();
$plum.insertAfter($apple);
expect($plum.prev()).toHaveLength(0);
});
- it('(single) should return the new element for chaining', function () {
- var $grape = $('
Grape').insertAfter('.apple');
+ it('(single) should return the new element for chaining', () => {
+ const $grape = $('
Grape').insertAfter('.apple');
expect($grape.cheerio).toBeTruthy();
expect($grape.each).toBeTruthy();
expect($grape.length).toBe(1);
expect($grape.hasClass('grape')).toBe(true);
});
- it('(single) should return the new elements for chaining', function () {
- var $purple = $(
+ it('(single) should return the new elements for chaining', () => {
+ const $purple = $(
'
GrapePlum'
).insertAfter('.apple');
expect($purple.cheerio).toBeTruthy();
@@ -1148,8 +1181,8 @@ describe('$(...)', function () {
expect($purple.eq(1).hasClass('plum')).toBe(true);
});
- it('(multiple) should return the new elements for chaining', function () {
- var $purple = $(
+ it('(multiple) should return the new elements for chaining', () => {
+ const $purple = $(
'
GrapePlum'
).insertAfter('.apple, .pear');
expect($purple.cheerio).toBeTruthy();
@@ -1161,16 +1194,16 @@ describe('$(...)', function () {
expect($purple.eq(3).hasClass('plum')).toBe(true);
});
- it('(single) should return the existing element for chaining', function () {
- var $pear = $('.pear').insertAfter('.apple');
+ it('(single) should return the existing element for chaining', () => {
+ const $pear = $('.pear').insertAfter('.apple');
expect($pear.cheerio).toBeTruthy();
expect($pear.each).toBeTruthy();
expect($pear.length).toBe(1);
expect($pear.hasClass('pear')).toBe(true);
});
- it('(single) should return the existing elements for chaining', function () {
- var $things = $('.orange, .apple').insertAfter('.pear');
+ it('(single) should return the existing elements for chaining', () => {
+ const $things = $('.orange, .apple').insertAfter('.pear');
expect($things.cheerio).toBeTruthy();
expect($things.each).toBeTruthy();
expect($things.length).toBe(2);
@@ -1178,9 +1211,9 @@ describe('$(...)', function () {
expect($things.eq(1).hasClass('orange')).toBe(true);
});
- it('(multiple) should return the existing elements for chaining', function () {
+ it('(multiple) should return the existing elements for chaining', () => {
$('
Grape').insertAfter('.apple');
- var $things = $('.orange, .apple').insertAfter('.pear, .grape');
+ const $things = $('.orange, .apple').insertAfter('.pear, .grape');
expect($things.cheerio).toBeTruthy();
expect($things.each).toBeTruthy();
expect($things.length).toBe(4);
@@ -1191,47 +1224,47 @@ describe('$(...)', function () {
});
});
- describe('.before', function () {
- it('() : should do nothing', function () {
+ describe('.before', () => {
+ it('() : should do nothing', () => {
expect($('#fruits').before()[0].tagName).toBe('ul');
});
- it('(html) : should add element as previous sibling', function () {
- var grape = '
Grape';
+ it('(html) : should add element as previous sibling', () => {
+ const grape = '
Grape';
$('.apple').before(grape);
expect($('.apple').prev().hasClass('grape')).toBe(true);
});
- it('($(...)) : should add element as previous sibling', function () {
- var $plum = $('
Plum');
+ it('($(...)) : should add element as previous sibling', () => {
+ const $plum = $('
Plum');
$('.apple').before($plum);
expect($('.apple').prev().hasClass('plum')).toBe(true);
});
- it('(Node) : should add element as previous sibling', function () {
- var plum = $('
Plum')[0];
+ it('(Node) : should add element as previous sibling', () => {
+ const plum = $('
Plum')[0];
$('.apple').before(plum);
expect($('.apple').prev().hasClass('plum')).toBe(true);
});
- it('(existing Node) : should remove existing nodes from previous locations', function () {
- var pear = $fruits.children()[2];
+ it('(existing Node) : should remove existing nodes from previous locations', () => {
+ const pear = $fruits.children()[2];
$('.apple').before(pear);
- var $children = $fruits.children();
+ const $children = $fruits.children();
expect($children).toHaveLength(3);
expect($children[0]).toBe(pear);
});
- it('(existing Node) : should update original direct siblings', function () {
+ it('(existing Node) : should update original direct siblings', () => {
$('.apple').before($('.orange'));
expect($('.apple').next()[0]).toBe($('.pear')[0]);
expect($('.pear').prev()[0]).toBe($('.apple')[0]);
});
- it('(existing Node) : should clone all but the last occurrence', function () {
- var $originalPear = $('.pear');
+ it('(existing Node) : should clone all but the last occurrence', () => {
+ const $originalPear = $('.pear');
$('.apple, .orange').before($originalPear);
expect($('.pear')).toHaveLength(2);
@@ -1243,17 +1276,17 @@ describe('$(...)', function () {
expect($('.pear')[1]).toStrictEqual($originalPear[0]);
});
- it('(elem) : should handle if removed', function () {
- var $apple = $('.apple');
- var $plum = $('
Plum');
+ it('(elem) : should handle if removed', () => {
+ const $apple = $('.apple');
+ const $plum = $('
Plum');
$apple.remove();
$apple.before($plum);
expect($plum.next()).toHaveLength(0);
});
- it('(Array) : should add all elements in the array as previous sibling', function () {
- var more = $(
+ it('(Array) : should add all elements in the array as previous sibling', () => {
+ const more = $(
'
PlumGrape'
).get();
$('.apple').before(more);
@@ -1261,22 +1294,23 @@ describe('$(...)', function () {
expect($fruits.children().eq(1).hasClass('grape')).toBe(true);
});
- it('($(...), html) : should add multiple elements as previous siblings', function () {
- var $plum = $('
Plum');
- var grape = '
Grape';
+ it('($(...), html) : should add multiple elements as previous siblings', () => {
+ const $plum = $('
Plum');
+ const grape = '
Grape';
$('.apple').before($plum, grape);
expect($('.apple').prev().hasClass('grape')).toBe(true);
expect($('.grape').prev().hasClass('plum')).toBe(true);
});
- it('(fn) : should invoke the callback with the correct arguments and context', function () {
- var args = [];
- var thisValues = [];
+ it('(fn) : should invoke the callback with the correct arguments and context', () => {
+ const args: [number, string][] = [];
+ const thisValues: Node[] = [];
$fruits = $fruits.children();
- $fruits.before(function () {
- args.push(Array.from(arguments));
+ $fruits.before(function (...myArgs) {
+ args.push(myArgs);
thisValues.push(this);
+ return this;
});
expect(args).toStrictEqual([
@@ -1287,96 +1321,102 @@ describe('$(...)', function () {
expect(thisValues).toStrictEqual([$fruits[0], $fruits[1], $fruits[2]]);
});
- it('(fn) : should add returned string as previous sibling', function () {
+ it('(fn) : should add returned string as previous sibling', () => {
$fruits = $fruits.children();
- $fruits.before(function () {
- return '
';
- });
+ $fruits.before(() => '');
expect($('.first')[0]).toBe($('#fruits').contents()[0]);
expect($('.first')[1]).toBe($('#fruits').contents()[2]);
expect($('.first')[2]).toBe($('#fruits').contents()[4]);
});
- it('(fn) : should add returned Cheerio object as previous sibling', function () {
+ it('(fn) : should add returned Cheerio object as previous sibling', () => {
$fruits = $fruits.children();
- $fruits.before(function () {
- return $('');
- });
+ $fruits.before(() => $(''));
expect($('.second')[0]).toBe($('#fruits').contents()[0]);
expect($('.second')[1]).toBe($('#fruits').contents()[2]);
expect($('.second')[2]).toBe($('#fruits').contents()[4]);
});
- it('(fn) : should add returned Node as previous sibling', function () {
+ it('(fn) : should add returned Node as previous sibling', () => {
$fruits = $fruits.children();
- $fruits.before(function () {
- return $('')[0];
- });
+ $fruits.before(() => $('')[0]);
expect($('.third')[0]).toBe($('#fruits').contents()[0]);
expect($('.third')[1]).toBe($('#fruits').contents()[2]);
expect($('.third')[2]).toBe($('#fruits').contents()[4]);
});
- it('($(...)) : should remove from root element', function () {
- var $plum = $('Plum');
- var root = $plum[0].parent;
- expect(root.type).toBe('root');
+ it('(fn) : should support text nodes', () => {
+ const $text = load(mixedText);
+
+ $text($text('body')[0].children).before(
+ (_, content) => `
${content}added`
+ );
+
+ expect($text('body').html()).toBe(
+ '
1added1TEXT
2added2'
+ );
+ });
+
+ it('($(...)) : should remove from root element', () => {
+ const $plum = $('
Plum');
+ const root = $plum[0].parent;
+ expect(root?.type).toBe('root');
$fruits.before($plum);
- expect($plum[0].parent.type).not.toBe('root');
- expect(root.childNodes).not.toContain($plum[0]);
+ expect($plum[0].parent?.type).not.toBe('root');
+ expect(root?.childNodes).not.toContain($plum[0]);
});
});
- describe('.insertBefore', function () {
- it('(selector) : should create element and add as prev sibling', function () {
- var grape = $('
Grape');
+ describe('.insertBefore', () => {
+ it('(selector) : should create element and add as prev sibling', () => {
+ const grape = $('
Grape');
grape.insertBefore('.apple');
expect($('.apple').prev().hasClass('grape')).toBe(true);
});
- it('(selector) : should create element and add as prev sibling of multiple elements', function () {
- var grape = $('
Grape');
+ it('(selector) : should create element and add as prev sibling of multiple elements', () => {
+ const grape = $('
Grape');
grape.insertBefore('.apple, .pear');
expect($('.apple').prev().hasClass('grape')).toBe(true);
expect($('.pear').prev().hasClass('grape')).toBe(true);
});
- it('($(...)) : should create element and add as prev sibling', function () {
- var grape = $('
Grape');
+ it('($(...)) : should create element and add as prev sibling', () => {
+ const grape = $('
Grape');
grape.insertBefore($('.apple'));
expect($('.apple').prev().hasClass('grape')).toBe(true);
});
- it('($(...)) : should create element and add as next sibling of multiple elements', function () {
- var grape = $('
Grape');
+ it('($(...)) : should create element and add as next sibling of multiple elements', () => {
+ const grape = $('
Grape');
grape.insertBefore($('.apple, .pear'));
expect($('.apple').prev().hasClass('grape')).toBe(true);
expect($('.pear').prev().hasClass('grape')).toBe(true);
});
- it('($(...)) : should create all elements in the array and add as prev siblings', function () {
- var more = $('
PlumGrape');
+ it('($(...)) : should create all elements in the array and add as prev siblings', () => {
+ const more = $('
PlumGrape');
more.insertBefore($('.apple'));
expect($fruits.children().eq(0).hasClass('plum')).toBe(true);
expect($fruits.children().eq(1).hasClass('grape')).toBe(true);
expect($fruits.children().eq(2).hasClass('apple')).toBe(true);
});
- it('(existing Node) : should remove existing nodes from previous locations', function () {
+ it('(existing Node) : should remove existing nodes from previous locations', () => {
$('.pear').insertBefore('.apple');
expect($fruits.children().eq(2).hasClass('pear')).toBe(false);
expect($fruits.children().length).toBe(3);
expect($('.pear').length).toBe(1);
});
- it('(existing Node) : should update original direct siblings', function () {
+ it('(existing Node) : should update original direct siblings', () => {
$('.pear').insertBefore('.apple');
expect($('.apple').prev().hasClass('pear')).toBe(true);
expect($('.apple').next().hasClass('orange')).toBe(true);
@@ -1384,38 +1424,38 @@ describe('$(...)', function () {
expect($('.pear').prev()).toHaveLength(0);
});
- it('(existing Node) : should update original direct siblings of multiple elements', function () {
+ it('(existing Node) : should update original direct siblings of multiple elements', () => {
$('.pear').insertBefore('.apple, .orange');
expect($('.apple').prev().hasClass('pear')).toBe(true);
expect($('.apple').next().hasClass('pear')).toBe(true);
expect($('.orange').prev().hasClass('pear')).toBe(true);
expect($('.orange').next()).toHaveLength(0);
expect($fruits.children().length).toBe(4);
- var pears = $('.pear');
+ const pears = $('.pear');
expect(pears.length).toBe(2);
expect(pears.eq(0).next().hasClass('apple')).toBe(true);
expect(pears.eq(1).next().hasClass('orange')).toBe(true);
});
- it('(elem) : should handle if removed', function () {
- var $apple = $('.apple');
- var $plum = $('
Plum');
+ it('(elem) : should handle if removed', () => {
+ const $apple = $('.apple');
+ const $plum = $('
Plum');
$apple.remove();
$plum.insertBefore($apple);
expect($plum.next()).toHaveLength(0);
});
- it('(single) should return the new element for chaining', function () {
- var $grape = $('
Grape').insertBefore('.apple');
+ it('(single) should return the new element for chaining', () => {
+ const $grape = $('
Grape').insertBefore('.apple');
expect($grape.cheerio).toBeTruthy();
expect($grape.each).toBeTruthy();
expect($grape.length).toBe(1);
expect($grape.hasClass('grape')).toBe(true);
});
- it('(single) should return the new elements for chaining', function () {
- var $purple = $(
+ it('(single) should return the new elements for chaining', () => {
+ const $purple = $(
'
GrapePlum'
).insertBefore('.apple');
expect($purple.cheerio).toBeTruthy();
@@ -1425,8 +1465,8 @@ describe('$(...)', function () {
expect($purple.eq(1).hasClass('plum')).toBe(true);
});
- it('(multiple) should return the new elements for chaining', function () {
- var $purple = $(
+ it('(multiple) should return the new elements for chaining', () => {
+ const $purple = $(
'
GrapePlum'
).insertBefore('.apple, .pear');
expect($purple.cheerio).toBeTruthy();
@@ -1438,16 +1478,16 @@ describe('$(...)', function () {
expect($purple.eq(3).hasClass('plum')).toBe(true);
});
- it('(single) should return the existing element for chaining', function () {
- var $orange = $('.orange').insertBefore('.apple');
+ it('(single) should return the existing element for chaining', () => {
+ const $orange = $('.orange').insertBefore('.apple');
expect($orange.cheerio).toBeTruthy();
expect($orange.each).toBeTruthy();
expect($orange.length).toBe(1);
expect($orange.hasClass('orange')).toBe(true);
});
- it('(single) should return the existing elements for chaining', function () {
- var $things = $('.orange, .pear').insertBefore('.apple');
+ it('(single) should return the existing elements for chaining', () => {
+ const $things = $('.orange, .pear').insertBefore('.apple');
expect($things.cheerio).toBeTruthy();
expect($things.each).toBeTruthy();
expect($things.length).toBe(2);
@@ -1455,9 +1495,9 @@ describe('$(...)', function () {
expect($things.eq(1).hasClass('pear')).toBe(true);
});
- it('(multiple) should return the existing elements for chaining', function () {
+ it('(multiple) should return the existing elements for chaining', () => {
$('
Grape').insertBefore('.apple');
- var $things = $('.orange, .apple').insertBefore('.pear, .grape');
+ const $things = $('.orange, .apple').insertBefore('.pear, .grape');
expect($things.cheerio).toBeTruthy();
expect($things.each).toBeTruthy();
expect($things.length).toBe(4);
@@ -1468,38 +1508,38 @@ describe('$(...)', function () {
});
});
- describe('.remove', function () {
- it('() : should remove selected elements', function () {
+ describe('.remove', () => {
+ it('() : should remove selected elements', () => {
$('.apple').remove();
expect($fruits.find('.apple')).toHaveLength(0);
});
- it('() : should be reentrant', function () {
- var $apple = $('.apple');
+ it('() : should be reentrant', () => {
+ const $apple = $('.apple');
$apple.remove();
$apple.remove();
expect($fruits.find('.apple')).toHaveLength(0);
});
- it('(selector) : should remove matching selected elements', function () {
+ it('(selector) : should remove matching selected elements', () => {
$('li').remove('.apple');
expect($fruits.find('.apple')).toHaveLength(0);
});
- it('($(...)) : should remove from root element', function () {
- var $plum = $('
Plum');
- var root = $plum[0].parent;
- expect(root.type).toBe('root');
+ it('($(...)) : should remove from root element', () => {
+ const $plum = $('
Plum');
+ const root = $plum[0].parent;
+ expect(root?.type).toBe('root');
$plum.remove();
expect($plum[0].parent).toBe(null);
- expect(root.childNodes).not.toContain($plum[0]);
+ expect(root?.childNodes).not.toContain($plum[0]);
});
});
- describe('.replaceWith', function () {
- it('(elem) : should replace one
tag with another', function () {
- var $plum = $('Plum');
+ describe('.replaceWith', () => {
+ it('(elem) : should replace one
tag with another', () => {
+ const $plum = $('Plum');
$('.orange').replaceWith($plum);
expect($('.apple').next().hasClass('plum')).toBe(true);
expect($('.apple').next().html()).toBe('Plum');
@@ -1507,8 +1547,8 @@ describe('$(...)', function () {
expect($('.pear').prev().html()).toBe('Plum');
});
- it('(Array) : should replace one
tag with the elements in the array', function () {
- var more = $(
+ it('(Array) : should replace one tag with the elements in the array', () => {
+ const more = $(
'PlumGrape'
).get();
$('.orange').replaceWith(more);
@@ -1520,72 +1560,70 @@ describe('$(...)', function () {
expect($fruits.children()).toHaveLength(4);
});
- it('(Node) : should replace the selected element with given node', function () {
- var $src = $('
hi there
');
- var $new = $('
');
- var $replaced = $src.find('span').replaceWith($new[0]);
+ it('(Node) : should replace the selected element with given node', () => {
+ const $src = $('
hi there
');
+ const $new = $('
');
+ const $replaced = $src.find('span').replaceWith($new[0]);
expect($new[0].parentNode).toBe($src[0]);
expect($replaced[0].parentNode).toBe(null);
expect($.html($src)).toBe('
hi
');
});
- it('(existing element) : should remove element from its previous location', function () {
+ it('(existing element) : should remove element from its previous location', () => {
$('.pear').replaceWith($('.apple'));
expect($fruits.children()).toHaveLength(2);
expect($fruits.children()[0]).toBe($('.orange')[0]);
expect($fruits.children()[1]).toBe($('.apple')[0]);
});
- it('(elem) : should NOP if removed', function () {
- var $pear = $('.pear');
- var $plum = $('
Plum');
+ it('(elem) : should NOP if removed', () => {
+ const $pear = $('.pear');
+ const $plum = $('
Plum');
$pear.remove();
$pear.replaceWith($plum);
expect($('.orange').next().hasClass('plum')).toBe(false);
});
- it('(elem) : should replace the single selected element with given element', function () {
- var $src = $('
hi there
');
- var $new = $('
here
');
- var $replaced = $src.find('span').replaceWith($new);
+ it('(elem) : should replace the single selected element with given element', () => {
+ const $src = $('
hi there
');
+ const $new = $('
here
');
+ const $replaced = $src.find('span').replaceWith($new);
expect($new[0].parentNode).toBe($src[0]);
expect($replaced[0].parentNode).toBe(null);
expect($.html($src)).toBe('
hi
here
');
});
- it('(self) : should be replaced after replacing it with itself', function () {
- var $a = cheerio.load('
foo', null, false);
- var replacement = '
bar';
- $a('a').replaceWith(function (i, el) {
- return el;
- });
+ it('(self) : should be replaced after replacing it with itself', () => {
+ const $a = load('
foo', null, false);
+ const replacement = '
bar';
+ $a('a').replaceWith((_, el: Node) => el);
$a('a').replaceWith(replacement);
expect($a.html()).toBe(replacement);
});
- it('(str) : should accept strings', function () {
- var $src = $('
hi there
');
- var newStr = '
here
';
- var $replaced = $src.find('span').replaceWith(newStr);
+ it('(str) : should accept strings', () => {
+ const $src = $('
hi there
');
+ const newStr = '
here
';
+ const $replaced = $src.find('span').replaceWith(newStr);
expect($replaced[0].parentNode).toBe(null);
expect($.html($src)).toBe('
hi
here
');
});
- it('(str) : should replace all selected elements', function () {
- var $src = $('
a
b
c
d');
- var $replaced = $src.find('br').replaceWith(' ');
+ it('(str) : should replace all selected elements', () => {
+ const $src = $('
a
b
c
d');
+ const $replaced = $src.find('br').replaceWith(' ');
expect($replaced[0].parentNode).toBe(null);
expect($.html($src)).toBe('
a b c d');
});
- it('(fn) : should invoke the callback with the correct argument and context', function () {
- var origChildren = $fruits.children().get();
- var args = [];
- var thisValues = [];
+ it('(fn) : should invoke the callback with the correct argument and context', () => {
+ const origChildren = $fruits.children().get();
+ const args: [number, Node][] = [];
+ const thisValues: Node[] = [];
- $fruits.children().replaceWith(function () {
- args.push(Array.from(arguments));
+ $fruits.children().replaceWith(function (...myArgs) {
+ args.push(myArgs);
thisValues.push(this);
return '
';
});
@@ -1602,65 +1640,59 @@ describe('$(...)', function () {
]);
});
- it('(fn) : should replace the selected element with the returned string', function () {
- $fruits.children().replaceWith(function () {
- return '';
- });
+ it('(fn) : should replace the selected element with the returned string', () => {
+ $fruits.children().replaceWith(() => '');
expect($fruits.find('.first')).toHaveLength(3);
});
- it('(fn) : should replace the selected element with the returned Cheerio object', function () {
- $fruits.children().replaceWith(function () {
- return $('');
- });
+ it('(fn) : should replace the selected element with the returned Cheerio object', () => {
+ $fruits.children().replaceWith(() => $(''));
expect($fruits.find('.second')).toHaveLength(3);
});
- it('(fn) : should replace the selected element with the returned node', function () {
- $fruits.children().replaceWith(function () {
- return $('')[0];
- });
+ it('(fn) : should replace the selected element with the returned node', () => {
+ $fruits.children().replaceWith(() => $('')[0]);
expect($fruits.find('.third')).toHaveLength(3);
});
- it('($(...)) : should remove from root element', function () {
- var $plum = $('Plum');
- var root = $plum[0].parent;
- expect(root.type).toBe('root');
+ it('($(...)) : should remove from root element', () => {
+ const $plum = $('
Plum');
+ const root = $plum[0].parent;
+ expect(root?.type).toBe('root');
$fruits.children().replaceWith($plum);
- expect($plum[0].parent.type).not.toBe('root');
- expect(root.childNodes).not.toContain($plum[0]);
+ expect($plum[0].parent?.type).not.toBe('root');
+ expect(root?.childNodes).not.toContain($plum[0]);
});
});
- describe('.empty', function () {
- it('() : should remove all children from selected elements', function () {
+ describe('.empty', () => {
+ it('() : should remove all children from selected elements', () => {
expect($fruits.children()).toHaveLength(3);
$fruits.empty();
expect($fruits.children()).toHaveLength(0);
});
- it('() : should allow element reinsertion', function () {
- var $children = $fruits.children();
+ it('() : should allow element reinsertion', () => {
+ const $children = $fruits.children();
$fruits.empty();
expect($fruits.children()).toHaveLength(0);
expect($children).toHaveLength(3);
$fruits.append($('
'));
- var $remove = $fruits.children().eq(0);
+ const $remove = $fruits.children().eq(0);
$remove.replaceWith($children);
expect($fruits.children()).toHaveLength(4);
});
- it("() : should destroy children's references to the parent", function () {
- var $children = $fruits.children();
+ it("() : should destroy children's references to the parent", () => {
+ const $children = $fruits.children();
$fruits.empty();
@@ -1674,10 +1706,19 @@ describe('$(...)', function () {
expect($children.eq(2).next()).toHaveLength(0);
expect($children.eq(2).prev()).toHaveLength(0);
});
+
+ it('() : should skip text nodes', () => {
+ const $text = load(mixedText);
+ const $body = $text($text('body')[0].children);
+
+ $body.empty();
+
+ expect($text('body').html()).toBe('
TEXT
');
+ });
});
- describe('.html', function () {
- it('() : should get the innerHTML for an element', function () {
+ describe('.html', () => {
+ it('() : should get the innerHTML for an element', () => {
expect($fruits.html()).toBe(
[
'
Apple',
@@ -1687,26 +1728,26 @@ describe('$(...)', function () {
);
});
- it('() : should get innerHTML even if its just text', function () {
- var item = '
Pear';
+ it('() : should get innerHTML even if its just text', () => {
+ const item = '
Pear';
expect($('.pear', item).html()).toBe('Pear');
});
- it('() : should return empty string if nothing inside', function () {
- var item = '
';
+ it('() : should return empty string if nothing inside', () => {
+ const item = '
';
expect($('li', item).html()).toBe('');
});
- it('(html) : should set the html for its children', function () {
+ it('(html) : should set the html for its children', () => {
$fruits.html('
Durian');
- var html = $fruits.html();
+ const html = $fruits.html();
expect(html).toBe('
Durian');
});
- it('(html) : should add new elements for each element in selection', function () {
+ it('(html) : should add new elements for each element in selection', () => {
$fruits = $('li');
$fruits.html('
Durian');
- var tested = 0;
+ let tested = 0;
$fruits.each(function () {
expect($(this).children().parent().get(0)).toBe(this);
tested++;
@@ -1714,77 +1755,89 @@ describe('$(...)', function () {
expect(tested).toBe(3);
});
- it('(elem) : should set the html for its children with element', function () {
+ it('(html) : should skip text nodes', () => {
+ const $text = load(mixedText);
+ const $body = $text($text('body')[0].children);
+
+ $body.html('test');
+
+ expect($text('body').html()).toBe('
testTEXT
test');
+ });
+
+ it('(elem) : should set the html for its children with element', () => {
$fruits.html($('
Durian'));
- var html = $fruits.html();
+ const html = $fruits.html();
expect(html).toBe('
Durian');
});
- it('() : should allow element reinsertion', function () {
- var $children = $fruits.children();
+ it('() : should allow element reinsertion', () => {
+ const $children = $fruits.children();
$fruits.html('
');
expect($fruits.children()).toHaveLength(2);
- var $remove = $fruits.children().eq(0);
+ const $remove = $fruits.children().eq(0);
$remove.replaceWith($children);
expect($fruits.children()).toHaveLength(4);
});
- it('(script value) : should add content as text', function () {
- var $data = '
';
- var $script = $(' blah');
- expect($apple[0].childNodes[0].data).toBe(
+ expect($apple[0].childNodes[0]).toHaveProperty(
+ 'data',
'blah blah'
);
expect($apple.text()).toBe('blah blah');
@@ -1863,4 +1935,37 @@ describe('$(...)', function () {
expect($apple.html()).not.toContain('');
});
});
+
+ describe('.clone', () => {
+ it('() : should return a copy', () => {
+ const $src = $(
+ 'foobarbaz
'
+ ).children();
+ const $elem = $src.clone();
+ expect($elem.length).toBe(3);
+ expect($elem.parent()).toHaveLength(0);
+ expect($elem.text()).toBe($src.text());
+ $src.text('rofl');
+ expect($elem.text()).not.toBe($src.text());
+ });
+
+ it('() : should return a copy of document', () => {
+ const $src = load('foo
bar')
+ .root()
+ .children();
+ const $elem = $src.clone();
+ expect($elem.length).toBe(1);
+ expect($elem.parent()).toHaveLength(0);
+ expect($elem.text()).toBe($src.text());
+ $src.text('rofl');
+ expect($elem.text()).not.toBe($src.text());
+ });
+
+ it('() : should preserve parsing options', () => {
+ const $ = load('π
', { decodeEntities: false });
+ const $div = $('div');
+
+ expect($div.text()).toBe($div.clone().text());
+ });
+ });
});
diff --git a/src/api/manipulation.ts b/src/api/manipulation.ts
new file mode 100644
index 0000000000..5fd444da58
--- /dev/null
+++ b/src/api/manipulation.ts
@@ -0,0 +1,1033 @@
+import { hasChildren } from 'domhandler';
+/**
+ * Methods for modifying the DOM structure.
+ *
+ * @module cheerio/manipulation
+ */
+
+import { Node, NodeWithChildren, Element, Text } from 'domhandler';
+import { default as parse, update as updateDOM } from '../parse';
+import { html as staticHtml, text as staticText } from '../static';
+import { domEach, cloneDom, isTag, isHtml, isCheerio } from '../utils';
+import { DomUtils } from 'htmlparser2';
+import type { Cheerio } from '../cheerio';
+import type { BasicAcceptedElems, AcceptedElems } from '../types';
+
+/**
+ * Create an array of nodes, recursing into arrays and parsing strings if necessary.
+ *
+ * @private
+ * @category Manipulation
+ * @param elem - Elements to make an array of.
+ * @param clone - Optionally clone nodes.
+ * @returns The array of nodes.
+ */
+export function _makeDomArray(
+ this: Cheerio,
+ elem?: BasicAcceptedElems,
+ clone?: boolean
+): Node[] {
+ if (elem == null) {
+ return [];
+ }
+ if (isCheerio(elem)) {
+ return clone ? cloneDom(elem.get()) : elem.get();
+ }
+ if (Array.isArray(elem)) {
+ return elem.reduce(
+ (newElems, el) => newElems.concat(this._makeDomArray(el, clone)),
+ []
+ );
+ }
+ if (typeof elem === 'string') {
+ return parse(elem, this.options, false).children;
+ }
+ return clone ? cloneDom([elem]) : [elem];
+}
+
+function _insert(
+ concatenator: (
+ dom: Node[],
+ children: Node[],
+ parent: NodeWithChildren
+ ) => void
+) {
+ return function (
+ this: Cheerio,
+ ...elems:
+ | [(this: Node, i: number, html: string) => BasicAcceptedElems]
+ | BasicAcceptedElems[]
+ ) {
+ const lastIdx = this.length - 1;
+
+ return domEach(this, (i, el) => {
+ if (!hasChildren(el)) return;
+ const domSrc =
+ typeof elems[0] === 'function'
+ ? elems[0].call(el, i, staticHtml(el.children))
+ : (elems as Node[]);
+
+ const dom = this._makeDomArray(domSrc, i < lastIdx);
+ concatenator(dom, el.children, el);
+ });
+ };
+}
+
+/**
+ * Modify an array in-place, removing some number of elements and adding new
+ * elements directly following them.
+ *
+ * @private
+ * @category Manipulation
+ * @param array - Target array to splice.
+ * @param spliceIdx - Index at which to begin changing the array.
+ * @param spliceCount - Number of elements to remove from the array.
+ * @param newElems - Elements to insert into the array.
+ * @param parent - The parent of the node.
+ * @returns The spliced array.
+ */
+function uniqueSplice(
+ array: Node[],
+ spliceIdx: number,
+ spliceCount: number,
+ newElems: Node[],
+ parent: NodeWithChildren
+): Node[] {
+ const spliceArgs: Parameters = [
+ spliceIdx,
+ spliceCount,
+ ...newElems,
+ ];
+ const prev: Node | null = array[spliceIdx - 1] || null;
+ const next: Node | null = array[spliceIdx + spliceCount] || null;
+
+ /*
+ * Before splicing in new elements, ensure they do not already appear in the
+ * current array.
+ */
+ for (let idx = 0; idx < newElems.length; ++idx) {
+ const node = newElems[idx];
+ const oldParent = node.parent;
+
+ if (oldParent) {
+ const prevIdx = oldParent.children.indexOf(newElems[idx]);
+
+ if (prevIdx > -1) {
+ oldParent.children.splice(prevIdx, 1);
+ if (parent === oldParent && spliceIdx > prevIdx) {
+ spliceArgs[0]--;
+ }
+ }
+ }
+
+ node.parent = parent;
+
+ if (node.prev) {
+ node.prev.next = node.next ?? null;
+ }
+
+ if (node.next) {
+ node.next.prev = node.prev ?? null;
+ }
+
+ node.prev = newElems[idx - 1] || prev;
+ node.next = newElems[idx + 1] || next;
+ }
+
+ if (prev) {
+ prev.next = newElems[0];
+ }
+ if (next) {
+ next.prev = newElems[newElems.length - 1];
+ }
+ return array.splice(...spliceArgs);
+}
+
+/**
+ * Insert every element in the set of matched elements to the end of the target.
+ *
+ * @category Manipulation
+ * @example
+ *
+ * ```js
+ * $('Plum').appendTo('#fruits');
+ * $.html();
+ * //=>
+ * // - Apple
+ * // - Orange
+ * // - Pear
+ * // - Plum
+ * //
+ * ```
+ *
+ * @param target - Element to append elements to.
+ * @returns The instance itself.
+ * @see {@link https://api.jquery.com/appendTo/}
+ */
+export function appendTo(
+ this: Cheerio,
+ target: BasicAcceptedElems
+): Cheerio {
+ const appendTarget = isCheerio(target)
+ ? target
+ : this._make(target, null, this._originalRoot);
+
+ appendTarget.append(this);
+
+ return this;
+}
+
+/**
+ * Insert every element in the set of matched elements to the beginning of the target.
+ *
+ * @category Manipulation
+ * @example
+ *
+ * ```js
+ * $('Plum').prependTo('#fruits');
+ * $.html();
+ * //=>
+ * // - Plum
+ * // - Apple
+ * // - Orange
+ * // - Pear
+ * //
+ * ```
+ *
+ * @param target - Element to prepend elements to.
+ * @returns The instance itself.
+ * @see {@link https://api.jquery.com/prependTo/}
+ */
+export function prependTo(
+ this: Cheerio,
+ target: BasicAcceptedElems
+): Cheerio {
+ const prependTarget = isCheerio(target)
+ ? target
+ : this._make(target, null, this._originalRoot);
+
+ prependTarget.prepend(this);
+
+ return this;
+}
+
+/**
+ * Inserts content as the *last* child of each of the selected elements.
+ *
+ * @category Manipulation
+ * @example
+ *
+ * ```js
+ * $('ul').append('Plum');
+ * $.html();
+ * //=>
+ * // - Apple
+ * // - Orange
+ * // - Pear
+ * // - Plum
+ * //
+ * ```
+ *
+ * @see {@link https://api.jquery.com/append/}
+ */
+export const append = _insert((dom, children, parent) => {
+ uniqueSplice(children, children.length, 0, dom, parent);
+});
+
+/**
+ * Inserts content as the *first* child of each of the selected elements.
+ *
+ * @category Manipulation
+ * @example
+ *
+ * ```js
+ * $('ul').prepend('Plum');
+ * $.html();
+ * //=>
+ * // - Plum
+ * // - Apple
+ * // - Orange
+ * // - Pear
+ * //
+ * ```
+ *
+ * @see {@link https://api.jquery.com/prepend/}
+ */
+export const prepend = _insert((dom, children, parent) => {
+ uniqueSplice(children, 0, 0, dom, parent);
+});
+
+function _wrap(
+ insert: (
+ el: Node,
+ elInsertLocation: NodeWithChildren,
+ wrapperDom: NodeWithChildren[]
+ ) => void
+) {
+ return function (
+ this: Cheerio,
+ wrapper: AcceptedElems
+ ) {
+ const lastIdx = this.length - 1;
+ const lastParent = this.parents().last();
+
+ for (let i = 0; i < this.length; i++) {
+ const el = this[i];
+
+ const wrap =
+ typeof wrapper === 'function'
+ ? wrapper.call(el, i, el)
+ : typeof wrapper === 'string' && !isHtml(wrapper)
+ ? lastParent.find(wrapper).clone()
+ : wrapper;
+
+ const [wrapperDom] = this._makeDomArray(wrap, i < lastIdx);
+
+ if (!wrapperDom || !DomUtils.hasChildren(wrapperDom)) continue;
+
+ let elInsertLocation = wrapperDom;
+
+ /*
+ * Find the deepest child. Only consider the first tag child of each node
+ * (ignore text); stop if no children are found.
+ */
+ let j = 0;
+
+ while (j < elInsertLocation.children.length) {
+ const child = elInsertLocation.children[j];
+ if (isTag(child)) {
+ elInsertLocation = child;
+ j = 0;
+ } else {
+ j++;
+ }
+ }
+
+ insert(el, elInsertLocation, [wrapperDom]);
+ }
+
+ return this;
+ };
+}
+
+/**
+ * The .wrap() function can take any string or object that could be passed to
+ * the $() factory function to specify a DOM structure. This structure may be
+ * nested several levels deep, but should contain only one inmost element. A
+ * copy of this structure will be wrapped around each of the elements in the set
+ * of matched elements. This method returns the original set of elements for
+ * chaining purposes.
+ *
+ * @category Manipulation
+ * @example
+ *
+ * ```js
+ * const redFruit = $('');
+ * $('.apple').wrap(redFruit);
+ *
+ * //=>
+ * //
+ * //
- Apple
+ * //
+ * // - Orange
+ * // - Plum
+ * //
+ *
+ * const healthy = $('');
+ * $('li').wrap(healthy);
+ *
+ * //=>
+ * //
+ * //
- Apple
+ * //
+ * //
+ * //
- Orange
+ * //
+ * //
+ * //
- Plum
+ * //
+ * //
+ * ```
+ *
+ * @param wrapper - The DOM structure to wrap around each element in the selection.
+ * @see {@link https://api.jquery.com/wrap/}
+ */
+export const wrap = _wrap((el, elInsertLocation, wrapperDom) => {
+ const { parent } = el;
+
+ if (!parent) return;
+
+ const siblings = parent.children;
+ const index = siblings.indexOf(el);
+
+ updateDOM([el], elInsertLocation);
+ /*
+ * The previous operation removed the current element from the `siblings`
+ * array, so the `dom` array can be inserted without removing any
+ * additional elements.
+ */
+ uniqueSplice(siblings, index, 0, wrapperDom, parent);
+});
+
+/**
+ * The .wrapInner() function can take any string or object that could be passed
+ * to the $() factory function to specify a DOM structure. This structure may be
+ * nested several levels deep, but should contain only one inmost element. The
+ * structure will be wrapped around the content of each of the elements in the
+ * set of matched elements.
+ *
+ * @category Manipulation
+ * @example
+ *
+ * ```js
+ * const redFruit = $('');
+ * $('.apple').wrapInner(redFruit);
+ *
+ * //=>
+ * // -
+ * //
Apple
+ * //
+ * // - Orange
+ * // - Pear
+ * //
+ *
+ * const healthy = $('');
+ * $('li').wrapInner(healthy);
+ *
+ * //=>
+ * // -
+ * //
Apple
+ * //
+ * // -
+ * //
Orange
+ * //
+ * // -
+ * //
Pear
+ * //
+ * //
+ * ```
+ *
+ * @param wrapper - The DOM structure to wrap around the content of each element
+ * in the selection.
+ * @returns The instance itself, for chaining.
+ * @see {@link https://api.jquery.com/wrapInner/}
+ */
+export const wrapInner = _wrap((el, elInsertLocation, wrapperDom) => {
+ if (!hasChildren(el)) return;
+ updateDOM(el.children, elInsertLocation);
+ updateDOM(wrapperDom, el);
+});
+
+/**
+ * The .unwrap() function, removes the parents of the set of matched elements
+ * from the DOM, leaving the matched elements in their place.
+ *
+ * @category Manipulation
+ * @example without selector
+ *
+ * ```js
+ * const $ = cheerio.load(
+ * ''
+ * );
+ * $('#test p').unwrap();
+ *
+ * //=>
+ * //
Hello
+ * //
World
+ * //
+ * ```
+ *
+ * @example with selector
+ *
+ * ```js
+ * const $ = cheerio.load(
+ * ''
+ * );
+ * $('#test p').unwrap('b');
+ *
+ * //=>
+ * //
Hello
+ * //
World
+ * //
+ * ```
+ *
+ * @param selector - A selector to check the parent element against. If an
+ * element's parent does not match the selector, the element won't be unwrapped.
+ * @returns The instance itself, for chaining.
+ * @see {@link https://api.jquery.com/unwrap/}
+ */
+export function unwrap(
+ this: Cheerio,
+ selector?: string
+): Cheerio {
+ this.parent(selector)
+ .not('body')
+ .each((_, el) => {
+ this._make(el).replaceWith(el.children);
+ });
+ return this;
+}
+
+/**
+ * The .wrapAll() function can take any string or object that could be passed to
+ * the $() function to specify a DOM structure. This structure may be nested
+ * several levels deep, but should contain only one inmost element. The
+ * structure will be wrapped around all of the elements in the set of matched
+ * elements, as a single group.
+ *
+ * @category Manipulation
+ * @example With markup passed to `wrapAll`
+ *
+ * ```js
+ * const $ = cheerio.load(
+ * ''
+ * );
+ * $('.inner').wrapAll("");
+ *
+ * //=>