Socket
Socket
Sign inDemoInstall

cheerio

Package Overview
Dependencies
8
Maintainers
4
Versions
69
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.0.0-rc.3 to 1.0.0-rc.4

types/index.d.ts

1033

History.md

@@ -1,9 +0,10 @@

1.0.0-rc.3 / 2019-04-06
==================
Starting with 1.0.0-rc.4, release notes are exclusively tracked in
[Github Releases](https://github.com/cheeriojs/cheerio/releases).
# 1.0.0-rc.3 / 2019-04-06
This release corrects a test expectation that was fixed by one of the project's
dependencies.
1.0.0-rc.2 / 2017-07-02
==================
# 1.0.0-rc.2 / 2017-07-02

@@ -26,628 +27,606 @@ This release changes Cheerio's default parser to [the Parse5 HTML

`cheerio.load( html[, options ] )`` This method continues to produce
jQuery-like functions, bound to the provided input text. In prior releases, the
provided string was interpreted as a document fragment. This meant that in a
statement such as:
`cheerio.load( html[, options ] )` This method continues to act as a "factory"
function. It produces functions that define an API that is similar to the
global `jQuery` function provided by the jQuery library. The generated function
operates on a DOM structure based on the provided HTML.
In releases prior to version 1.0, the provided HTML was interpreted as a
document fragment. Following version 1.0, strings provided to the `load` method
are interpreted as documents. The same example will produce a `$` function that
operates on a full HTML document, including an `<html>` document element with
nested `<head>` and `<body>` tags. This mimics web browser behavior much more
closely, but may require alterations to existing code.
For example, the following code will produce different results between 0.x and
1.0 releases:
var $ = cheerio.load('<p>Hello, <b>world</b>!</p>');
The resulting `$` function would operate on a tree whose root element was a
paragraph tag.
$.root().html();
With this release of Cheerio, strings provided to the `load` method are
interpreted as documents. The same example will produce a `$` function that
operates on a full HTML document, including an `<html>` document element with
nested `<head>` and `<body>` tags. This mimics web browser behavior much more
closely, but may require alterations to existing code. To work with fragments,
first load an empty document, and then use the resulting `$` to parse the
input:
//=> In version 0.x: '<p>Hello, <b>world</b>!</p>'
//=> In version 1.0: '<html><head></head><body><p>Hello, <b>world</b>!</p></body></html>'
Users wishing to parse, manipulate, and render full documents should not need
to modify their code. Likewise, code that does not interact with the "root"
element should not be effected by this change. (In the above example, the
expression `$('p')` returns the same result across Cheerio versions--a Cheerio
collection whose only member is a paragraph element.)
However, users wishing to render document fragments should now explicitly
create a "wrapper" element to contain their input.
// First, create a Cheerio function "bound" to an empty document (this is
// similar to loading an empty page in a web browser)
var $ = cheerio.load('');
var $fragment = $('<p>Hello, <b>world</b>!</p>');
// Next, create a "wrapper" element for the input fragment:
var $wrapper = $('<div/>');
// Finally, supply the input markup as the content for the wrapper:
$wrapper.append('<p>Hello, <b>world</b>!</p>');
* Update History.md (and include migration guide) (Mike Pennisi)
* Rename `useHtmlParser2` option (Mike Pennisi)
* Remove documentation for `xmlMode` option (Mike Pennisi)
* Document advanced usage with htmlparser2 (Mike Pennisi)
* Correct errors in Readme.md (Mike Pennisi)
* Improve release process (Mike Pennisi)
* 1.0.0-rc.1 (Mike Pennisi)
* Update unit test (Mike Pennisi)
* Normalize code style (Mike Pennisi)
* Added support for nested wrapping. (Diane Looney)
* Add nested wrapping test (Toni Helenius)
* Added $.merge following the specification at https://api.jquery.com/jquery.merge/ Added test cases for $.merge (Diane Looney)
* Clarify project scope in README file (Mike Pennisi)
* .text() ignores script and style tags (#1018) (Haleem Assal)
* Test suite housekeeping (#1016) (DianeLooney)
* experiment with a job board (Matthew)
* Change options format (inikulin)
* Add test for #997 (inikulin)
* Update .filter function docs. (Konstantin)
* Standardise readme on ES6 variable declarations (Dekatron)
* Use documents via $.load (inikulin)
* Use parse5 as a default parser (closes #863) (inikulin)
* Fix small typo in Readme (Darren Scerri)
* Report test failures in CI (Mike Pennisi)
* serializeArray should not ignore input elements without value attributes (Ricardo Gladwell)
* Disallow variable shadowing (Mike Pennisi)
* Update hasClass method (sufisaid)
* Added MIT License fixes #902 (Prasanth Vaaheeswaran)
* chore(package): update dependencies (greenkeeper[bot])
* Use modular lodash package (#913) (Billy Janitsch)
$wrapper.html();
//=> '<p>Hello, <b>world</b>!</p>'
0.22.0 / 2016-08-23
==================
---
* Return undefined in .prop if given an invalid element or tag (#880)
* Merge pull request #884 from cheeriojs/readme-cleanup
* readme updates
* Merge pull request #881 from piamancini/patch-1
* Added backers and sponsors from OpenCollective
* Use jQuery from the jquery module in benchmarks (#871)
* Document, test, and extend static `$.text` method (#855)
* Fix typo on calling _.extend (#861)
* Update versions (#870)
* Use individual lodash functions (#864)
* Added `.serialize()` support. Fixes #69 (#827)
* Update Readme.md (#857)
* add extension for JSON require call
* remove gittask badge
* Merge pull request #672 from underdogio/dev/checkbox.radio.values.sqwished
* Added default value for checkboxes/radios
Change log:
0.20.0 / 2016-02-01
==================
- Update History.md (and include migration guide) (Mike Pennisi)
- Rename `useHtmlParser2` option (Mike Pennisi)
- Remove documentation for `xmlMode` option (Mike Pennisi)
- Document advanced usage with htmlparser2 (Mike Pennisi)
- Correct errors in Readme.md (Mike Pennisi)
- Improve release process (Mike Pennisi)
- 1.0.0-rc.1 (Mike Pennisi)
- Update unit test (Mike Pennisi)
- Normalize code style (Mike Pennisi)
- Added support for nested wrapping. (Diane Looney)
- Add nested wrapping test (Toni Helenius)
- Added $.merge following the specification at https://api.jquery.com/jquery.merge/ Added test cases for $.merge (Diane Looney)
- Clarify project scope in README file (Mike Pennisi)
- .text() ignores script and style tags (#1018) (Haleem Assal)
- Test suite housekeeping (#1016) (DianeLooney)
- experiment with a job board (Matthew)
- Change options format (inikulin)
- Add test for #997 (inikulin)
- Update .filter function docs. (Konstantin)
- Standardise readme on ES6 variable declarations (Dekatron)
- Use documents via \$.load (inikulin)
- Use parse5 as a default parser (closes #863) (inikulin)
- Fix small typo in Readme (Darren Scerri)
- Report test failures in CI (Mike Pennisi)
- serializeArray should not ignore input elements without value attributes (Ricardo Gladwell)
- Disallow variable shadowing (Mike Pennisi)
- Update hasClass method (sufisaid)
- Added MIT License fixes #902 (Prasanth Vaaheeswaran)
- chore(package): update dependencies (greenkeeper[bot])
- Use modular lodash package (#913) (Billy Janitsch)
* Add coveralls badge, remove link to old report (Felix Böhm)
* Update lodash dependeny to 4.1.0 (leif.hanack)
* Fix PR #726 adding 'appendTo()' and 'prependTo()' (Delgan)
* Added appendTo and prependTo with tests #641 (digihaven)
* Fix #780 by changing options context in '.find()' (Felix Böhm)
* Add an unit test checking the query of child (Delgan)
* fix #667: attr({foo: null}) removes attribute foo, like attr('foo', null) (Ray Waldin)
* Include reference to dedicated "Loading" section (Mike Pennisi)
* Added load method to $ (alanev)
* update css-select to 1.2.0 (Felix Böhm)
* Fixing Grammatical Error (Dan Corman)
* Test against node v0.12 --> v4.2 (Jason Kurian)
* Correct output in example (Felix Böhm)
* Fix npm files filter (Bogdan Chadkin)
* Enable setting data on all elements in selection (Mike Pennisi)
* Reinstate `$.fn.toArray` (Mike Pennisi)
* update css-select to 1.1.0 (Thomas Shafer)
* Complete implementation of `wrap` (Mike Pennisi)
* Correct name of unit test (Mike Pennisi)
* Correct grammar in test titles (Mike Pennisi)
* Normalize whitespace (Mike Pennisi)
* Insert omitted assertion (Mike Pennisi)
* Update invocation of `children` (Mike Pennisi)
* Begin implementation of `wrap` method (Dandlezzz)
* Update Readme.md (Sven Slootweg)
* fix document's mistake in Readme.md (exoticknight)
* Add tests for setting text and html as non-strings (Ryc O'Chet)
* Fix for passing non-string values to .html or .text (Ryc O'Chet)
* use a selector to filter form elements (fb55)
* fix README.md typo (Yutian Li)
* README: fix spelling (Chris Rebert)
* Added support for options without a `value` attribute. Fixes #633 (Todd Wolfson)
* responding to pull request feedback - remove item() method and related tests (Ray Waldin)
* add length property and item method to object returned by prop('style'), plus tests (Ray Waldin)
* Added .prop method to readme (Artem Burtsev)
* Added .prop method (Artem Burtsev)
* Added Gitter badge (The Gitter Badger)
# 0.22.0 / 2016-08-23
0.19.0 / 2015-03-21
==================
- Return undefined in .prop if given an invalid element or tag (#880)
- Merge pull request #884 from cheeriojs/readme-cleanup
- readme updates
- Merge pull request #881 from piamancini/patch-1
- Added backers and sponsors from OpenCollective
- Use jQuery from the jquery module in benchmarks (#871)
- Document, test, and extend static `$.text` method (#855)
- Fix typo on calling \_.extend (#861)
- Update versions (#870)
- Use individual lodash functions (#864)
- Added `.serialize()` support. Fixes #69 (#827)
- Update Readme.md (#857)
- add extension for JSON require call
- remove gittask badge
- Merge pull request #672 from underdogio/dev/checkbox.radio.values.sqwished
- Added default value for checkboxes/radios
* fixed allignment (fb55)
* added test case for malformed json in data attributes (fb55)
* fix: handle some extreme cases like `data-custom="{{templatevar}}"`. There is possibility error while parsing json . (Harish.K)
* Add missing optional selector doc for {prev,next}{All,Until} (Jérémie Astori)
* update to dom-serializer@0.1.0 (Felix Böhm)
* Document `Cheerio#serialzeArray` (Mike Pennisi)
* Fixed up `serializeArray()` and added multiple support (Todd Wolfson)
* Implement serializeArray() (Jarno Leppänen)
* recognize options in $.xml() (fb55)
* lib/static.js: text(): rm errant space before ++ (Chris Rebert)
* Do not expose internal `children` array (Mike Pennisi)
* Change lodash dependencies to ^3.1.0 (Samy Pessé)
* Update lodash@3.1.0 (Samy Pessé)
* Updates Readme.md: .not(function (index, elem)) (Patrick Ward)
* update to css-select@1.0.0 (fb55)
* Allow failures in Node.js v0.11 (Mike Pennisi)
* Added: Gittask badge (Matthew Mueller)
* Isolate prototypes of functions created via `load` (Mike Pennisi)
* Updates Readme.md: adds JS syntax highlighting (frankcash)
* #608 -- Add support for insertBefore/insertAfter syntax. Supports target types of: $, [$], selector (both single and multiple results) (Ben Cochran)
* Clone input nodes when inserting over a set (Mike Pennisi)
* Move unit test files (Mike Pennisi)
* remove unnecessarily tricky code (David Chambers)
* pass options to $.html in toString (fb55)
* add license info to package.json (Chris Rebert)
* xyz@~0.5.0 (David Chambers)
* Remove unofficial signature of `children` (Mike Pennisi)
* Fix bug in `css` method (Mike Pennisi)
* Correct bug in implementation of `Cheerio#val` (Mike Pennisi)
# 0.20.0 / 2016-02-01
0.18.0 / 2014-11-06
==================
- Add coveralls badge, remove link to old report (Felix Böhm)
- Update lodash dependeny to 4.1.0 (leif.hanack)
- Fix PR #726 adding 'appendTo()' and 'prependTo()' (Delgan)
- Added appendTo and prependTo with tests #641 (digihaven)
- Fix #780 by changing options context in '.find()' (Felix Böhm)
- Add an unit test checking the query of child (Delgan)
- fix #667: attr({foo: null}) removes attribute foo, like attr('foo', null) (Ray Waldin)
- Include reference to dedicated "Loading" section (Mike Pennisi)
- Added load method to \$ (alanev)
- update css-select to 1.2.0 (Felix Böhm)
- Fixing Grammatical Error (Dan Corman)
- Test against node v0.12 --> v4.2 (Jason Kurian)
- Correct output in example (Felix Böhm)
- Fix npm files filter (Bogdan Chadkin)
- Enable setting data on all elements in selection (Mike Pennisi)
- Reinstate `$.fn.toArray` (Mike Pennisi)
- update css-select to 1.1.0 (Thomas Shafer)
- Complete implementation of `wrap` (Mike Pennisi)
- Correct name of unit test (Mike Pennisi)
- Correct grammar in test titles (Mike Pennisi)
- Normalize whitespace (Mike Pennisi)
- Insert omitted assertion (Mike Pennisi)
- Update invocation of `children` (Mike Pennisi)
- Begin implementation of `wrap` method (Dandlezzz)
- Update Readme.md (Sven Slootweg)
- fix document's mistake in Readme.md (exoticknight)
- Add tests for setting text and html as non-strings (Ryc O'Chet)
- Fix for passing non-string values to .html or .text (Ryc O'Chet)
- use a selector to filter form elements (fb55)
- fix README.md typo (Yutian Li)
- README: fix spelling (Chris Rebert)
- Added support for options without a `value` attribute. Fixes #633 (Todd Wolfson)
- responding to pull request feedback - remove item() method and related tests (Ray Waldin)
- add length property and item method to object returned by prop('style'), plus tests (Ray Waldin)
- Added .prop method to readme (Artem Burtsev)
- Added .prop method (Artem Burtsev)
- Added Gitter badge (The Gitter Badger)
* bump htmlparser2 dependency to ~3.8.1 (Chris Rebert)
* Correct unit test titles (Mike Pennisi)
* Correct behavior of `after` and `before` (Mike Pennisi)
* implement jQuery's .has() (Chris Rebert)
* Update repository url (haqii)
* attr() should return undefined or name for booleans (Raoul Millais)
* Update Readme.md (Ryan Breen)
* Implement `Cheerio#not` (Mike Pennisi)
* Clone nodes according to original parsing options (Mike Pennisi)
* fix lint error (David Chambers)
* Add explicit tests for DOM level 1 API (Mike Pennisi)
* Expose DOM level 1 API for Node-like objects (Mike Pennisi)
* Correct error in documentation (Mike Pennisi)
* Return a fully-qualified Function from `$.load` (Mike Pennisi)
* Update tests to avoid duck typing (Mike Pennisi)
* Alter "loaded" functions to produce true instances (Mike Pennisi)
* Organize tests for `cheerio.load` (Mike Pennisi)
* Complete `$.prototype.find` (Mike Pennisi)
* Use JSHint's `extends` option (Mike Pennisi)
* Remove aliases for exported methods (Mike Pennisi)
* Disallow unused variables (Mike Pennisi)
* Remove unused internal variables (Mike Pennisi)
* Remove unused variables from unit tests (Mike Pennisi)
* Remove unused API method references (Mike Pennisi)
* Move tests for `contains` method (Mike Pennisi)
* xyz@0.4.0 (David Chambers)
* Created a wiki for companies using cheerio in production (Matthew Mueller)
* Implement `$.prototype.index` (Mike Pennisi)
* Implement `$.prototype.addBack` (Mike Pennisi)
* Added double quotes to radio attribute name to account for characters such as brackets (akant10)
* Update History.md (Gabriel Falkenberg)
* add 0.17.0 changelog (David Chambers)
* exit prepublish script if tag not found (David Chambers)
* alphabetize devDependencies (fb55)
* ignore coverage dir (fb55)
* submit coverage to coveralls (fb55)
* replace jscoverage with istanbul (fb55)
# 0.19.0 / 2015-03-21
0.17.0 / 2014-06-10
==================
- fixed allignment (fb55)
- added test case for malformed json in data attributes (fb55)
- fix: handle some extreme cases like `data-custom="{{templatevar}}"`. There is possibility error while parsing json . (Harish.K)
- Add missing optional selector doc for {prev,next}{All,Until} (Jérémie Astori)
- update to dom-serializer@0.1.0 (Felix Böhm)
- Document `Cheerio#serialzeArray` (Mike Pennisi)
- Fixed up `serializeArray()` and added multiple support (Todd Wolfson)
- Implement serializeArray() (Jarno Leppänen)
- recognize options in \$.xml() (fb55)
- lib/static.js: text(): rm errant space before ++ (Chris Rebert)
- Do not expose internal `children` array (Mike Pennisi)
- Change lodash dependencies to ^3.1.0 (Samy Pessé)
- Update lodash@3.1.0 (Samy Pessé)
- Updates Readme.md: .not(function (index, elem)) (Patrick Ward)
- update to css-select@1.0.0 (fb55)
- Allow failures in Node.js v0.11 (Mike Pennisi)
- Added: Gittask badge (Matthew Mueller)
- Isolate prototypes of functions created via `load` (Mike Pennisi)
- Updates Readme.md: adds JS syntax highlighting (frankcash)
- #608 -- Add support for insertBefore/insertAfter syntax. Supports target types of: $, [$], selector (both single and multiple results) (Ben Cochran)
- Clone input nodes when inserting over a set (Mike Pennisi)
- Move unit test files (Mike Pennisi)
- remove unnecessarily tricky code (David Chambers)
- pass options to \$.html in toString (fb55)
- add license info to package.json (Chris Rebert)
- xyz@~0.5.0 (David Chambers)
- Remove unofficial signature of `children` (Mike Pennisi)
- Fix bug in `css` method (Mike Pennisi)
- Correct bug in implementation of `Cheerio#val` (Mike Pennisi)
* Fix bug in internal `uniqueSplice` function (Mike Pennisi)
* accept buffer argument to cheerio.load (David Chambers)
* Respect options on the element level (Alex Indigo)
* Change state definition to more readable (Artem Burtsev)
* added test (0xBADC0FFEE)
* add class only if doesn't exist (Artem Burtsev)
* Made it less insane. (Alex Indigo)
* Implement `Cheerio#add` (Mike Pennisi)
* Use "loaded" instance of Cheerio in unit tests (Mike Pennisi)
* Be more strict with object check. (Alex Indigo)
* Added options argument to .html() static method. (Alex Indigo)
* Fixed encoding mishaps. Adjusted tests. (Alex Indigo)
* use dom-serializer module (fb55)
* don't test on 0.8, don't ignore 0.11 (Felix Böhm)
* parse: rm unused variables (coderaiser)
* cheerio: rm unused variable (coderaiser)
* Fixed test (Avi Kohn)
* Added test (Avi Kohn)
* Changed == to === (Avi Kohn)
* Fixed a bug in removing type="hidden" attr (Avi Kohn)
* sorted (Alexey Raspopov)
* add `muted` attr to booleanAttributes (Alexey Raspopov)
* fixed context of `this` in .html (Felix Böhm)
* append new elements for each element in selection (fb55)
# 0.18.0 / 2014-11-06
0.16.0 / 2014-05-08
==================
- bump htmlparser2 dependency to ~3.8.1 (Chris Rebert)
- Correct unit test titles (Mike Pennisi)
- Correct behavior of `after` and `before` (Mike Pennisi)
- implement jQuery's .has() (Chris Rebert)
- Update repository url (haqii)
- attr() should return undefined or name for booleans (Raoul Millais)
- Update Readme.md (Ryan Breen)
- Implement `Cheerio#not` (Mike Pennisi)
- Clone nodes according to original parsing options (Mike Pennisi)
- fix lint error (David Chambers)
- Add explicit tests for DOM level 1 API (Mike Pennisi)
- Expose DOM level 1 API for Node-like objects (Mike Pennisi)
- Correct error in documentation (Mike Pennisi)
- Return a fully-qualified Function from `$.load` (Mike Pennisi)
- Update tests to avoid duck typing (Mike Pennisi)
- Alter "loaded" functions to produce true instances (Mike Pennisi)
- Organize tests for `cheerio.load` (Mike Pennisi)
- Complete `$.prototype.find` (Mike Pennisi)
- Use JSHint's `extends` option (Mike Pennisi)
- Remove aliases for exported methods (Mike Pennisi)
- Disallow unused variables (Mike Pennisi)
- Remove unused internal variables (Mike Pennisi)
- Remove unused variables from unit tests (Mike Pennisi)
- Remove unused API method references (Mike Pennisi)
- Move tests for `contains` method (Mike Pennisi)
- xyz@0.4.0 (David Chambers)
- Created a wiki for companies using cheerio in production (Matthew Mueller)
- Implement `$.prototype.index` (Mike Pennisi)
- Implement `$.prototype.addBack` (Mike Pennisi)
- Added double quotes to radio attribute name to account for characters such as brackets (akant10)
- Update History.md (Gabriel Falkenberg)
- add 0.17.0 changelog (David Chambers)
- exit prepublish script if tag not found (David Chambers)
- alphabetize devDependencies (fb55)
- ignore coverage dir (fb55)
- submit coverage to coveralls (fb55)
- replace jscoverage with istanbul (fb55)
* fix `make bench` (David Chambers)
* makefile: add release-* targets (David Chambers)
* alphabetize dependencies (David Chambers)
* Rewrite `data` internals with caching behavior (Mike Pennisi)
* Fence .val example as js (Kevin Sawicki)
* Fixed typos. Deleted trailing whitespace from test/render.js (Nattaphoom Ch)
* Fix manipulation APIs with removed elements (kpdecker)
* Perform manual string parsing for hasClass (kpdecker)
* Fix existing element removal (kpdecker)
* update render tests (Felix Böhm)
* fixed cheerio path (Felix Böhm)
* use `entities.escape` for attribute values (Felix Böhm)
* bump entities version (Felix Böhm)
* remove lowerCaseTags option from readme (Felix Böhm)
* added test case for .html in xmlMode (fb55)
* render xml in `html()` when `xmlMode: true` (fb55)
* use a map for booleanAttributes (fb55)
* update singleTags, use utils.isTag (fb55)
* update travis badge URL (Felix Böhm)
* use typeof instead of _.isString and _.isNumber (fb55)
* use Array.isArray instead of _.isArray (fb55)
* replace _.isFunction with typeof (fb55)
* removed unnecessary error message (fb55)
* decode entities in htmlparser2 (fb55)
* pass options object to CSSselect (fb55)
# 0.17.0 / 2014-06-10
0.15.0 / 2014-04-08
==================
- Fix bug in internal `uniqueSplice` function (Mike Pennisi)
- accept buffer argument to cheerio.load (David Chambers)
- Respect options on the element level (Alex Indigo)
- Change state definition to more readable (Artem Burtsev)
- added test (0xBADC0FFEE)
- add class only if doesn't exist (Artem Burtsev)
- Made it less insane. (Alex Indigo)
- Implement `Cheerio#add` (Mike Pennisi)
- Use "loaded" instance of Cheerio in unit tests (Mike Pennisi)
- Be more strict with object check. (Alex Indigo)
- Added options argument to .html() static method. (Alex Indigo)
- Fixed encoding mishaps. Adjusted tests. (Alex Indigo)
- use dom-serializer module (fb55)
- don't test on 0.8, don't ignore 0.11 (Felix Böhm)
- parse: rm unused variables (coderaiser)
- cheerio: rm unused variable (coderaiser)
- Fixed test (Avi Kohn)
- Added test (Avi Kohn)
- Changed == to === (Avi Kohn)
- Fixed a bug in removing type="hidden" attr (Avi Kohn)
- sorted (Alexey Raspopov)
- add `muted` attr to booleanAttributes (Alexey Raspopov)
- fixed context of `this` in .html (Felix Böhm)
- append new elements for each element in selection (fb55)
* Update callbacks to pass element per docs (@kpdecker)
* preserve options (@fb55)
* Use SVG travis badge (@t3chnoboy)
* only use static requires (@fb55)
* Optimize manipulation methods (@kpdecker)
* Optimize add and remove class cases (@kpdecker)
* accept dom of DomHandler to cheerio.load (@nleush)
* added parentsUntil method (@finspin)
* Add performance optimization and bug fix `empty` method (@kpdecker)
# 0.16.0 / 2014-05-08
0.14.0 / 2014-04-01
==================
- fix `make bench` (David Chambers)
- makefile: add release-\* targets (David Chambers)
- alphabetize dependencies (David Chambers)
- Rewrite `data` internals with caching behavior (Mike Pennisi)
- Fence .val example as js (Kevin Sawicki)
- Fixed typos. Deleted trailing whitespace from test/render.js (Nattaphoom Ch)
- Fix manipulation APIs with removed elements (kpdecker)
- Perform manual string parsing for hasClass (kpdecker)
- Fix existing element removal (kpdecker)
- update render tests (Felix Böhm)
- fixed cheerio path (Felix Böhm)
- use `entities.escape` for attribute values (Felix Böhm)
- bump entities version (Felix Böhm)
- remove lowerCaseTags option from readme (Felix Böhm)
- added test case for .html in xmlMode (fb55)
- render xml in `html()` when `xmlMode: true` (fb55)
- use a map for booleanAttributes (fb55)
- update singleTags, use utils.isTag (fb55)
- update travis badge URL (Felix Böhm)
- use typeof instead of _.isString and _.isNumber (fb55)
- use Array.isArray instead of \_.isArray (fb55)
- replace \_.isFunction with typeof (fb55)
- removed unnecessary error message (fb55)
- decode entities in htmlparser2 (fb55)
- pass options object to CSSselect (fb55)
* call encodeXML and directly expose decodeHTML (@fb55)
* use latest htmlparser2 and entities versions (@fb55)
* Deprecate `$.fn.toArray` (@jugglinmike)
* Implement `$.fn.get` (@jugglinmike)
* .replaceWith now replaces all selected elements. (@xavi-)
* Correct arguments for 'replaceWith' callback (@jugglinmike)
* switch to lodash (@fb55)
* update to entities@0.5.0 (@fb55)
* Fix attr when $ collection contains text modules (@kpdecker)
* Update to latest version of expect.js (@jugglinmike)
* Remove nodes from their previous structures (@jugglinmike)
* Update render.js (@stevenvachon)
* CDATA test (@stevenvachon)
* only ever one child index for cdata (@stevenvachon)
* don't loop through cdata children array (@stevenvachon)
* proper rendering of CDATA (@stevenvachon)
* Add cheerio-only bench option (@kpdecker)
* Avoid delete operations (@kpdecker)
* Add independent html benchmark (@kpdecker)
* Cache tag check in render (@kpdecker)
* Simplify attribute rendering step (@kpdecker)
* Add html rendering bench case (@kpdecker)
* Remove unnecessary check from removeAttr (@kpdecker)
* Remove unnecessary encoding step for attrs (@kpdecker)
* Add test for removeAttr+attr on boolean attributes (@kpdecker)
* Add single element benchmark case (@kpdecker)
* Optimize filter with selector (@kpdecker)
* Fix passing context as dom node (@alfred-nsh)
* Fix bug in `nextUntil` (@jugglinmike)
* Fix bug in `nextAll` (@jugglinmike)
* Implement `selector` argument of `next` method (@jugglinmike)
* Fix bug in `prevUntil` (@jugglinmike)
* Implement `selector` argument of `prev` method (@jugglinmike)
* Fix bug in `prevAll` (@jugglinmike)
* Fix bug in `siblings` (@jugglinmike)
* Avoid unnecessary indexOf from toggleClass (@kpdecker)
* Use strict equality rather than falsy check in eq (@kpdecker)
* Add benchmark coverage for all $ APIs (@kpdecker)
* Optimize filter Cheerio intermediate creation (@kpdecker)
* Optimize siblings cheerio instance creation (@kpdecker)
* Optimize identity cases for first/last/eq (@kpdecker)
* Use domEach for traversal (@kpdecker)
* Inline children lookup in find (@kpdecker)
* Use domEach in data accessor (@kpdecker)
* Avoid cheerio creation in add/remove/toggleClass (@kpdecker)
* Implement getAttr local helper (@kpdecker)
# 0.15.0 / 2014-04-08
0.13.1 / 2014-01-07
==================
- Update callbacks to pass element per docs (@kpdecker)
- preserve options (@fb55)
- Use SVG travis badge (@t3chnoboy)
- only use static requires (@fb55)
- Optimize manipulation methods (@kpdecker)
- Optimize add and remove class cases (@kpdecker)
- accept dom of DomHandler to cheerio.load (@nleush)
- added parentsUntil method (@finspin)
- Add performance optimization and bug fix `empty` method (@kpdecker)
* Fix select with context in Cheerio function (@jugglinmike)
* Remove unecessary DOM maintenance logic (@jugglinmike)
* Deprecate support for node 0.6
# 0.14.0 / 2014-04-01
0.13.0 / 2013-12-30
==================
- call encodeXML and directly expose decodeHTML (@fb55)
- use latest htmlparser2 and entities versions (@fb55)
- Deprecate `$.fn.toArray` (@jugglinmike)
- Implement `$.fn.get` (@jugglinmike)
- .replaceWith now replaces all selected elements. (@xavi-)
- Correct arguments for 'replaceWith' callback (@jugglinmike)
- switch to lodash (@fb55)
- update to entities@0.5.0 (@fb55)
- Fix attr when \$ collection contains text modules (@kpdecker)
- Update to latest version of expect.js (@jugglinmike)
- Remove nodes from their previous structures (@jugglinmike)
- Update render.js (@stevenvachon)
- CDATA test (@stevenvachon)
- only ever one child index for cdata (@stevenvachon)
- don't loop through cdata children array (@stevenvachon)
- proper rendering of CDATA (@stevenvachon)
- Add cheerio-only bench option (@kpdecker)
- Avoid delete operations (@kpdecker)
- Add independent html benchmark (@kpdecker)
- Cache tag check in render (@kpdecker)
- Simplify attribute rendering step (@kpdecker)
- Add html rendering bench case (@kpdecker)
- Remove unnecessary check from removeAttr (@kpdecker)
- Remove unnecessary encoding step for attrs (@kpdecker)
- Add test for removeAttr+attr on boolean attributes (@kpdecker)
- Add single element benchmark case (@kpdecker)
- Optimize filter with selector (@kpdecker)
- Fix passing context as dom node (@alfred-nsh)
- Fix bug in `nextUntil` (@jugglinmike)
- Fix bug in `nextAll` (@jugglinmike)
- Implement `selector` argument of `next` method (@jugglinmike)
- Fix bug in `prevUntil` (@jugglinmike)
- Implement `selector` argument of `prev` method (@jugglinmike)
- Fix bug in `prevAll` (@jugglinmike)
- Fix bug in `siblings` (@jugglinmike)
- Avoid unnecessary indexOf from toggleClass (@kpdecker)
- Use strict equality rather than falsy check in eq (@kpdecker)
- Add benchmark coverage for all \$ APIs (@kpdecker)
- Optimize filter Cheerio intermediate creation (@kpdecker)
- Optimize siblings cheerio instance creation (@kpdecker)
- Optimize identity cases for first/last/eq (@kpdecker)
- Use domEach for traversal (@kpdecker)
- Inline children lookup in find (@kpdecker)
- Use domEach in data accessor (@kpdecker)
- Avoid cheerio creation in add/remove/toggleClass (@kpdecker)
- Implement getAttr local helper (@kpdecker)
* Remove "root" node (@jugglinmike)
* Fix bug in `prevAll`, `prev`, `nextAll`, `next`, `prevUntil`, `nextUntil` (@jugglinmike)
* Fix `replaceWith` method (@jugglinmike)
* added nextUntil() and prevUntil() (@finspin)
* Remove internal `connect` function (@jugglinmike)
* Rename `Cheerio#make` to document private status (@jugginmike)
* Remove extraneous call to `_.uniq` (@jugglinmike)
* Use CSSselect library directly (@jugglinmike)
* Run CI against Node v0.11 as an allowed failure (@jugginmike)
* Correct bug in `Cheerio#parents` (@jugglinmike)
* Implement `$.fn.end` (@jugginmike)
* Ignore colons inside of url(.*) when parsing css (@Meekohi)
* Introduce rudimentary benchmark suite (@jugglinmike)
* Update HtmlParser2 version (@jugglinmike)
* Correct inconsistency in `$.fn.map` (@jugglinmike)
* fixed traversing tests (@finspin)
* Simplify `make` method (@jugglinmike)
* Avoid shadowing instance methods from arrays (@jugglinmike)
# 0.13.1 / 2014-01-07
0.12.4 / 2013-11-12
==================
- Fix select with context in Cheerio function (@jugglinmike)
- Remove unecessary DOM maintenance logic (@jugglinmike)
- Deprecate support for node 0.6
* Coerce JSON values returned by `data` (@jugglinmike)
* issue #284: when rendering HTML, use original data attributes (@Trott)
* Introduce JSHint for automated code linting (@jugglinmike)
* Prevent `find` from returning duplicate elements (@jugglinmike)
* Implement function signature of `replaceWith` (@jugglinmike)
* Implement function signature of `before` (@jugglinmike)
* Implement function signature of `after` (@jugglinmike)
* Implement function signature of `append`/`prepend` (@jugglinmike)
* Extend iteration methods to accept nodes (@jugglinmike)
* Improve `removeClass` (@jugglinmike)
* Complete function signature of `addClass` (@jugglinmike)
* Fix bug in `removeClass` (@jugglinmike)
* Improve contributing.md (@jugglinmike)
* Fix and document .css() (@jugglinmike)
# 0.13.0 / 2013-12-30
0.12.3 / 2013-10-04
===================
- Remove "root" node (@jugglinmike)
- Fix bug in `prevAll`, `prev`, `nextAll`, `next`, `prevUntil`, `nextUntil` (@jugglinmike)
- Fix `replaceWith` method (@jugglinmike)
- added nextUntil() and prevUntil() (@finspin)
- Remove internal `connect` function (@jugglinmike)
- Rename `Cheerio#make` to document private status (@jugginmike)
- Remove extraneous call to `_.uniq` (@jugglinmike)
- Use CSSselect library directly (@jugglinmike)
- Run CI against Node v0.11 as an allowed failure (@jugginmike)
- Correct bug in `Cheerio#parents` (@jugglinmike)
- Implement `$.fn.end` (@jugginmike)
- Ignore colons inside of url(.\*) when parsing css (@Meekohi)
- Introduce rudimentary benchmark suite (@jugglinmike)
- Update HtmlParser2 version (@jugglinmike)
- Correct inconsistency in `$.fn.map` (@jugglinmike)
- fixed traversing tests (@finspin)
- Simplify `make` method (@jugglinmike)
- Avoid shadowing instance methods from arrays (@jugglinmike)
* Add .toggleClass() function (@cyberthom)
* Add contributing guidelines (@jugglinmike)
* Fix bug in `siblings` (@jugglinmike)
* Correct the implementation `filter` and `is` (@jugglinmike)
* add .data() function (@andi-neck)
* add .css() (@yields)
* Implements contents() (@jlep)
# 0.12.4 / 2013-11-12
0.12.2 / 2013-09-04
==================
- Coerce JSON values returned by `data` (@jugglinmike)
- issue #284: when rendering HTML, use original data attributes (@Trott)
- Introduce JSHint for automated code linting (@jugglinmike)
- Prevent `find` from returning duplicate elements (@jugglinmike)
- Implement function signature of `replaceWith` (@jugglinmike)
- Implement function signature of `before` (@jugglinmike)
- Implement function signature of `after` (@jugglinmike)
- Implement function signature of `append`/`prepend` (@jugglinmike)
- Extend iteration methods to accept nodes (@jugglinmike)
- Improve `removeClass` (@jugglinmike)
- Complete function signature of `addClass` (@jugglinmike)
- Fix bug in `removeClass` (@jugglinmike)
- Improve contributing.md (@jugglinmike)
- Fix and document .css() (@jugglinmike)
* Correct implementation of `$.fn.text` (@jugglinmike)
* Refactor Cheerio array creation (@jugglinmike)
* Extend manipulation methods to accept Arrays (@jugglinmike)
* support .attr(attributeName, function(index, attr)) (@xiaohwan)
# 0.12.3 / 2013-10-04
0.12.1 / 2013-07-30
==================
- Add .toggleClass() function (@cyberthom)
- Add contributing guidelines (@jugglinmike)
- Fix bug in `siblings` (@jugglinmike)
- Correct the implementation `filter` and `is` (@jugglinmike)
- add .data() function (@andi-neck)
- add .css() (@yields)
- Implements contents() (@jlep)
* Correct behavior of `Cheerio#parents` (@jugglinmike)
* Double quotes inside attributes kills HTML (@khoomeister)
* Making next({}) and prev({}) return empty object (@absentTelegraph)
* Implement $.parseHTML (@jugglinmike)
* Correct bug in jQuery.fn.closest (@jugglinmike)
* Correct behavior of $.fn.val on 'option' elements (@jugglinmike)
# 0.12.2 / 2013-09-04
0.12.0 / 2013-06-09
===================
- Correct implementation of `$.fn.text` (@jugglinmike)
- Refactor Cheerio array creation (@jugglinmike)
- Extend manipulation methods to accept Arrays (@jugglinmike)
- support .attr(attributeName, function(index, attr)) (@xiaohwan)
* Breaking Change: Changed context from parent to the actual passed one (@swissmanu)
* Fixed: jquery checkbox val behavior (@jhubble)
* Added: output xml with $.xml() (@Maciek416)
* Bumped: htmlparser2 to 3.1.1
* Fixed: bug in attr(key, val) on empty objects (@farhadi)
* Added: prevAll, nextAll (@lessmind)
* Fixed: Safety check in parents and closest (@zero21xxx)
* Added: .is(sel) (@zero21xxx)
# 0.12.1 / 2013-07-30
0.11.0 / 2013-04-22
==================
- Correct behavior of `Cheerio#parents` (@jugglinmike)
- Double quotes inside attributes kills HTML (@khoomeister)
- Making next({}) and prev({}) return empty object (@absentTelegraph)
- Implement \$.parseHTML (@jugglinmike)
- Correct bug in jQuery.fn.closest (@jugglinmike)
- Correct behavior of \$.fn.val on 'option' elements (@jugglinmike)
* Added: .closest() (@jeremy-dentel)
* Added: .parents() (@zero21xxx)
* Added: .val() (@rschmukler & @leahciMic)
* Added: Travis support for node 0.10.0 (@jeremy-dentel)
* Fixed: .find() if no selector (@davidchambers)
* Fixed: Propagate syntax errors caused by invalid selectors (@davidchambers)
# 0.12.0 / 2013-06-09
0.10.8 / 2013-03-11
==================
- Breaking Change: Changed context from parent to the actual passed one (@swissmanu)
- Fixed: jquery checkbox val behavior (@jhubble)
- Added: output xml with \$.xml() (@Maciek416)
- Bumped: htmlparser2 to 3.1.1
- Fixed: bug in attr(key, val) on empty objects (@farhadi)
- Added: prevAll, nextAll (@lessmind)
- Fixed: Safety check in parents and closest (@zero21xxx)
- Added: .is(sel) (@zero21xxx)
* Add slice method (SBoudrias)
# 0.11.0 / 2013-04-22
0.10.7 / 2013-02-10
==================
- Added: .closest() (@jeremy-dentel)
- Added: .parents() (@zero21xxx)
- Added: .val() (@rschmukler & @leahciMic)
- Added: Travis support for node 0.10.0 (@jeremy-dentel)
- Fixed: .find() if no selector (@davidchambers)
- Fixed: Propagate syntax errors caused by invalid selectors (@davidchambers)
* Code & doc cleanup (davidchambers)
* Fixed bug in filter (jugglinmike)
# 0.10.8 / 2013-03-11
0.10.6 / 2013-01-29
==================
- Add slice method (SBoudrias)
* Added `$.contains(...)` (jugglinmike)
* formatting cleanup (davidchambers)
* Bug fix for `.children()` (jugglinmike & davidchambers)
* Remove global `render` bug (wvl)
# 0.10.7 / 2013-02-10
0.10.5 / 2012-12-18
===================
- Code & doc cleanup (davidchambers)
- Fixed bug in filter (jugglinmike)
* Fixed botched publish from 0.10.4 - changes should now be present
# 0.10.6 / 2013-01-29
0.10.4 / 2012-12-16
==================
- Added `$.contains(...)` (jugglinmike)
- formatting cleanup (davidchambers)
- Bug fix for `.children()` (jugglinmike & davidchambers)
- Remove global `render` bug (wvl)
* $.find should query descendants only (@jugglinmike)
* Tighter underscore dependency
# 0.10.5 / 2012-12-18
0.10.3 / 2012-11-18
===================
- Fixed botched publish from 0.10.4 - changes should now be present
* fixed outer html bug
* Updated documentation for $(...).html() and $.html()
# 0.10.4 / 2012-12-16
0.10.2 / 2012-11-17
===================
- \$.find should query descendants only (@jugglinmike)
- Tighter underscore dependency
* Added a toString() method (@bensheldon)
* use `_.each` and `_.map` to simplify cheerio namesakes (@davidchambers)
* Added filter() with tests and updated readme (@bensheldon & @davidchambers)
* Added spaces between attributes rewritten by removeClass (@jos3000)
* updated docs to remove reference to size method (@ironchefpython)
* removed HTML tidy/pretty print from cheerio
# 0.10.3 / 2012-11-18
0.10.1 / 2012-10-04
===================
- fixed outer html bug
- Updated documentation for $(...).html() and $.html()
* Fixed regression, filtering with a context (#106)
# 0.10.2 / 2012-11-17
0.10.0 / 2012-09-24
===================
- Added a toString() method (@bensheldon)
- use `_.each` and `_.map` to simplify cheerio namesakes (@davidchambers)
- Added filter() with tests and updated readme (@bensheldon & @davidchambers)
- Added spaces between attributes rewritten by removeClass (@jos3000)
- updated docs to remove reference to size method (@ironchefpython)
- removed HTML tidy/pretty print from cheerio
* Greatly simplified and reorganized the library, reducing the loc by 30%
* Now supports mocha's test-coverage
* Deprecated self-closing tags (HTML5 doesn't require them)
* Fixed error thrown in removeClass(...) @robashton
# 0.10.1 / 2012-10-04
0.9.2 / 2012-08-10
==================
- Fixed regression, filtering with a context (#106)
* added $(...).map(fn)
* manipulation: refactor `makeCheerioArray`
* make .removeClass() remove *all* occurrences (#64)
# 0.10.0 / 2012-09-24
0.9.1 / 2012-08-03
==================
- Greatly simplified and reorganized the library, reducing the loc by 30%
- Now supports mocha's test-coverage
- Deprecated self-closing tags (HTML5 doesn't require them)
- Fixed error thrown in removeClass(...) @robashton
* fixed bug causing options not to make it to the parser
# 0.9.2 / 2012-08-10
0.9.0 / 2012-07-24
==================
- added \$(...).map(fn)
- manipulation: refactor `makeCheerioArray`
- make .removeClass() remove _all_ occurrences (#64)
* Added node 8.x support
* Removed node 4.x support
* Add html(dom) support (@wvl)
* fixed xss vulnerabilities on .attr(), .text(), & .html() (@benatkin, @FB55)
* Rewrote tests into javascript, removing coffeescript dependency (@davidchambers)
* Tons of cleanup (@davidchambers)
# 0.9.1 / 2012-08-03
0.8.3 / 2012-06-12
==================
- fixed bug causing options not to make it to the parser
* Fixed minor package regression (closes #60)
# 0.9.0 / 2012-07-24
0.8.2 / 2012-06-11
==================
- Added node 8.x support
- Removed node 4.x support
- Add html(dom) support (@wvl)
- fixed xss vulnerabilities on .attr(), .text(), & .html() (@benatkin, @FB55)
- Rewrote tests into javascript, removing coffeescript dependency (@davidchambers)
- Tons of cleanup (@davidchambers)
* Now fails gracefully in cases that involve special chars, which is inline with jQuery (closes #59)
* text() now decode special entities (closes #52)
* updated travis.yml to test node 4.x
# 0.8.3 / 2012-06-12
0.8.1 / 2012-06-02
==================
- Fixed minor package regression (closes #60)
* fixed regression where if you created an element, it would update the root
* compatible with node 4.x (again)
# 0.8.2 / 2012-06-11
0.8.0 / 2012-05-27
==================
- Now fails gracefully in cases that involve special chars, which is inline with jQuery (closes #59)
- text() now decode special entities (closes #52)
- updated travis.yml to test node 4.x
* Updated CSS parser to use FB55/CSSselect. Cheerio now supports most CSS3 psuedo selectors thanks to @FB55.
* ignoreWhitespace now on by default again. See #55 for context.
* Changed $(':root') to $.root(), cleaned up $.clone()
* Support for .eq(i) thanks to @alexbardas
* Removed support for node 0.4.x
* Fixed memory leak where package.json was continually loaded
* Tons more tests
# 0.8.1 / 2012-06-02
0.7.0 / 2012-04-08
==================
- fixed regression where if you created an element, it would update the root
- compatible with node 4.x (again)
* Now testing with node v0.7.7
* Added travis-ci integration
* Replaced should.js with expect.js. Browser testing to come
* Fixed spacing between attributes and their values
* Added HTML tidy/pretty print
* Exposed node-htmlparser2 parsing options
* Revert .replaceWith(...) to be consistent with jQuery
# 0.8.0 / 2012-05-27
0.6.2 / 2012-02-12
==================
- Updated CSS parser to use FB55/CSSselect. Cheerio now supports most CSS3 psuedo selectors thanks to @FB55.
- ignoreWhitespace now on by default again. See #55 for context.
- Changed $(':root') to $.root(), cleaned up \$.clone()
- Support for .eq(i) thanks to @alexbardas
- Removed support for node 0.4.x
- Fixed memory leak where package.json was continually loaded
- Tons more tests
* Fixed .replaceWith(...) regression
# 0.7.0 / 2012-04-08
0.6.1 / 2012-02-12
==================
- Now testing with node v0.7.7
- Added travis-ci integration
- Replaced should.js with expect.js. Browser testing to come
- Fixed spacing between attributes and their values
- Added HTML tidy/pretty print
- Exposed node-htmlparser2 parsing options
- Revert .replaceWith(...) to be consistent with jQuery
* Added .first(), .last(), and .clone() commands.
* Option to parse using whitespace added to `.load`.
* Many bug fixes to make cheerio more aligned with jQuery.
* Added $(':root') to select the highest level element.
# 0.6.2 / 2012-02-12
- Fixed .replaceWith(...) regression
# 0.6.1 / 2012-02-12
- Added .first(), .last(), and .clone() commands.
- Option to parse using whitespace added to `.load`.
- Many bug fixes to make cheerio more aligned with jQuery.
- Added \$(':root') to select the highest level element.
Many thanks to the contributors that made this release happen: @ironchefpython and @siddMahen
0.6.0 / 2012-02-07
==================
# 0.6.0 / 2012-02-07
* *Important:* `$(...).html()` now returns inner HTML, which is in line with the jQuery spec
* `$.html()` returns the full HTML string. `$.html([cheerioObject])` will return the outer(selected element's tag) and inner HTML of that object
* Fixed bug that prevented HTML strings with depth (eg. `append('<ul><li><li></ul>')`) from getting `parent`, `next`, `prev` attributes.
* Halted [htmlparser2](https://github.com/FB55/node-htmlparser) at v2.2.2 until single attributes bug gets fixed.
- _Important:_ `$(...).html()` now returns inner HTML, which is in line with the jQuery spec
- `$.html()` returns the full HTML string. `$.html([cheerioObject])` will return the outer(selected element's tag) and inner HTML of that object
- Fixed bug that prevented HTML strings with depth (eg. `append('<ul><li><li></ul>')`) from getting `parent`, `next`, `prev` attributes.
- Halted [htmlparser2](https://github.com/FB55/node-htmlparser) at v2.2.2 until single attributes bug gets fixed.
0.5.1 / 2012-02-05
==================
# 0.5.1 / 2012-02-05
* Fixed minor regression: $(...).text(fn) would fail
- Fixed minor regression: \$(...).text(fn) would fail
0.5.1 / 2012-02-05
==================
# 0.5.1 / 2012-02-05
* Fixed regression: HTML pages with comments would fail
- Fixed regression: HTML pages with comments would fail
0.5.0 / 2012-02-04
==================
# 0.5.0 / 2012-02-04
* Transitioned from Coffeescript back to Javascript
* Parser now ignores whitespace
* Fixed issue with double slashes on self-enclosing tags
* Added boolean attributes to html rendering
- Transitioned from Coffeescript back to Javascript
- Parser now ignores whitespace
- Fixed issue with double slashes on self-enclosing tags
- Added boolean attributes to html rendering
0.4.2 / 2012-01-16
==================
# 0.4.2 / 2012-01-16
* Multiple selectors support: $('.apple, .orange'). Thanks @siddMahen!
* Update package.json to always use latest cheerio-soupselect
* Fix memory leak in index.js
- Multiple selectors support: \$('.apple, .orange'). Thanks @siddMahen!
- Update package.json to always use latest cheerio-soupselect
- Fix memory leak in index.js
0.4.1 / 2011-12-19
==================
* Minor packaging changes to allow `make test` to work from npm installation
# 0.4.1 / 2011-12-19
0.4.0 / 2011-12-19
==================
- Minor packaging changes to allow `make test` to work from npm installation
* Rewrote all unit tests as cheerio transitioned from vows -> mocha
* Internally, renderer.render -> render(...), parser.parse -> parse(...)
* Append, prepend, html, before, after all work with only text (no tags)
* Bugfix: Attributes can now be removed from script and style tags
* Added yield as a single tag
* Cheerio now compatible with node >=0.4.7
# 0.4.0 / 2011-12-19
0.3.2 / 2011-12-1
=================
- Rewrote all unit tests as cheerio transitioned from vows -> mocha
- Internally, renderer.render -> render(...), parser.parse -> parse(...)
- Append, prepend, html, before, after all work with only text (no tags)
- Bugfix: Attributes can now be removed from script and style tags
- Added yield as a single tag
- Cheerio now compatible with node >=0.4.7
* Fixed $(...).text(...) to work with "root" element
# 0.3.2 / 2011-12-1
0.3.1 / 2011-11-25
==================
- Fixed \$(...).text(...) to work with "root" element
* Now relying on cheerio-soupselect instead of node-soupselect
* Removed all lingering htmlparser dependencies
* parser now returns parent "root" element. Root now never needs to be updated when there is multiple roots. This fixes ongoing issues with before(...), after(...) and other manipulation functions
* Added jQuery's $(...).replaceWith(...)
# 0.3.1 / 2011-11-25
0.3.0 / 2011-11-19
==================
- Now relying on cheerio-soupselect instead of node-soupselect
- Removed all lingering htmlparser dependencies
- parser now returns parent "root" element. Root now never needs to be updated when there is multiple roots. This fixes ongoing issues with before(...), after(...) and other manipulation functions
- Added jQuery's \$(...).replaceWith(...)
* Now using htmlparser2 for parsing (2x speed increase, cleaner, actively developed)
* Added benchmark directory for future speed tests
* $('...').dom() was funky, so it was removed in favor of $('...').get(). $.dom() still works the same.
* $.root now correctly static across all instances of $
* Added a screencast
# 0.3.0 / 2011-11-19
0.2.2 / 2011-11-9
=================
- Now using htmlparser2 for parsing (2x speed increase, cleaner, actively developed)
- Added benchmark directory for future speed tests
- $('...').dom() was funky, so it was removed in favor of $('...').get(). \$.dom() still works the same.
- $.root now correctly static across all instances of $
- Added a screencast
* Traversing will select `<script>` and `<style>` tags (Closes Issue: #8)
* .text(string) now working with empty elements (Closes Issue: #7)
* Fixed before(...) & after(...) again if there is no parent (Closes Issue: #2)
# 0.2.2 / 2011-11-9
0.2.1 / 2011-11-5
=================
- Traversing will select `<script>` and `<style>` tags (Closes Issue: #8)
- .text(string) now working with empty elements (Closes Issue: #7)
- Fixed before(...) & after(...) again if there is no parent (Closes Issue: #2)
* Fixed before(...) & after(...) if there is no parent (Closes Issue: #2)
* Comments now rendered correctly (Closes Issue: #5)
# 0.2.1 / 2011-11-5
< 0.2.0 / 2011-10-31
====================
- Fixed before(...) & after(...) if there is no parent (Closes Issue: #2)
- Comments now rendered correctly (Closes Issue: #5)
* Initial release (untracked development)
# < 0.2.0 / 2011-10-31
- Initial release (untracked development)
/**
* Export cheerio (with )
* @module cheerio
* @borrows static.load as load
* @borrows static.html as html
* @borrows static.text as text
* @borrows static.xml as xml
*/
var staticMethods = require('./lib/static');
exports = module.exports = require('./lib/cheerio');
/*
Export the version
*/
/**
* An identifier describing the version of Cheerio which has been executed.
*
* @type {string}
*/
exports.version = require('./package.json').version;
exports.version = require('./package.json').version;
exports.load = staticMethods.load;
exports.html = staticMethods.html;
exports.text = staticMethods.text;
exports.xml = staticMethods.xml;
/**
* In order to promote consistency with the jQuery library, users are
* encouraged to instead use the static method of the same name.
*
* @example
* var $ = cheerio.load('<div><p></p></div>');
* $.contains($('div').get(0), $('p').get(0)); // true
* $.contains($('p').get(0), $('div').get(0)); // false
*
* @function
* @returns {boolean}
* @deprecated
*/
exports.contains = staticMethods.contains;
/**
* In order to promote consistency with the jQuery library, users are
* encouraged to instead use the static method of the same name.
*
* @example
* var $ = cheerio.load('');
* $.merge([1, 2], [3, 4]) // [1, 2, 3, 4]
*
* @function
* @deprecated
*/
exports.merge = staticMethods.merge;
/**
* In order to promote consistency with the jQuery library, users are
* encouraged to instead use the static method of the same name as it is
* defined on the "loaded" Cheerio factory function.
*
* @example
* var $ = cheerio.load('');
* $.parseHTML('<b>markup</b>');
*
* @function
* @deprecated See {@link static/parseHTML}.
*/
exports.parseHTML = staticMethods.parseHTML;
/**
* Users seeking to access the top-level element of a parsed document should
* instead use the `root` static method of a "loaded" Cheerio function.
*
* @example
* var $ = cheerio.load('');
* $.root();
*
* @function
* @deprecated
*/
exports.root = staticMethods.root;

@@ -1,31 +0,29 @@

var $ = require('../static'),
utils = require('../utils'),
isTag = utils.isTag,
domEach = utils.domEach,
hasOwn = Object.prototype.hasOwnProperty,
camelCase = utils.camelCase,
cssCase = utils.cssCase,
rspace = /\s+/,
dataAttrPrefix = 'data-',
_ = {
forEach: require('lodash/forEach'),
extend: require('lodash/assignIn'),
some: require('lodash/some')
},
/**
* Methods for getting and modifying attributes.
*
* @module cheerio/attributes
*/
// Lookup table for coercing string data-* attributes to their corresponding
// JavaScript primitives
primitives = {
null: null,
true: true,
false: false
},
var text = require('../static').text;
var utils = require('../utils');
var isTag = utils.isTag;
var domEach = utils.domEach;
var hasOwn = Object.prototype.hasOwnProperty;
var camelCase = utils.camelCase;
var cssCase = utils.cssCase;
var rspace = /\s+/;
var dataAttrPrefix = 'data-';
// Lookup table for coercing string data-* attributes to their corresponding
// JavaScript primitives
var primitives = {
null: null,
true: true,
false: false,
};
// Attributes that are booleans
var rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i;
// Matches strings that look like JSON objects or arrays
var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/;
// Attributes that are booleans
rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
// Matches strings that look like JSON objects or arrays
rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/;
var getAttr = function(elem, name) {
var getAttr = function (elem, name) {
if (!elem || !isTag(elem)) return;

@@ -49,9 +47,11 @@

if (elem.name === 'option' && name === 'value') {
return $.text(elem.children);
return text(elem.children);
}
// Mimic DOM with default value for radios/checkboxes
if (elem.name === 'input' &&
(elem.attribs.type === 'radio' || elem.attribs.type === 'checkbox') &&
name === 'value') {
if (
elem.name === 'input' &&
(elem.attribs.type === 'radio' || elem.attribs.type === 'checkbox') &&
name === 'value'
) {
return 'on';

@@ -61,24 +61,43 @@ }

var setAttr = function(el, name, value) {
var setAttr = function (el, name, value) {
if (value === null) {
removeAttribute(el, name);
} else {
el.attribs[name] = value+'';
el.attribs[name] = value + '';
}
};
exports.attr = function(name, value) {
/**
* Method for getting and setting attributes. Gets the attribute value for only
* the first element in the matched set. If you set an attribute's value to
* `null`, you remove that attribute. You may also pass a `map` and `function`
* like jQuery.
*
* @example
*
* $('ul').attr('id')
* //=> fruits
*
* $('.apple').attr('id', 'favorite').html()
* //=> <li class="apple" id="favorite">Apple</li>
*
* @param {string} name - Name of the attribute.
* @param {string} [value] - If specified sets the value of the attribute.
*
* @see {@link http://api.jquery.com/attr/}
*/
exports.attr = function (name, value) {
// Set the value (with attr map support)
if (typeof name === 'object' || value !== undefined) {
if (typeof value === 'function') {
return domEach(this, function(i, el) {
return domEach(this, function (i, el) {
setAttr(el, name, value.call(el, i, el.attribs[name]));
});
}
return domEach(this, function(i, el) {
return domEach(this, function (i, el) {
if (!isTag(el)) return;
if (typeof name === 'object') {
_.forEach(name, function(objValue, objName) {
Object.keys(name).forEach(function (objName) {
var objValue = name[objName];
setAttr(el, objName, objValue);

@@ -98,7 +117,7 @@ });

return hasOwn.call(el, name)
? el[name]
: rboolean.test(name)
? getAttr(el, name) !== undefined
: getAttr(el, name);
return name in el
? el[name]
: rboolean.test(name)
? getAttr(el, name) !== undefined
: getAttr(el, name);
};

@@ -110,8 +129,24 @@

/**
* Method for getting and setting properties. Gets the property value for only
* the first element in the matched set.
*
* @example
*
* $('input[type="checkbox"]').prop('checked')
* //=> false
*
* $('input[type="checkbox"]').prop('checked', true).val()
* //=> ok
*
* @param {string} name - Name of the property.
* @param {any} [value] - If specified set the property to this.
*
* @see {@link http://api.jquery.com/prop/}
*/
exports.prop = function (name, value) {
var i = 0,
property;
var i = 0;
var property;
if (typeof name === 'string' && value === undefined) {
switch (name) {

@@ -121,3 +156,3 @@ case 'style':

_.forEach(property, function (v, p) {
Object.keys(property).forEach(function (p) {
property[i++] = p;

@@ -133,2 +168,5 @@ });

break;
case 'outerHTML':
property = this.clone().wrap('<container />').parent().html();
break;
default:

@@ -142,5 +180,4 @@ property = getProp(this[0], name);

if (typeof name === 'object' || value !== undefined) {
if (typeof value === 'function') {
return domEach(this, function(j, el) {
return domEach(this, function (j, el) {
setProp(el, name, value.call(el, j, getProp(el, name)));

@@ -150,11 +187,10 @@ });

return domEach(this, function(__, el) {
return domEach(this, function (__, el) {
if (!isTag(el)) return;
if (typeof name === 'object') {
_.forEach(name, function(val, key) {
Object.keys(name).forEach(function (key) {
var val = name[key];
setProp(el, key, val);
});
} else {

@@ -164,7 +200,6 @@ setProp(el, name, value);

});
}
};
var setData = function(el, name, value) {
var setData = function (el, name, value) {
if (!el.data) {

@@ -174,3 +209,3 @@ el.data = {};

if (typeof name === 'object') return _.extend(el.data, name);
if (typeof name === 'object') return Object.assign(el.data, name);
if (typeof name === 'string' && value !== undefined) {

@@ -185,11 +220,17 @@ el.data[name] = value;

// manner.
var readData = function(el, name) {
var readData = function (el, name) {
var readAll = arguments.length === 1;
var domNames, domName, jsNames, jsName, value, idx, length;
var domNames;
var domName;
var jsNames;
var jsName;
var value;
var idx;
var length;
if (readAll) {
domNames = Object.keys(el.attribs).filter(function(attrName) {
domNames = Object.keys(el.attribs).filter(function (attrName) {
return attrName.slice(0, dataAttrPrefix.length) === dataAttrPrefix;
});
jsNames = domNames.map(function(_domName) {
jsNames = domNames.map(function (_domName) {
return camelCase(_domName.slice(dataAttrPrefix.length));

@@ -205,3 +246,3 @@ });

jsName = jsNames[idx];
if (hasOwn.call(el.attribs, domName)) {
if (hasOwn.call(el.attribs, domName) && !hasOwn.call(el.data, jsName)) {
value = el.attribs[domName];

@@ -216,3 +257,5 @@

value = JSON.parse(value);
} catch(e){ }
} catch (e) {
/* ignore */
}
}

@@ -227,3 +270,24 @@

exports.data = function(name, value) {
/**
* Method for getting and setting data attributes. Gets or sets the data
* attribute value for only the first element in the matched set.
*
* @example
*
* $('<div data-apple-color="red"></div>').data()
* //=> { appleColor: 'red' }
*
* $('<div data-apple-color="red"></div>').data('apple-color')
* //=> 'red'
*
* const apple = $('.apple').data('kind', 'mac')
* apple.data('kind')
* //=> 'mac'
*
* @param {string} name - Name of the attribute.
* @param {any} [value] - If specified new value.
*
* @see {@link http://api.jquery.com/data/}
*/
exports.data = function (name, value) {
var elem = this[0];

@@ -244,3 +308,3 @@

if (typeof name === 'object' || value !== undefined) {
domEach(this, function(i, el) {
domEach(this, function (i, el) {
setData(el, name, value);

@@ -257,11 +321,23 @@ });

/**
* Get the value of an element
* Method for getting and setting the value of input, select, and textarea.
* Note: Support for `map`, and `function` has not been added yet.
*
* @example
*
* $('input[type="text"]').val()
* //=> input_text
*
* $('input[type="text"]').val('test').html()
* //=> <input type="text" value="test"/>
*
* @param {string} [value] - If specified new value.
*
* @see {@link http://api.jquery.com/val/}
*/
exports.val = function (value) {
var querying = arguments.length === 0;
var element = this[0];
exports.val = function(value) {
var querying = arguments.length === 0,
element = this[0];
if (!element) return;
if(!element) return;
switch (element.name) {

@@ -271,18 +347,15 @@ case 'textarea':

case 'input':
switch (this.attr('type')) {
case 'radio':
if (querying) {
return this.attr('value');
} else {
this.attr('value', value);
return this;
}
break;
default:
return this.attr('value', value);
if (this.attr('type') === 'radio') {
if (querying) {
return this.attr('value');
}
this.attr('value', value);
return this;
}
return;
return this.attr('value', value);
case 'select':
var option = this.find('option:selected'),
returnValue;
var option = this.find('option:selected');
var returnValue;
if (option === undefined) return undefined;

@@ -305,3 +378,3 @@ if (!querying) {

returnValue = [];
domEach(option, function(__, el) {
domEach(option, function (__, el) {
returnValue.push(getAttr(el, 'value'));

@@ -321,34 +394,86 @@ });

/**
* Remove an attribute
* Remove an attribute.
*
* @private
* @param {node} elem - Node to remove attribute from.
* @param {string} name - Name of the attribute to remove.
*/
var removeAttribute = function (elem, name) {
if (!elem.attribs || !hasOwn.call(elem.attribs, name)) return;
var removeAttribute = function(elem, name) {
if (!elem.attribs || !hasOwn.call(elem.attribs, name))
return;
delete elem.attribs[name];
};
/**
* Splits a space-separated list of names to individual
* names.
*
* @param {string} names - Names to split.
* @returns {string[]} - Split names.
*/
var splitNames = function (names) {
return names ? names.trim().split(rspace) : [];
};
exports.removeAttr = function(name) {
domEach(this, function(i, elem) {
removeAttribute(elem, name);
});
/**
* Method for removing attributes by `name`.
*
* @example
*
* $('.pear').removeAttr('class').html()
* //=> <li>Pear</li>
*
* $('.apple').attr('id', 'favorite')
* $('.apple').removeAttr('id class').html()
* //=> <li>Apple</li>
*
* @param {string} name - Name of the attribute.
*
* @see {@link http://api.jquery.com/removeAttr/}
*/
exports.removeAttr = function (name) {
var attrNames = splitNames(name);
for (var i = 0; i < attrNames.length; i++) {
domEach(this, function (j, elem) {
removeAttribute(elem, attrNames[i]);
});
}
return this;
};
exports.hasClass = function(className) {
return _.some(this, function(elem) {
var attrs = elem.attribs,
clazz = attrs && attrs['class'],
idx = -1,
end;
/**
* Check to see if *any* of the matched elements have the given `className`.
*
* @example
*
* $('.pear').hasClass('pear')
* //=> true
*
* $('apple').hasClass('fruit')
* //=> false
*
* $('li').hasClass('pear')
* //=> true
*
* @param {string} className - Name of the class.
*
* @see {@link http://api.jquery.com/hasClass/}
*/
exports.hasClass = function (className) {
return this.toArray().some(function (elem) {
var attrs = elem.attribs;
var clazz = attrs && attrs['class'];
var idx = -1;
var end;
if (clazz && className.length) {
while ((idx = clazz.indexOf(className, idx+1)) > -1) {
while ((idx = clazz.indexOf(className, idx + 1)) > -1) {
end = idx + className.length;
if ((idx === 0 || rspace.test(clazz[idx-1]))
&& (end === clazz.length || rspace.test(clazz[end]))) {
if (
(idx === 0 || rspace.test(clazz[idx - 1])) &&
(end === clazz.length || rspace.test(clazz[end]))
) {
return true;

@@ -361,6 +486,22 @@ }

exports.addClass = function(value) {
/**
* Adds class(es) to all of the matched elements. Also accepts a `function`
* like jQuery.
*
* @example
*
* $('.pear').addClass('fruit').html()
* //=> <li class="pear fruit">Pear</li>
*
* $('.apple').addClass('fruit red').html()
* //=> <li class="apple fruit red">Apple</li>
*
* @param {string} value - Name of new class.
*
* @see {@link http://api.jquery.com/addClass/}
*/
exports.addClass = function (value) {
// Support functions
if (typeof value === 'function') {
return domEach(this, function(i, el) {
return domEach(this, function (i, el) {
var className = el.attribs['class'] || '';

@@ -374,6 +515,5 @@ exports.addClass.call([el], value.call(el, i, className));

var classNames = value.split(rspace),
numElements = this.length;
var classNames = value.split(rspace);
var numElements = this.length;
for (var i = 0; i < numElements; i++) {

@@ -384,5 +524,5 @@ // If selected element isn't a tag, move on

// If we don't already have classes
var className = getAttr(this[i], 'class'),
numClasses,
setClass;
var className = getAttr(this[i], 'class');
var numClasses;
var setClass;

@@ -398,4 +538,3 @@ if (!className) {

var appendClass = classNames[j] + ' ';
if (setClass.indexOf(' ' + appendClass) < 0)
setClass += appendClass;
if (setClass.indexOf(' ' + appendClass) < 0) setClass += appendClass;
}

@@ -410,16 +549,29 @@

var splitClass = function(className) {
return className ? className.trim().split(rspace) : [];
};
/**
* Removes one or more space-separated classes from the selected elements. If
* no `className` is defined, all classes will be removed. Also accepts a
* `function` like jQuery.
*
* @example
*
* $('.pear').removeClass('pear').html()
* //=> <li class="">Pear</li>
*
* $('.apple').addClass('red').removeClass().html()
* //=> <li class="">Apple</li>
* @param {string} value - Name of the class.
*
* @see {@link http://api.jquery.com/removeClass/}
*/
exports.removeClass = function (value) {
var classes;
var numClasses;
var removeAll;
exports.removeClass = function(value) {
var classes,
numClasses,
removeAll;
// Handle if value is a function
if (typeof value === 'function') {
return domEach(this, function(i, el) {
return domEach(this, function (i, el) {
exports.removeClass.call(
[el], value.call(el, i, el.attribs['class'] || '')
[el],
value.call(el, i, el.attribs['class'] || '')
);

@@ -429,7 +581,7 @@ });

classes = splitClass(value);
classes = splitNames(value);
numClasses = classes.length;
removeAll = arguments.length === 0;
return domEach(this, function(i, el) {
return domEach(this, function (i, el) {
if (!isTag(el)) return;

@@ -441,5 +593,5 @@

} else {
var elClasses = splitClass(el.attribs.class),
index,
changed;
var elClasses = splitNames(el.attribs.class);
var index;
var changed;

@@ -465,6 +617,24 @@ for (var j = 0; j < numClasses; j++) {

exports.toggleClass = function(value, stateVal) {
/**
* Add or remove class(es) from the matched elements, depending on either the
* class's presence or the value of the switch argument. Also accepts a
* `function` like jQuery.
*
* @example
*
* $('.apple.green').toggleClass('fruit green red').html()
* //=> <li class="apple fruit red">Apple</li>
*
* $('.apple.green').toggleClass('fruit green red', true).html()
* //=> <li class="apple green fruit red">Apple</li>
*
* @param {(string|Function)} value - Name of the class. Can also be a function.
* @param {boolean} [stateVal] - If specified the state of the class.
*
* @see {@link http://api.jquery.com/toggleClass/}
*/
exports.toggleClass = function (value, stateVal) {
// Support functions
if (typeof value === 'function') {
return domEach(this, function(i, el) {
return domEach(this, function (i, el) {
exports.toggleClass.call(

@@ -481,8 +651,8 @@ [el],

var classNames = value.split(rspace),
numClasses = classNames.length,
state = typeof stateVal === 'boolean' ? stateVal ? 1 : -1 : 0,
numElements = this.length,
elementClasses,
index;
var classNames = value.split(rspace);
var numClasses = classNames.length;
var state = typeof stateVal === 'boolean' ? (stateVal ? 1 : -1) : 0;
var numElements = this.length;
var elementClasses;
var index;

@@ -493,3 +663,3 @@ for (var i = 0; i < numElements; i++) {

elementClasses = splitClass(this[i].attribs.class);
elementClasses = splitNames(this[i].attribs.class);

@@ -516,2 +686,13 @@ // Check if class already exists

/**
* Checks the current list of elements and returns `true` if _any_ of the
* elements match the selector. If using an element or Cheerio selection,
* returns `true` if _any_ of the elements match. If using a predicate
* function, the function is executed in the context of the selected element,
* so `this` refers to the current element.
*
* @param {string|Function|cheerio|node} selector - Selector for the selection.
*
* @see {@link http://api.jquery.com/is/}
*/
exports.is = function (selector) {

@@ -518,0 +699,0 @@ if (selector) {

@@ -1,27 +0,31 @@

var domEach = require('../utils').domEach,
_ = {
pick: require('lodash/pick'),
};
/**
* @module cheerio/css
*/
var domEach = require('../utils').domEach;
var toString = Object.prototype.toString;
/**
* Set / Get css.
* Get the value of a style property for the first element in the set of
* matched elements or set one or more CSS properties for every matched
* element.
*
* @param {String|Object} prop
* @param {String} val
* @return {self}
* @api public
* @param {string|object} prop - The name of the property.
* @param {string} [val] - If specified the new value.
* @returns {self}
*
* @see {@link http://api.jquery.com/css/}
*/
exports.css = function(prop, val) {
if (arguments.length === 2 ||
exports.css = function (prop, val) {
if (
arguments.length === 2 ||
// When `prop` is a "plain" object
(toString.call(prop) === '[object Object]')) {
return domEach(this, function(idx, el) {
toString.call(prop) === '[object Object]'
) {
return domEach(this, function (idx, el) {
setCss(el, prop, val, idx);
});
} else {
return getCss(this[0], prop);
}
return getCss(this[0], prop);
};

@@ -32,9 +36,9 @@

*
* @param {String|Object} prop
* @param {String} val
* @param {Number} idx - optional index within the selection
* @return {self}
* @api private
* @param {object} el - Element to set style of.
* @param {string|object} prop - Name of property.
* @param {string} val - Value to set property to.
* @param {number} [idx] - Optional index within the selection.
* @returns {self}
* @private
*/
function setCss(el, prop, val, idx) {

@@ -55,3 +59,3 @@ if ('string' == typeof prop) {

} else if ('object' == typeof prop) {
Object.keys(prop).forEach(function(k){
Object.keys(prop).forEach(function (k) {
setCss(el, k, prop[k]);

@@ -65,8 +69,12 @@ });

*
* @param {String} prop
* @return {Object}
* @api private
* @param {node} el - Element to get styles from.
* @param {string} prop - Name of the prop.
* @returns {object}
* @private
*/
function getCss(el, prop) {
if (!el || !el.attribs) {
return undefined;
}
function getCss(el, prop) {
var styles = parse(el.attribs.style);

@@ -76,6 +84,11 @@ if (typeof prop === 'string') {

} else if (Array.isArray(prop)) {
return _.pick(styles, prop);
} else {
return styles;
var newStyles = {};
prop.forEach(function (item) {
if (styles[item] != null) {
newStyles[item] = styles[item];
}
});
return newStyles;
}
return styles;
}

@@ -86,17 +99,10 @@

*
* @param {Object} obj
* @return {Object}
* @api private
* @param {object} obj - Object to stringify.
* @returns {object}
* @private
*/
function stringify(obj) {
return Object.keys(obj || {})
.reduce(function(str, prop){
return str += ''
+ (str ? ' ' : '')
+ prop
+ ': '
+ obj[prop]
+ ';';
}, '');
return Object.keys(obj || {}).reduce(function (str, prop) {
return (str += '' + (str ? ' ' : '') + prop + ': ' + obj[prop] + ';');
}, '');
}

@@ -107,7 +113,6 @@

*
* @param {String} styles
* @return {Object}
* @api private
* @param {string} styles - Styles to be parsed.
* @returns {object}
* @private
*/
function parse(styles) {

@@ -118,11 +123,9 @@ styles = (styles || '').trim();

return styles
.split(';')
.reduce(function(obj, str){
var n = str.indexOf(':');
// skip if there is no :, or if it is the first/last character
if (n < 1 || n === str.length-1) return obj;
obj[str.slice(0,n).trim()] = str.slice(n+1).trim();
return obj;
}, {});
return styles.split(';').reduce(function (obj, str) {
var n = str.indexOf(':');
// skip if there is no :, or if it is the first/last character
if (n < 1 || n === str.length - 1) return obj;
obj[str.slice(0, n).trim()] = str.slice(n + 1).trim();
return obj;
}, {});
}

@@ -0,11 +1,17 @@

/**
* @module cheerio/forms
*/
// https://github.com/jquery/jquery/blob/2.1.3/src/manipulation/var/rcheckableType.js
// https://github.com/jquery/jquery/blob/2.1.3/src/serialize.js
var submittableSelector = 'input,select,textarea,keygen',
r20 = /%20/g,
rCRLF = /\r?\n/g,
_ = {
map: require('lodash/map')
};
var submittableSelector = 'input,select,textarea,keygen';
var r20 = /%20/g;
var rCRLF = /\r?\n/g;
exports.serialize = function() {
/**
* Encode a set of form elements as a string for submission.
*
* @see {@link http://api.jquery.com/serialize/}
*/
exports.serialize = function () {
// Convert form elements into name/value objects

@@ -15,3 +21,3 @@ var arr = this.serializeArray();

// Serialize each element into a key/value string
var retArr = _.map(arr, function(data) {
var retArr = arr.map(function (data) {
return encodeURIComponent(data.name) + '=' + encodeURIComponent(data.value);

@@ -24,22 +30,32 @@ });

exports.serializeArray = function() {
/**
* Encode a set of form elements as an array of names and values.
*
* @example
* $('<form><input name="foo" value="bar" /></form>').serializeArray()
* //=> [ { name: 'foo', value: 'bar' } ]
*
* @see {@link http://api.jquery.com/serializeArray/}
*/
exports.serializeArray = function () {
// Resolve all form elements from either forms or collections of form elements
var Cheerio = this.constructor;
return this.map(function() {
var elem = this;
var $elem = Cheerio(elem);
if (elem.name === 'form') {
return $elem.find(submittableSelector).toArray();
} else {
return $elem.filter(submittableSelector).toArray();
}
}).filter(
// Verify elements have a name (`attr.name`) and are not disabled (`:disabled`)
'[name!=""]:not(:disabled)'
return this.map(function () {
var elem = this;
var $elem = Cheerio(elem);
if (elem.name === 'form') {
return $elem.find(submittableSelector).toArray();
}
return $elem.filter(submittableSelector).toArray();
})
.filter(
// Verify elements have a name (`attr.name`) and are not disabled (`:disabled`)
'[name!=""]:not(:disabled)' +
// and cannot be clicked (`[type=submit]`) or are used in `x-www-form-urlencoded` (`[type=file]`)
+ ':not(:submit, :button, :image, :reset, :file)'
':not(:submit, :button, :image, :reset, :file)' +
// and are either checked/don't have a checkable state
+ ':matches([checked], :not(:checkbox, :radio))'
// Convert each of the elements to its value(s)
).map(function(i, elem) {
':matches([checked], :not(:checkbox, :radio))'
// Convert each of the elements to its value(s)
)
.map(function (i, elem) {
var $elem = Cheerio(elem);

@@ -56,13 +72,14 @@ var name = $elem.attr('name');

if (Array.isArray(value)) {
return _.map(value, function(val) {
return value.map(function (val) {
// We trim replace any line endings (e.g. `\r` or `\r\n` with `\r\n`) to guarantee consistency across platforms
// These can occur inside of `<textarea>'s`
return {name: name, value: val.replace( rCRLF, '\r\n' )};
return { name: name, value: val.replace(rCRLF, '\r\n') };
});
// Otherwise (e.g. `<input type="text">`, return only one key/value pair
} else {
return {name: name, value: value.replace( rCRLF, '\r\n' )};
// Otherwise (e.g. `<input type="text">`, return only one key/value pair
}
// Convert our result to an array
}).get();
return { name: name, value: value.replace(rCRLF, '\r\n') };
// Convert our result to an array
})
.get();
};

@@ -1,18 +0,27 @@

var parse = require('../parse'),
$ = require('../static'),
updateDOM = parse.update,
evaluate = parse.evaluate,
utils = require('../utils'),
domEach = utils.domEach,
cloneDom = utils.cloneDom,
isHtml = utils.isHtml,
slice = Array.prototype.slice,
_ = {
flatten: require('lodash/flatten'),
bind: require('lodash/bind'),
forEach: require('lodash/forEach')
};
/**
* Methods for modifying the DOM structure.
*
* @module cheerio/manipulation
*/
// Create an array of nodes, recursing into arrays and parsing strings if
// necessary
var parse = require('../parse');
var html = require('../static').html;
var text = require('../static').text;
var updateDOM = parse.update;
var utils = require('../utils');
var domEach = utils.domEach;
var cloneDom = utils.cloneDom;
var isHtml = utils.isHtml;
var slice = Array.prototype.slice;
var domhandler = require('domhandler');
var DomUtils = require('htmlparser2').DomUtils;
/**
* Create an array of nodes, recursing into arrays and parsing strings if
* necessary.
*
* @param {cheerio|string|cheerio[]|string[]} [elem] - Elements to make an array of.
* @param {boolean} [clone] - Optionally clone nodes.
* @private
*/
exports._makeDomArray = function makeDomArray(elem, clone) {

@@ -24,22 +33,25 @@ if (elem == null) {

} else if (Array.isArray(elem)) {
return _.flatten(elem.map(function(el) {
return this._makeDomArray(el, clone);
}, this));
return elem.reduce(
function (newElems, el) {
return newElems.concat(this._makeDomArray(el, clone));
}.bind(this),
[]
);
} else if (typeof elem === 'string') {
return evaluate(elem, this.options, false);
} else {
return clone ? cloneDom([elem]) : [elem];
return parse(elem, this.options, false).children;
}
return clone ? cloneDom([elem]) : [elem];
};
var _insert = function(concatenator) {
return function() {
var elems = slice.call(arguments),
lastIdx = this.length - 1;
var _insert = function (concatenator) {
return function () {
var elems = slice.call(arguments);
var lastIdx = this.length - 1;
return domEach(this, function(i, el) {
var dom, domSrc;
return domEach(this, function (i, el) {
var dom;
var domSrc;
if (typeof elems[0] === 'function') {
domSrc = elems[0].call(el, i, $.html(el.children));
domSrc = elems[0].call(el, i, html(el.children));
} else {

@@ -64,9 +76,13 @@ domSrc = elems;

*
* @api private
* @private
*/
var uniqueSplice = function(array, spliceIdx, spliceCount, newElems, parent) {
var spliceArgs = [spliceIdx, spliceCount].concat(newElems),
prev = array[spliceIdx - 1] || null,
next = array[spliceIdx] || null;
var idx, len, prevIdx, node, oldParent;
var uniqueSplice = function (array, spliceIdx, spliceCount, newElems, parent) {
var spliceArgs = [spliceIdx, spliceCount].concat(newElems);
var prev = array[spliceIdx - 1] || null;
var next = array[spliceIdx + spliceCount] || null;
var idx;
var len;
var prevIdx;
var node;
var oldParent;

@@ -77,3 +93,3 @@ // Before splicing in new elements, ensure they do not already appear in the

node = newElems[idx];
oldParent = node.parent || node.root;
oldParent = node.parent;
prevIdx = oldParent && oldParent.children.indexOf(newElems[idx]);

@@ -88,3 +104,2 @@

node.root = null;
node.parent = parent;

@@ -113,5 +128,29 @@

exports.appendTo = function(target) {
/**
* Insert every element in the set of matched elements to the end of the
* target.
*
* @param {string|cheerio} target - Element to append elements to.
*
* @example
*
* $('<li class="plum">Plum</li>').appendTo('#fruits')
* $.html()
* //=> <ul id="fruits">
* // <li class="apple">Apple</li>
* // <li class="orange">Orange</li>
* // <li class="pear">Pear</li>
* // <li class="plum">Plum</li>
* // </ul>
*
* @see {@link http://api.jquery.com/appendTo/}
*/
exports.appendTo = function (target) {
if (!target.cheerio) {
target = this.constructor.call(this.constructor, target, null, this._originalRoot);
target = this.constructor.call(
this.constructor,
target,
null,
this._originalRoot
);
}

@@ -124,5 +163,29 @@

exports.prependTo = function(target) {
/**
* Insert every element in the set of matched elements to the beginning of the
* target.
*
* @param {string|cheerio} target - Element to prepend elements to.
*
* @example
*
* $('<li class="plum">Plum</li>').prependTo('#fruits')
* $.html()
* //=> <ul id="fruits">
* // <li class="plum">Plum</li>
* // <li class="apple">Apple</li>
* // <li class="orange">Orange</li>
* // <li class="pear">Pear</li>
* // </ul>
*
* @see {@link http://api.jquery.com/prependTo/}
*/
exports.prependTo = function (target) {
if (!target.cheerio) {
target = this.constructor.call(this.constructor, target, null, this._originalRoot);
target = this.constructor.call(
this.constructor,
target,
null,
this._originalRoot
);
}

@@ -135,67 +198,210 @@

exports.append = _insert(function(dom, children, parent) {
/**
* Inserts content as the *last* child of each of the selected elements.
*
* @function
*
* @example
*
* $('ul').append('<li class="plum">Plum</li>')
* $.html()
* //=> <ul id="fruits">
* // <li class="apple">Apple</li>
* // <li class="orange">Orange</li>
* // <li class="pear">Pear</li>
* // <li class="plum">Plum</li>
* // </ul>
*
* @see {@link http://api.jquery.com/append/}
*/
exports.append = _insert(function (dom, children, parent) {
uniqueSplice(children, children.length, 0, dom, parent);
});
exports.prepend = _insert(function(dom, children, parent) {
/**
* Inserts content as the *first* child of each of the selected elements.
*
* @function
*
* @example
*
* $('ul').prepend('<li class="plum">Plum</li>')
* $.html()
* //=> <ul id="fruits">
* // <li class="plum">Plum</li>
* // <li class="apple">Apple</li>
* // <li class="orange">Orange</li>
* // <li class="pear">Pear</li>
* // </ul>
*
* @see {@link http://api.jquery.com/prepend/}
*/
exports.prepend = _insert(function (dom, children, parent) {
uniqueSplice(children, 0, 0, dom, parent);
});
exports.wrap = function(wrapper) {
var wrapperFn = typeof wrapper === 'function' && wrapper,
lastIdx = this.length - 1;
function _wrap(insert) {
return function (wrapper) {
var wrapperFn = typeof wrapper === 'function' && wrapper;
var lastIdx = this.length - 1;
var lastParent = this.parents().last();
_.forEach(this, _.bind(function(el, i) {
var parent = el.parent || el.root,
siblings = parent.children,
wrapperDom, elInsertLocation, j, index;
for (var i = 0; i < this.length; i++) {
var el = this[i];
var wrapperDom;
var elInsertLocation;
var j;
if (!parent) {
return;
}
if (wrapperFn) {
wrapper = wrapperFn.call(el, i);
}
if (wrapperFn) {
wrapper = wrapperFn.call(el, i);
}
if (typeof wrapper === 'string' && !isHtml(wrapper)) {
wrapper = lastParent.find(wrapper).clone();
}
if (typeof wrapper === 'string' && !isHtml(wrapper)) {
wrapper = this.parents().last().find(wrapper).clone();
}
wrapperDom = this._makeDomArray(wrapper, i < lastIdx).slice(0, 1);
elInsertLocation = wrapperDom[0];
// Find the deepest child. Only consider the first tag child of each node
// (ignore text); stop if no children are found.
j = 0;
wrapperDom = this._makeDomArray(wrapper, i < lastIdx).slice(0, 1);
elInsertLocation = wrapperDom[0];
// Find the deepest child. Only consider the first tag child of each node
// (ignore text); stop if no children are found.
j = 0;
while (elInsertLocation && elInsertLocation.children) {
if (j >= elInsertLocation.children.length) {
break;
}
while (elInsertLocation && elInsertLocation.children) {
if (j >= elInsertLocation.children.length) {
break;
if (elInsertLocation.children[j].type === 'tag') {
elInsertLocation = elInsertLocation.children[j];
j = 0;
} else {
j++;
}
}
if (elInsertLocation.children[j].type === 'tag') {
elInsertLocation = elInsertLocation.children[j];
j=0;
} else {
j++;
}
insert(el, elInsertLocation, wrapperDom);
}
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);
}, this));
return this;
};
}
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.
*
* @param {cheerio} wrapper - The DOM structure to wrap around each element in the selection.
*
* @example
*
* const redFruit = $('<div class="red-fruit"></div>')
* $('.apple').wrap(redFruit)
*
* //=> <ul id="fruits">
* // <div class="red-fruit">
* // <li class="apple">Apple</li>
* // </div>
* // <li class="orange">Orange</li>
* // <li class="plum">Plum</li>
* // </ul>
*
* const healthy = $('<div class="healthy"></div>')
* $('li').wrap(healthy)
*
* //=> <ul id="fruits">
* // <div class="healthy">
* // <li class="apple">Apple</li>
* // </div>
* // <div class="healthy">
* // <li class="orange">Orange</li>
* // </div>
* // <div class="healthy">
* // <li class="plum">Plum</li>
* // </div>
* // </ul>
*
* @see {@link http://api.jquery.com/wrap/}
*/
exports.wrap = _wrap(function (el, elInsertLocation, wrapperDom) {
var parent = el.parent;
var siblings = parent.children;
var index = siblings.indexOf(el);
exports.after = function() {
var elems = slice.call(arguments),
lastIdx = this.length - 1;
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);
});
domEach(this, function(i, el) {
var parent = el.parent || el.root;
/**
* 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.
*
* @param {cheerio} wrapper - The DOM structure to wrap around the content of each element in the selection.
*
* @example
*
* const redFruit = $('<div class="red-fruit"></div>')
* $('.apple').wrapInner(redFruit)
*
* //=> <ul id="fruits">
* // <li class="apple">
* // <div class="red-fruit">Apple</div>
* // </li>
* // <li class="orange">Orange</li>
* // <li class="pear">Pear</li>
* // </ul>
*
* const healthy = $('<div class="healthy"></div>')
* $('li').wrapInner(healthy)
*
* //=> <ul id="fruits">
* // <li class="apple">
* // <div class="healthy">Apple</div>
* // </li>
* // <li class="orange">
* // <div class="healthy">Orange</div>
* // </li>
* // <li class="pear">
* // <div class="healthy">Pear</div>
* // </li>
* // </ul>
*
* @see {@link http://api.jquery.com/wrapInner/}
*/
exports.wrapInner = _wrap(function (el, elInsertLocation, wrapperDom) {
updateDOM(el.children, elInsertLocation);
updateDOM(wrapperDom, el);
});
/**
* Insert content next to each element in the set of matched elements.
*
* @example
*
* $('.apple').after('<li class="plum">Plum</li>')
* $.html()
* //=> <ul id="fruits">
* // <li class="apple">Apple</li>
* // <li class="plum">Plum</li>
* // <li class="orange">Orange</li>
* // <li class="pear">Pear</li>
* // </ul>
*
* @see {@link http://api.jquery.com/after/}
*/
exports.after = function () {
var elems = slice.call(arguments);
var lastIdx = this.length - 1;
domEach(this, function (i, el) {
var parent = el.parent;
if (!parent) {

@@ -205,5 +411,6 @@ return;

var siblings = parent.children,
index = siblings.indexOf(el),
domSrc, dom;
var siblings = parent.children;
var index = siblings.indexOf(el);
var domSrc;
var dom;

@@ -214,3 +421,3 @@ // If not found, move on

if (typeof elems[0] === 'function') {
domSrc = elems[0].call(el, i, $.html(el.children));
domSrc = elems[0].call(el, i, html(el.children));
} else {

@@ -228,13 +435,36 @@ domSrc = elems;

exports.insertAfter = function(target) {
var clones = [],
self = this;
/**
* Insert every element in the set of matched elements after the target.
*
* @example
*
* $('<li class="plum">Plum</li>').insertAfter('.apple')
* $.html()
* //=> <ul id="fruits">
* // <li class="apple">Apple</li>
* // <li class="plum">Plum</li>
* // <li class="orange">Orange</li>
* // <li class="pear">Pear</li>
* // </ul>
*
* @param {string|cheerio} target - Element to insert elements after.
*
* @see {@link http://api.jquery.com/insertAfter/}
*/
exports.insertAfter = function (target) {
var clones = [];
var self = this;
if (typeof target === 'string') {
target = this.constructor.call(this.constructor, target, null, this._originalRoot);
target = this.constructor.call(
this.constructor,
target,
null,
this._originalRoot
);
}
target = this._makeDomArray(target);
self.remove();
domEach(target, function(i, el) {
domEach(target, function (i, el) {
var clonedSelf = self._makeDomArray(self.clone());
var parent = el.parent || el.root;
var parent = el.parent;
if (!parent) {

@@ -244,4 +474,4 @@ return;

var siblings = parent.children,
index = siblings.indexOf(el);
var siblings = parent.children;
var index = siblings.indexOf(el);

@@ -258,8 +488,24 @@ // If not found, move on

exports.before = function() {
var elems = slice.call(arguments),
lastIdx = this.length - 1;
/**
* Insert content previous to each element in the set of matched elements.
*
* @example
*
* $('.apple').before('<li class="plum">Plum</li>')
* $.html()
* //=> <ul id="fruits">
* // <li class="plum">Plum</li>
* // <li class="apple">Apple</li>
* // <li class="orange">Orange</li>
* // <li class="pear">Pear</li>
* // </ul>
*
* @see {@link http://api.jquery.com/before/}
*/
exports.before = function () {
var elems = slice.call(arguments);
var lastIdx = this.length - 1;
domEach(this, function(i, el) {
var parent = el.parent || el.root;
domEach(this, function (i, el) {
var parent = el.parent;
if (!parent) {

@@ -269,5 +515,6 @@ return;

var siblings = parent.children,
index = siblings.indexOf(el),
domSrc, dom;
var siblings = parent.children;
var index = siblings.indexOf(el);
var domSrc;
var dom;

@@ -278,3 +525,3 @@ // If not found, move on

if (typeof elems[0] === 'function') {
domSrc = elems[0].call(el, i, $.html(el.children));
domSrc = elems[0].call(el, i, html(el.children));
} else {

@@ -293,13 +540,36 @@ domSrc = elems;

exports.insertBefore = function(target) {
var clones = [],
self = this;
/**
* Insert every element in the set of matched elements before the target.
*
* @example
*
* $('<li class="plum">Plum</li>').insertBefore('.apple')
* $.html()
* //=> <ul id="fruits">
* // <li class="plum">Plum</li>
* // <li class="apple">Apple</li>
* // <li class="orange">Orange</li>
* // <li class="pear">Pear</li>
* // </ul>
*
* @param {string|cheerio} target - Element to insert elements before.
*
* @see {@link http://api.jquery.com/insertBefore/}
*/
exports.insertBefore = function (target) {
var clones = [];
var self = this;
if (typeof target === 'string') {
target = this.constructor.call(this.constructor, target, null, this._originalRoot);
target = this.constructor.call(
this.constructor,
target,
null,
this._originalRoot
);
}
target = this._makeDomArray(target);
self.remove();
domEach(target, function(i, el) {
domEach(target, function (i, el) {
var clonedSelf = self._makeDomArray(self.clone());
var parent = el.parent || el.root;
var parent = el.parent;
if (!parent) {

@@ -309,4 +579,4 @@ return;

var siblings = parent.children,
index = siblings.indexOf(el);
var siblings = parent.children;
var index = siblings.indexOf(el);

@@ -323,31 +593,28 @@ // If not found, move on

/*
remove([selector])
*/
exports.remove = function(selector) {
/**
* Removes the set of matched elements from the DOM and all their children.
* `selector` filters the set of matched elements to be removed.
*
* @example
*
* $('.pear').remove()
* $.html()
* //=> <ul id="fruits">
* // <li class="apple">Apple</li>
* // <li class="orange">Orange</li>
* // </ul>
*
* @param {string} [selector] - Optional selector for elements to remove.
*
* @see {@link http://api.jquery.com/remove/}
*/
exports.remove = function (selector) {
var elems = this;
// Filter if we have selector
if (selector)
elems = elems.filter(selector);
if (selector) elems = elems.filter(selector);
domEach(elems, function(i, el) {
var parent = el.parent || el.root;
if (!parent) {
return;
}
var siblings = parent.children,
index = siblings.indexOf(el);
if (index < 0) return;
siblings.splice(index, 1);
if (el.prev) {
el.prev.next = el.next;
}
if (el.next) {
el.next.prev = el.prev;
}
el.prev = el.next = el.parent = el.root = null;
domEach(elems, function (i, el) {
DomUtils.removeElement(el);
el.prev = el.next = el.parent = null;
});

@@ -358,7 +625,25 @@

exports.replaceWith = function(content) {
/**
* Replaces matched elements with `content`.
*
* @example
*
* const plum = $('<li class="plum">Plum</li>')
* $('.pear').replaceWith(plum)
* $.html()
* //=> <ul id="fruits">
* // <li class="apple">Apple</li>
* // <li class="orange">Orange</li>
* // <li class="plum">Plum</li>
* // </ul>
*
* @param {cheerio|Function} content - Replacement for matched elements.
*
* @see {@link http://api.jquery.com/replaceWith/}
*/
exports.replaceWith = function (content) {
var self = this;
domEach(this, function(i, el) {
var parent = el.parent || el.root;
domEach(this, function (i, el) {
var parent = el.parent;
if (!parent) {

@@ -368,5 +653,7 @@ return;

var siblings = parent.children,
dom = self._makeDomArray(typeof content === 'function' ? content.call(el, i, el) : content),
index;
var siblings = parent.children;
var dom = self._makeDomArray(
typeof content === 'function' ? content.call(el, i, el) : content
);
var index;

@@ -381,3 +668,3 @@ // In the case that `dom` contains nodes that already exist in other

uniqueSplice(siblings, index, 1, dom, parent);
el.parent = el.prev = el.next = el.root = null;
el.parent = el.prev = el.next = null;
});

@@ -388,5 +675,16 @@

exports.empty = function() {
domEach(this, function(i, el) {
_.forEach(el.children, function(child) {
/**
* Empties an element, removing all its children.
*
* @example
*
* $('ul').empty()
* $.html()
* //=> <ul id="fruits"></ul>
*
* @see {@link http://api.jquery.com/empty/}
*/
exports.empty = function () {
domEach(this, function (i, el) {
el.children.forEach(function (child) {
child.next = child.prev = child.parent = null;

@@ -401,8 +699,22 @@ });

/**
* Set/Get the HTML
* Gets an HTML content string from the first selected element. If `htmlString`
* is specified, each selected element's content is replaced by the new
* content.
*
* @param {string} str - If specified used to replace selection's contents.
*
* @example
*
* $('.orange').html()
* //=> Orange
*
* $('#fruits').html('<li class="mango">Mango</li>').html()
* //=> <li class="mango">Mango</li>
*
* @see {@link http://api.jquery.com/html/}
*/
exports.html = function(str) {
exports.html = function (str) {
if (str === undefined) {
if (!this[0] || !this[0].children) return null;
return $.html(this[0].children, this.options);
return html(this[0].children, this.options);
}

@@ -412,8 +724,10 @@

domEach(this, function(i, el) {
_.forEach(el.children, function(child) {
domEach(this, function (i, el) {
el.children.forEach(function (child) {
child.next = child.prev = child.parent = null;
});
var content = str.cheerio ? str.clone().get() : evaluate('' + str, opts, false);
var content = str.cheerio
? str.clone().get()
: parse('' + str, opts, false).children;

@@ -426,15 +740,34 @@ updateDOM(content, el);

exports.toString = function() {
return $.html(this, this.options);
exports.toString = function () {
return html(this, this.options);
};
exports.text = function(str) {
/**
* Get the combined text contents of each element in the set of matched
* elements, including their descendants. If `textString` is specified, each
* selected element's content is replaced by the new text content.
*
* @param {string} [str] - If specified replacement for the selected element's contents.
*
* @example
*
* $('.orange').text()
* //=> Orange
*
* $('ul').text()
* //=> Apple
* // Orange
* // Pear
*
* @see {@link http://api.jquery.com/text/}
*/
exports.text = function (str) {
// If `str` is undefined, act as a "getter"
if (str === undefined) {
return $.text(this);
return text(this);
} else if (typeof str === 'function') {
// Function support
return domEach(this, function(i, el) {
var $el = [el];
return exports.text.call($el, str.call(el, i, $.text($el)));
var self = this;
return domEach(this, function (i, el) {
return exports.text.call(self._make(el), str.call(el, i, text([el])));
});

@@ -444,17 +777,10 @@ }

// Append text node to each selected elements
domEach(this, function(i, el) {
_.forEach(el.children, function(child) {
domEach(this, function (i, el) {
el.children.forEach(function (child) {
child.next = child.prev = child.parent = null;
});
var elem = {
data: '' + str,
type: 'text',
parent: el,
prev: null,
next: null,
children: []
};
var textNode = new domhandler.Text(str);
updateDOM(elem, el);
updateDOM(textNode, el);
});

@@ -465,4 +791,13 @@

exports.clone = function() {
/**
* Clone the cheerio object.
*
* @example
*
* const moreFruit = $('#fruits').clone()
*
* @see {@link http://api.jquery.com/clone/}
*/
exports.clone = function () {
return this._make(cloneDom(this.get(), this.options));
};

@@ -1,17 +0,31 @@

var select = require('css-select'),
utils = require('../utils'),
domEach = utils.domEach,
uniqueSort = require('htmlparser2').DomUtils.uniqueSort,
isTag = utils.isTag,
_ = {
bind: require('lodash/bind'),
forEach: require('lodash/forEach'),
reject: require('lodash/reject'),
filter: require('lodash/filter'),
reduce: require('lodash/reduce')
};
/**
* Methods for traversing the DOM structure.
*
* @module cheerio/traversing
*/
exports.find = function(selectorOrHaystack) {
var elems = _.reduce(this, function(memo, elem) {
return memo.concat(_.filter(elem.children, isTag));
var select = require('cheerio-select');
var utils = require('../utils');
var domEach = utils.domEach;
var uniqueSort = require('htmlparser2').DomUtils.uniqueSort;
var isTag = utils.isTag;
/**
* Get the descendants of each element in the current set of matched elements,
* filtered by a selector, jQuery object, or element.
*
* @example
*
* $('#fruits').find('li').length
* //=> 3
* $('#fruits').find($('.apple')).length
* //=> 1
*
* @param {string|cheerio|node} selectorOrHaystack - Element to look for.
*
* @see {@link http://api.jquery.com/find/}
*/
exports.find = function (selectorOrHaystack) {
var elems = this.toArray().reduce(function (newElems, elem) {
return newElems.concat(elem.children.filter(isTag));
}, []);

@@ -28,25 +42,43 @@ var contains = this.constructor.contains;

return this._make(haystack.filter(function(elem) {
var idx, len;
for (idx = 0, len = this.length; idx < len; ++idx) {
if (contains(this[idx], elem)) {
return true;
return this._make(
haystack.filter(function (elem) {
var idx;
var len;
for (idx = 0, len = this.length; idx < len; ++idx) {
if (contains(this[idx], elem)) {
return true;
}
}
}
}, this));
}, this)
);
}
var options = {__proto__: this.options, context: this.toArray()};
var options = { __proto__: this.options, context: this.toArray() };
return this._make(select(selectorOrHaystack, elems, options));
return this._make(select.select(selectorOrHaystack || '', elems, options));
};
// Get the parent of each element in the current set of matched elements,
// optionally filtered by a selector.
exports.parent = function(selector) {
/**
* Get the parent of each element in the current set of matched elements,
* optionally filtered by a selector.
*
* @example
*
* $('.pear').parent().attr('id')
* //=> fruits
*
* @param {string} [selector] - If specified filter for parent.
*
* @see {@link http://api.jquery.com/parent/}
*/
exports.parent = function (selector) {
var set = [];
domEach(this, function(idx, elem) {
domEach(this, function (idx, elem) {
var parentElem = elem.parent;
if (parentElem && set.indexOf(parentElem) < 0) {
if (
parentElem &&
parentElem.type !== 'root' &&
set.indexOf(parentElem) < 0
) {
set.push(parentElem);

@@ -63,3 +95,18 @@ }

exports.parents = function(selector) {
/**
* Get a set of parents filtered by `selector` of each element in the current
* set of match elements.
*
* @example
*
* $('.orange').parents().length
* // => 2
* $('.orange').parents('#fruits').length
* // => 1
*
* @param {string} [selector] - If specified filter for parents.
*
* @see {@link http://api.jquery.com/parents/}
*/
exports.parents = function (selector) {
var parentNodes = [];

@@ -70,11 +117,13 @@

// removed.
this.get().reverse().forEach(function(elem) {
traverseParents(this, elem.parent, selector, Infinity)
.forEach(function(node) {
this.get()
.reverse()
.forEach(function (elem) {
traverseParents(this, elem.parent, selector, Infinity).forEach(function (
node
) {
if (parentNodes.indexOf(node) === -1) {
parentNodes.push(node);
}
}
);
}, this);
});
}, this);

@@ -84,7 +133,28 @@ return this._make(parentNodes);

exports.parentsUntil = function(selector, filter) {
var parentNodes = [], untilNode, untilNodes;
/**
* Get the ancestors of each element in the current set of matched elements, up
* to but not including the element matched by the selector, DOM node, or
* cheerio object.
*
* @example
*
* $('.orange').parentsUntil('#food').length
* // => 1
*
* @param {string|node|cheerio} selector - Selector for element to stop at.
* @param {string|Function} [filter] - Optional filter for parents.
*
* @see {@link http://api.jquery.com/parentsUntil/}
*/
exports.parentsUntil = function (selector, filter) {
var parentNodes = [];
var untilNode;
var untilNodes;
if (typeof selector === 'string') {
untilNode = select(selector, this.parents().toArray(), this.options)[0];
untilNode = select.select(
selector,
this.parents().toArray(),
this.options
)[0];
} else if (selector && selector.cheerio) {

@@ -100,21 +170,46 @@ untilNodes = selector.toArray();

this.toArray().reverse().forEach(function(elem) {
while ((elem = elem.parent)) {
if ((untilNode && elem !== untilNode) ||
(untilNodes && untilNodes.indexOf(elem) === -1) ||
(!untilNode && !untilNodes)) {
if (isTag(elem) && parentNodes.indexOf(elem) === -1) { parentNodes.push(elem); }
} else {
break;
this.toArray()
.reverse()
.forEach(function (elem) {
while ((elem = elem.parent)) {
if (
(untilNode && elem !== untilNode) ||
(untilNodes && untilNodes.indexOf(elem) === -1) ||
(!untilNode && !untilNodes)
) {
if (isTag(elem) && parentNodes.indexOf(elem) === -1) {
parentNodes.push(elem);
}
} else {
break;
}
}
}
}, this);
}, this);
return this._make(filter ? select(filter, parentNodes, this.options) : parentNodes);
return this._make(
filter ? select.select(filter, parentNodes, this.options) : parentNodes
);
};
// For each element in the set, get the first element that matches the selector
// by testing the element itself and traversing up through its ancestors in the
// DOM tree.
exports.closest = function(selector) {
/**
* For each element in the set, get the first element that matches the selector
* by testing the element itself and traversing up through its ancestors in
* the DOM tree.
*
* @example
*
* $('.orange').closest()
* // => []
* $('.orange').closest('.apple')
* // => []
* $('.orange').closest('li')
* // => [<li class="orange">Orange</li>]
* $('.orange').closest('#fruits')
* // => [<ul id="fruits"> ... </ul>]
*
* @param {string} [selector] - Selector for the element to find.
*
* @see {@link http://api.jquery.com/closest/}
*/
exports.closest = function (selector) {
var set = [];

@@ -126,3 +221,3 @@

domEach(this, function(idx, elem) {
domEach(this, function (idx, elem) {
var closestElem = traverseParents(this, elem, selector, 1)[0];

@@ -134,3 +229,3 @@

}
}.bind(this));
});

@@ -140,7 +235,22 @@ return this._make(set);

exports.next = function(selector) {
if (!this[0]) { return this; }
/**
* Gets the next sibling of the first selected element, optionally filtered by
* a selector.
*
* @example
*
* $('.apple').next().hasClass('orange')
* //=> true
*
* @param {string} [selector] - If specified filter for sibling.
*
* @see {@link http://api.jquery.com/next/}
*/
exports.next = function (selector) {
if (!this[0]) {
return this;
}
var elems = [];
_.forEach(this, function(elem) {
this.toArray().forEach(function (elem) {
while ((elem = elem.next)) {

@@ -154,12 +264,29 @@ if (isTag(elem)) {

return selector ?
exports.filter.call(elems, selector, this) :
this._make(elems);
return selector
? exports.filter.call(elems, selector, this)
: this._make(elems);
};
exports.nextAll = function(selector) {
if (!this[0]) { return this; }
/**
* Gets all the following siblings of the first selected element, optionally
* filtered by a selector.
*
* @example
*
* $('.apple').nextAll()
* //=> [<li class="orange">Orange</li>, <li class="pear">Pear</li>]
* $('.apple').nextAll('.orange')
* //=> [<li class="orange">Orange</li>]
*
* @param {string} [selector] - If specified filter for siblings.
*
* @see {@link http://api.jquery.com/nextAll/}
*/
exports.nextAll = function (selector) {
if (!this[0]) {
return this;
}
var elems = [];
_.forEach(this, function(elem) {
this.toArray().forEach(function (elem) {
while ((elem = elem.next)) {

@@ -172,13 +299,31 @@ if (isTag(elem) && elems.indexOf(elem) === -1) {

return selector ?
exports.filter.call(elems, selector, this) :
this._make(elems);
return selector
? exports.filter.call(elems, selector, this)
: this._make(elems);
};
exports.nextUntil = function(selector, filterSelector) {
if (!this[0]) { return this; }
var elems = [], untilNode, untilNodes;
/**
* Gets all the following siblings up to but not including the element matched
* by the selector, optionally filtered by another selector.
*
* @example
*
* $('.apple').nextUntil('.pear')
* //=> [<li class="orange">Orange</li>]
*
* @param {string|cheerio|node} selector - Selector for element to stop at.
* @param {string} [filterSelector] - If specified filter for siblings.
*
* @see {@link http://api.jquery.com/nextUntil/}
*/
exports.nextUntil = function (selector, filterSelector) {
if (!this[0]) {
return this;
}
var elems = [];
var untilNode;
var untilNodes;
if (typeof selector === 'string') {
untilNode = select(selector, this.nextAll().get(), this.options)[0];
untilNode = select.select(selector, this.nextAll().get(), this.options)[0];
} else if (selector && selector.cheerio) {

@@ -190,7 +335,9 @@ untilNodes = selector.get();

_.forEach(this, function(elem) {
this.toArray().forEach(function (elem) {
while ((elem = elem.next)) {
if ((untilNode && elem !== untilNode) ||
if (
(untilNode && elem !== untilNode) ||
(untilNodes && untilNodes.indexOf(elem) === -1) ||
(!untilNode && !untilNodes)) {
(!untilNode && !untilNodes)
) {
if (isTag(elem) && elems.indexOf(elem) === -1) {

@@ -205,12 +352,27 @@ elems.push(elem);

return filterSelector ?
exports.filter.call(elems, filterSelector, this) :
this._make(elems);
return filterSelector
? exports.filter.call(elems, filterSelector, this)
: this._make(elems);
};
exports.prev = function(selector) {
if (!this[0]) { return this; }
/**
* Gets the previous sibling of the first selected element optionally filtered
* by a selector.
*
* @example
*
* $('.orange').prev().hasClass('apple')
* //=> true
*
* @param {string} [selector] - If specified filter for siblings.
*
* @see {@link http://api.jquery.com/prev/}
*/
exports.prev = function (selector) {
if (!this[0]) {
return this;
}
var elems = [];
_.forEach(this, function(elem) {
this.toArray().forEach(function (elem) {
while ((elem = elem.prev)) {

@@ -224,12 +386,29 @@ if (isTag(elem)) {

return selector ?
exports.filter.call(elems, selector, this) :
this._make(elems);
return selector
? exports.filter.call(elems, selector, this)
: this._make(elems);
};
exports.prevAll = function(selector) {
if (!this[0]) { return this; }
/**
* Gets all the preceding siblings of the first selected element, optionally
* filtered by a selector.
*
* @example
*
* $('.pear').prevAll()
* //=> [<li class="orange">Orange</li>, <li class="apple">Apple</li>]
* $('.pear').prevAll('.orange')
* //=> [<li class="orange">Orange</li>]
*
* @param {string} [selector] - If specified filter for siblings.
*
* @see {@link http://api.jquery.com/prevAll/}
*/
exports.prevAll = function (selector) {
if (!this[0]) {
return this;
}
var elems = [];
_.forEach(this, function(elem) {
this.toArray().forEach(function (elem) {
while ((elem = elem.prev)) {

@@ -242,13 +421,31 @@ if (isTag(elem) && elems.indexOf(elem) === -1) {

return selector ?
exports.filter.call(elems, selector, this) :
this._make(elems);
return selector
? exports.filter.call(elems, selector, this)
: this._make(elems);
};
exports.prevUntil = function(selector, filterSelector) {
if (!this[0]) { return this; }
var elems = [], untilNode, untilNodes;
/**
* Gets all the preceding siblings up to but not including the element matched
* by the selector, optionally filtered by another selector.
*
* @example
*
* $('.pear').prevUntil('.apple')
* //=> [<li class="orange">Orange</li>]
*
* @param {string|cheerio|node} selector - Selector for element to stop at.
* @param {string} [filterSelector] - If specified filter for siblings.
*
* @see {@link http://api.jquery.com/prevUntil/}
*/
exports.prevUntil = function (selector, filterSelector) {
if (!this[0]) {
return this;
}
var elems = [];
var untilNode;
var untilNodes;
if (typeof selector === 'string') {
untilNode = select(selector, this.prevAll().get(), this.options)[0];
untilNode = select.select(selector, this.prevAll().get(), this.options)[0];
} else if (selector && selector.cheerio) {

@@ -260,7 +457,9 @@ untilNodes = selector.get();

_.forEach(this, function(elem) {
this.toArray().forEach(function (elem) {
while ((elem = elem.prev)) {
if ((untilNode && elem !== untilNode) ||
if (
(untilNode && elem !== untilNode) ||
(untilNodes && untilNodes.indexOf(elem) === -1) ||
(!untilNode && !untilNodes)) {
(!untilNode && !untilNodes)
) {
if (isTag(elem) && elems.indexOf(elem) === -1) {

@@ -275,26 +474,55 @@ elems.push(elem);

return filterSelector ?
exports.filter.call(elems, filterSelector, this) :
this._make(elems);
return filterSelector
? exports.filter.call(elems, filterSelector, this)
: this._make(elems);
};
exports.siblings = function(selector) {
/**
* Gets the first selected element's siblings, excluding itself.
*
* @example
*
* $('.pear').siblings().length
* //=> 2
*
* $('.pear').siblings('.orange').length
* //=> 1
*
* @param {string} [selector] - If specified filter for siblings.
*
* @see {@link http://api.jquery.com/siblings/}
*/
exports.siblings = function (selector) {
var parent = this.parent();
var elems = _.filter(
parent ? parent.children() : this.siblingsAndMe(),
_.bind(function(elem) { return isTag(elem) && !this.is(elem); }, this)
);
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);
} else {
return this._make(elems);
}
return this._make(elems);
};
exports.children = function(selector) {
var elems = _.reduce(this, function(memo, elem) {
return memo.concat(_.filter(elem.children, isTag));
/**
* Gets the children of the first selected element.
*
* @example
*
* $('#fruits').children().length
* //=> 3
*
* $('#fruits').children('.pear').text()
* //=> Pear
*
* @param {string} [selector] - If specified filter for children.
*
* @see {@link http://api.jquery.com/children/}
*/
exports.children = function (selector) {
var elems = this.toArray().reduce(function (newElems, elem) {
return newElems.concat(elem.children.filter(isTag));
}, []);

@@ -307,11 +535,45 @@

exports.contents = function() {
return this._make(_.reduce(this, function(all, elem) {
all.push.apply(all, elem.children);
return all;
}, []));
/**
* Gets the children of each element in the set of matched elements, including
* text and comment nodes.
*
* @example
*
* $('#fruits').contents().length
* //=> 3
*
* @see {@link http://api.jquery.com/contents/}
*/
exports.contents = function () {
var elems = this.toArray().reduce(function (newElems, elem) {
return newElems.concat(elem.children);
}, []);
return this._make(elems);
};
exports.each = function(fn) {
var i = 0, len = this.length;
/**
* Iterates over a cheerio object, executing a function for each matched
* element. When the callback is fired, the function is fired in the context of
* the DOM element, so `this` refers to the current element, which is
* equivalent to the function parameter `element`. To break out of the `each`
* loop early, return with `false`.
*
* @example
*
* const fruits = [];
*
* $('li').each(function(i, elem) {
* fruits[i] = $(this).text();
* });
*
* fruits.join(', ');
* //=> Apple, Orange, Pear
*
* @param {Function} fn - Function to execute.
*
* @see {@link http://api.jquery.com/each/}
*/
exports.each = function (fn) {
var i = 0;
var len = this.length;
while (i < len && fn.call(this[i], i, this[i]) !== false) ++i;

@@ -321,38 +583,160 @@ return this;

exports.map = function(fn) {
return this._make(_.reduce(this, function(memo, el, i) {
/**
* Pass each element in the current matched set through a function, producing a
* new Cheerio object containing the return values. The function can return an
* individual data item or an array of data items to be inserted into the
* resulting set. If an array is returned, the elements inside the array are
* inserted into the set. If the function returns null or undefined, no element
* will be inserted.
*
* @example
*
* $('li').map(function(i, el) {
* // this === el
* return $(this).text();
* }).get().join(' ');
* //=> "apple orange pear"
*
* @param {Function} fn - Function to execute.
*
* @see {@link http://api.jquery.com/map/}
*/
exports.map = function (fn) {
var elems = [];
for (var i = 0; i < this.length; i++) {
var el = this[i];
var val = fn.call(el, i, el);
return val == null ? memo : memo.concat(val);
}, []));
if (val != null) {
elems = elems.concat(val);
}
}
return this._make(elems);
};
var makeFilterMethod = function(filterFn) {
return function(match, container) {
var testFn;
container = container || this;
function getFilterFn(match) {
if (typeof match === 'function') {
return function (el, i) {
return match.call(el, i, el);
};
} else if (match.cheerio) {
return match.is.bind(match);
}
return function (el) {
return match === el;
};
}
if (typeof match === 'string') {
testFn = select.compile(match, container.options);
} else if (typeof match === 'function') {
testFn = function(el, i) {
return match.call(el, i, el);
};
} else if (match.cheerio) {
testFn = match.is.bind(match);
} else {
testFn = function(el) {
return match === el;
};
}
/**
* Iterates over a cheerio object, reducing the set of selector elements to
* those that match the selector or pass the function's test. When a Cheerio
* selection is specified, return only the elements contained in that
* selection. When an element is specified, return only that element (if it is
* contained in the original selection). If using the function method, the
* function is executed in the context of the selected element, so `this`
* refers to the current element.
*
* @function
* @param {string | Function} match - Value to look for, following the rules above.
* @param {node[]} container - Optional node to filter instead.
*
* @example <caption>Selector</caption>
*
* $('li').filter('.orange').attr('class');
* //=> orange
*
* @example <caption>Function</caption>
*
* $('li').filter(function(i, el) {
* // this === el
* return $(this).attr('class') === 'orange';
* }).attr('class')
* //=> orange
*
* @see {@link http://api.jquery.com/filter/}
*/
exports.filter = function (match, container) {
container = container || this;
var elements = this.toArray ? this.toArray() : this;
return container._make(filterFn(this, testFn));
};
if (typeof match === 'string') {
elements = select.filter(match, elements, container.options);
} else {
elements = elements.filter(getFilterFn(match));
}
return container._make(elements);
};
exports.filter = makeFilterMethod(_.filter);
exports.not = makeFilterMethod(_.reject);
/**
* Remove elements from the set of matched elements. Given a jQuery object that
* represents a set of DOM elements, the `.not()` method constructs a new
* jQuery object from a subset of the matching elements. The supplied selector
* is tested against each element; the elements that don't match the selector
* will be included in the result. The `.not()` method can take a function as
* its argument in the same way that `.filter()` does. Elements for which the
* function returns true are excluded from the filtered set; all other elements
* are included.
*
* @function
* @param {string | Function} match - Value to look for, following the rules above.
* @param {node[]} container - Optional node to filter instead.
*
* @example <caption>Selector</caption>
*
* $('li').not('.apple').length;
* //=> 2
*
* @example <caption>Function</caption>
*
* $('li').not(function(i, el) {
* // this === el
* return $(this).attr('class') === 'orange';
* }).length;
* //=> 2
*
* @see {@link http://api.jquery.com/not/}
*/
exports.not = function (match, container) {
container = container || this;
var elements = container.toArray ? container.toArray() : container;
var matches;
var filterFn;
exports.has = function(selectorOrHaystack) {
if (typeof match === 'string') {
matches = new Set(select.filter(match, elements, this.options));
elements = elements.filter(function (el) {
return !matches.has(el);
});
} else {
filterFn = getFilterFn(match);
elements = elements.filter(function (el, i) {
return !filterFn(el, i);
});
}
return container._make(elements);
};
/**
* Filters the set of matched elements to only those which have the given DOM
* element as a descendant or which have a descendant that matches the given
* selector. Equivalent to `.filter(':has(selector)')`.
*
* @example <caption>Selector</caption>
*
* $('ul').has('.pear').attr('id');
* //=> fruits
*
* @example <caption>Element</caption>
*
* $('ul').has($('.pear')[0]).attr('id');
* //=> fruits
*
* @param {string|cheerio|node} selectorOrHaystack - Element to look for.
*
* @see {@link http://api.jquery.com/has/}
*/
exports.has = function (selectorOrHaystack) {
var that = this;
return exports.filter.call(this, function() {
return exports.filter.call(this, function () {
return that._make(this).find(selectorOrHaystack).length > 0;

@@ -362,12 +746,47 @@ });

exports.first = function() {
/**
* Will select the first element of a cheerio object.
*
* @example
*
* $('#fruits').children().first().text()
* //=> Apple
*
* @see {@link http://api.jquery.com/first/}
*/
exports.first = function () {
return this.length > 1 ? this._make(this[0]) : this;
};
exports.last = function() {
/**
* Will select the last element of a cheerio object.
*
* @example
*
* $('#fruits').children().last().text()
* //=> Pear
*
* @see {@link http://api.jquery.com/last/}
*/
exports.last = function () {
return this.length > 1 ? this._make(this[this.length - 1]) : this;
};
// Reduce the set of matched elements to the one at the specified index.
exports.eq = function(i) {
/**
* Reduce the set of matched elements to the one at the specified index. Use
* `.eq(-i)` to count backwards from the last selected element.
*
* @example
*
* $('li').eq(0).text()
* //=> Apple
*
* $('li').eq(-1).text()
* //=> Pear
*
* @param {number} i - Index of the element to select.
*
* @see {@link http://api.jquery.com/eq/}
*/
exports.eq = function (i) {
i = +i;

@@ -382,14 +801,48 @@

// Retrieve the DOM elements matched by the jQuery object.
exports.get = function(i) {
/**
* Retrieve the DOM elements matched by the Cheerio object. If an index is
* specified, retrieve one of the elements matched by the Cheerio object.
*
* @example
*
* $('li').get(0).tagName
* //=> li
*
* If no index is specified, retrieve all elements matched by the Cheerio object:
*
* @example
*
* $('li').get().length
* //=> 3
*
* @param {number} [i] - Element to retrieve.
*
* @see {@link http://api.jquery.com/get/}
*/
exports.get = function (i) {
if (i == null) {
return Array.prototype.slice.call(this);
} else {
return this[i < 0 ? (this.length + i) : i];
}
return this[i < 0 ? this.length + i : i];
};
// Search for a given element from among the matched elements.
exports.index = function(selectorOrNeedle) {
var $haystack, needle;
/**
* Search for a given element from among the matched elements.
*
* @example
*
* $('.pear').index()
* //=> 2
* $('.orange').index('li')
* //=> 1
* $('.apple').index($('#fruit, li'))
* //=> 1
*
* @param {string|cheerio|node} [selectorOrNeedle] - Element to look for.
*
* @see {@link http://api.jquery.com/index/}
*/
exports.index = function (selectorOrNeedle) {
var $haystack;
var needle;

@@ -410,3 +863,16 @@ if (arguments.length === 0) {

exports.slice = function() {
/**
* Gets the elements matching the specified range.
*
* @example
*
* $('li').slice(1).eq(0).text()
* //=> 'Orange'
*
* $('li').slice(1, 2).length
* //=> 1
*
* @see {@link http://api.jquery.com/slice/}
*/
exports.slice = function () {
return this._make([].slice.apply(this, arguments));

@@ -417,3 +883,3 @@ };

var elems = [];
while (elem && elems.length < limit) {
while (elem && elems.length < limit && elem.type !== 'root') {
if (!selector || exports.filter.call([elem], selector, self).length) {

@@ -427,9 +893,31 @@ elems.push(elem);

// End the most recent filtering operation in the current chain and return the
// set of matched elements to its previous state.
exports.end = function() {
/**
* End the most recent filtering operation in the current chain and return the
* set of matched elements to its previous state.
*
* @example
*
* $('li').eq(0).end().length
* //=> 3
*
* @see {@link http://api.jquery.com/end/}
*/
exports.end = function () {
return this.prevObject || this._make([]);
};
exports.add = function(other, context) {
/**
* Add elements to the set of matched elements.
*
* @example
*
* $('.apple').add('.orange').length
* //=> 2
*
* @param {string|cheerio} other - Elements to add.
* @param {cheerio} [context] - Optionally the context of the new selection.
*
* @see {@link http://api.jquery.com/add/}
*/
exports.add = function (other, context) {
var selection = this._make(other, context);

@@ -446,5 +934,16 @@ var contents = uniqueSort(selection.get().concat(this.get()));

// Add the previous set of elements on the stack to the current set, optionally
// filtered by a selector.
exports.addBack = function(selector) {
/**
* Add the previous set of elements on the stack to the current set, optionally
* filtered by a selector.
*
* @example
*
* $('li').eq(0).addBack('.orange').length
* //=> 2
*
* @param {string} selector - Selector for the elements to add.
*
* @see {@link http://api.jquery.com/addBack/}
*/
exports.addBack = function (selector) {
return this.add(

@@ -451,0 +950,0 @@ arguments.length ? this.prevObject.filter(selector) : this.prevObject

@@ -5,12 +5,6 @@ /*

var parse = require('./parse'),
defaultOptions = require('./options').default,
flattenOptions = require('./options').flatten,
isHtml = require('./utils').isHtml,
_ = {
extend: require('lodash/assignIn'),
bind: require('lodash/bind'),
forEach: require('lodash/forEach'),
defaults: require('lodash/defaults')
};
var parse = require('./parse');
var defaultOptions = require('./options').default;
var flattenOptions = require('./options').flatten;
var isHtml = require('./utils').isHtml;

@@ -20,3 +14,2 @@ /*

*/
var api = [

@@ -27,14 +20,34 @@ require('./api/attributes'),

require('./api/css'),
require('./api/forms')
require('./api/forms'),
];
/*
* Instance of cheerio
/**
* Instance of cheerio. Methods are specified in the modules.
* Usage of this constructor is not recommended. Please use $.load instead.
*
* @class
* @hideconstructor
* @param {string|cheerio|node|node[]} selector - The new selection.
* @param {string|cheerio|node|node[]} [context] - Context of the selection.
* @param {string|cheerio|node|node[]} [root] - Sets the root node.
* @param {object} [options] - Options for the instance.
*
* @mixes module:cheerio/attributes
* @mixes module:cheerio/css
* @mixes module:cheerio/forms
* @mixes module:cheerio/manipulation
* @mixes module:cheerio/traversing
*/
var Cheerio = (module.exports = function (selector, context, root, options) {
if (!(this instanceof Cheerio)) {
return new Cheerio(selector, context, root, options);
}
var Cheerio = module.exports = function(selector, context, root, options) {
if (!(this instanceof Cheerio)) return new Cheerio(selector, context, root, options);
this.options = Object.assign(
{},
defaultOptions,
this.options,
flattenOptions(options)
);
this.options = _.defaults(flattenOptions(options), this.options, defaultOptions);
// $(), $(null), $(undefined), $(false)

@@ -52,10 +65,9 @@ if (!selector) return this;

// $(dom)
if (isNode(selector))
selector = [selector];
if (isNode(selector)) selector = [selector];
// $([dom])
if (Array.isArray(selector)) {
_.forEach(selector, _.bind(function(elem, idx) {
selector.forEach(function (elem, idx) {
this[idx] = elem;
}, this));
}, this);
this.length = selector.length;

@@ -83,4 +95,4 @@ return this;

}
// $('li', node), $('li', [nodes])
} else if (!context.cheerio) {
// $('li', node), $('li', [nodes])
context = Cheerio.call(this, context);

@@ -94,14 +106,7 @@ }

return context.find(selector);
};
});
/**
* Mix in `static`
*/
_.extend(Cheerio, require('./static'));
/*
* Set a signature of the object
*/
Cheerio.prototype.cheerio = '[cheerio object]';

@@ -112,3 +117,2 @@

*/
Cheerio.prototype.length = 0;

@@ -120,6 +124,5 @@ Cheerio.prototype.splice = Array.prototype.splice;

*
* @api private
* @private
*/
Cheerio.prototype._make = function(dom, context) {
Cheerio.prototype._make = function (dom, context) {
var cheerio = new this.constructor(dom, context, this._root, this.options);

@@ -131,18 +134,29 @@ cheerio.prevObject = this;

/**
* Turn a cheerio object into an array
* Retrieve all the DOM elements contained in the jQuery set as an array.
*
* @example
* $('li').toArray()
* //=> [ {...}, {...}, {...} ]
*/
Cheerio.prototype.toArray = function() {
Cheerio.prototype.toArray = function () {
return this.get();
};
/**
* Plug in the API
*/
api.forEach(function(mod) {
_.extend(Cheerio.prototype, mod);
// Support for (const element of $(...)) iteration:
if (typeof Symbol !== 'undefined') {
Cheerio.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
}
// Plug in the API
api.forEach(function (mod) {
Object.assign(Cheerio.prototype, mod);
});
var isNode = function(obj) {
return obj.name || obj.type === 'text' || obj.type === 'comment';
var isNode = function (obj) {
return (
obj.name ||
obj.type === 'root' ||
obj.type === 'text' ||
obj.type === 'comment'
);
};

@@ -1,3 +0,1 @@

var assign = require('lodash/assign');
/*

@@ -8,10 +6,14 @@ * Cheerio default options

exports.default = {
withDomLvl1: true,
normalizeWhitespace: false,
xml: false,
decodeEntities: true
decodeEntities: true,
};
exports.flatten = function(options) {
return options && options.xml ? assign({xmlMode: true}, options.xml) : options;
};
var xmlModeDefault = { _useHtmlParser2: true, xmlMode: true };
exports.flatten = function (options) {
return options && options.xml
? typeof options.xml === 'boolean'
? xmlModeDefault
: Object.assign({}, xmlModeDefault, options.xml)
: options;
};
/*
Module Dependencies
*/
var htmlparser = require('htmlparser2'),
parse5 = require('parse5');
var htmlparser = require('htmlparser2');
var parse5 = require('parse5');
var htmlparser2Adapter = require('parse5-htmlparser2-tree-adapter');
var domhandler = require('domhandler');
var DomUtils = htmlparser.DomUtils;

@@ -10,24 +13,3 @@ /*

*/
exports = module.exports = function(content, options, isDocument) {
var dom = exports.evaluate(content, options, isDocument),
// Generic root element
root = exports.evaluate('<root></root>', options, false)[0];
root.type = 'root';
root.parent = null;
// Update the dom using the root
exports.update(dom, root);
return root;
};
function parseWithParse5 (content, isDocument) {
var parse = isDocument ? parse5.parse : parse5.parseFragment,
root = parse(content, { treeAdapter: parse5.treeAdapters.htmlparser2 });
return root.children;
}
exports.evaluate = function(content, options, isDocument) {
exports = module.exports = function parse(content, options, isDocument) {
// options = options || $.fn.options;

@@ -37,4 +19,5 @@

if (Buffer.isBuffer(content))
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(content)) {
content = content.toString();
}

@@ -44,5 +27,21 @@ if (typeof content === 'string') {

dom = useHtmlParser2 ? htmlparser.parseDOM(content, options) : parseWithParse5(content, isDocument);
dom = useHtmlParser2
? htmlparser.parseDocument(content, options)
: parseWithParse5(content, options, isDocument);
} else {
dom = content;
if (
typeof content === 'object' &&
content != null &&
content.type === 'root'
) {
dom = content;
} else {
// Generic root element
var root = new domhandler.Document(content);
content.forEach(function (node) {
node.parent = root;
});
dom = root;
}
}

@@ -53,6 +52,15 @@

function parseWithParse5(content, options, isDocument) {
var parse = isDocument ? parse5.parse : parse5.parseFragment;
return parse(content, {
treeAdapter: htmlparser2Adapter,
sourceCodeLocationInfo: options.sourceCodeLocationInfo,
});
}
/*
Update the dom structure, for one changed layer
*/
exports.update = function(arr, parent) {
exports.update = function (arr, parent) {
// normalize

@@ -73,12 +81,4 @@ if (!Array.isArray(arr)) arr = [arr];

// Cleanly remove existing nodes from their previous structures.
var oldParent = node.parent || node.root,
oldSiblings = oldParent && oldParent.children;
if (oldSiblings && oldSiblings !== arr) {
oldSiblings.splice(oldSiblings.indexOf(node), 1);
if (node.prev) {
node.prev.next = node.next;
}
if (node.next) {
node.next.prev = node.prev;
}
if (node.parent && node.parent.children !== arr) {
DomUtils.removeElement(node);
}

@@ -93,9 +93,3 @@

if (parent && parent.type === 'root') {
node.root = parent;
node.parent = null;
} else {
node.root = null;
node.parent = parent;
}
node.parent = parent;
}

@@ -105,3 +99,1 @@

};
// module.exports = $.extend(exports);

@@ -0,34 +1,46 @@

var htmlparser2Adapter = require('parse5-htmlparser2-tree-adapter');
/**
* Module dependencies
* @module cheerio/static
* @ignore
*/
var serialize = require('dom-serializer'),
defaultOptions = require('./options').default,
flattenOptions = require('./options').flatten,
select = require('css-select'),
parse = require('./parse'),
_ = {
merge: require('lodash/merge'),
defaults: require('lodash/defaults')
};
var serialize = require('dom-serializer').default;
var defaultOptions = require('./options').default;
var flattenOptions = require('./options').flatten;
var select = require('cheerio-select').select;
var parse5 = require('parse5');
var parse = require('./parse');
/**
* $.load(str)
* Create a querying function, bound to a document created from the provided
* markup. Note that similar to web browser contexts, this operation may
* introduce `<html>`, `<head>`, and `<body>` elements; set `isDocument` to `false`
* to switch to fragment mode and disable this.
*
* See the README section titled "Loading" for additional usage information.
*
* @param {string} content - Markup to be loaded.
* @param {object} [options] - Options for the created instance.
* @param {boolean} [isDocument] - Allows parser to be switched to fragment mode.
*
*/
exports.load = function (content, options, isDocument) {
if (content === null || content === undefined) {
throw new Error('cheerio.load() expects a string');
}
exports.load = function(content, options, isDocument) {
var Cheerio = require('./cheerio');
options = _.defaults(flattenOptions(options || {}), defaultOptions);
options = Object.assign({}, defaultOptions, flattenOptions(options));
if (isDocument === void 0)
isDocument = true;
if (isDocument === void 0) isDocument = true;
var root = parse(content, options, isDocument);
var initialize = function(selector, context, r, opts) {
var initialize = function (selector, context, r, opts) {
if (!(this instanceof initialize)) {
return new initialize(selector, context, r, opts);
}
opts = _.defaults(opts || {}, options);
opts = Object.assign({}, options, opts);
return Cheerio.call(this, selector, context, r || root, opts);

@@ -50,3 +62,3 @@ };

// Add in the static methods
_.merge(initialize, exports);
Object.assign(initialize, exports);

@@ -62,4 +74,4 @@ // Add in the root

/*
* Helper function
*/
* Helper function
*/

@@ -77,10 +89,29 @@ function render(that, dom, options) {

return serialize(dom, options);
if (options.xmlMode || options._useHtmlParser2) {
return serialize(dom, options);
}
// `dom-serializer` passes over the special "root" node and renders the
// node's children in its place. To mimic this behavior with `parse5`, an
// equivalent operation must be applied to the input array.
var nodes = 'length' in dom ? dom : [dom];
for (var index = 0; index < nodes.length; index += 1) {
if (nodes[index].type === 'root') {
nodes.splice.apply(nodes, [index, 1].concat(nodes[index].children));
}
}
return parse5.serialize(
{ children: nodes },
{ treeAdapter: htmlparser2Adapter }
);
}
/**
* $.html([selector | dom], [options])
* Renders the document.
*
* @param {string|cheerio|node} [dom] - Element to render.
* @param {object} [options] - Options for the renderer.
*/
exports.html = function(dom, options) {
exports.html = function (dom, options) {
// be flexible about parameters, sometimes we call html(),

@@ -90,4 +121,8 @@ // with options as only parameter

// assume there is no 'length' or 'type' properties in the options object
if (Object.prototype.toString.call(dom) === '[object Object]' && !options && !('length' in dom) && !('type' in dom))
{
if (
Object.prototype.toString.call(dom) === '[object Object]' &&
!options &&
!('length' in dom) &&
!('type' in dom)
) {
options = dom;

@@ -99,3 +134,8 @@ dom = undefined;

// so fallback non existing options to the default ones
options = _.defaults(flattenOptions(options || {}), this._options, defaultOptions);
options = Object.assign(
{},
defaultOptions,
this._options,
flattenOptions(options || {})
);

@@ -106,8 +146,9 @@ return render(this, dom, options);

/**
* $.xml([selector | dom])
* Render the document as XML.
*
* @param {string|cheerio|node} [dom] - Element to render.
*/
exports.xml = function (dom) {
var options = Object.assign({}, this._options, { xmlMode: true });
exports.xml = function(dom) {
var options = _.defaults({xml: true}, this._options);
return render(this, dom, options);

@@ -117,6 +158,7 @@ };

/**
* $.text(dom)
* Render the document as text.
*
* @param {string|cheerio|node} [elems] - Elements to render.
*/
exports.text = function(elems) {
exports.text = function (elems) {
if (!elems) {

@@ -126,5 +168,5 @@ elems = this.root();

var ret = '',
len = elems.length,
elem;
var ret = '';
var len = elems.length;
var elem;

@@ -134,3 +176,8 @@ for (var i = 0; i < len; i++) {

if (elem.type === 'text') ret += elem.data;
else if (elem.children && elem.type !== 'comment' && elem.tagName !== 'script' && elem.tagName !== 'style') {
else if (
elem.children &&
elem.type !== 'comment' &&
elem.tagName !== 'script' &&
elem.tagName !== 'style'
) {
ret += exports.text(elem.children);

@@ -144,7 +191,13 @@ }

/**
* $.parseHTML(data [, context ] [, keepScripts ])
* Parses a string into an array of DOM nodes. The `context` argument has no
* meaning for Cheerio, but it is maintained for API compatibility with jQuery.
*
* @param {string} data - Markup that will be parsed.
* @param {any|boolean} [context] - Will be ignored. If it is a boolean it will be used as the value of `keepScripts`.
* @param {boolean} [keepScripts] - If false all scripts will be removed.
*
* @alias Cheerio.parseHTML
* @see {@link https://api.jquery.com/jQuery.parseHTML/}
*/
exports.parseHTML = function(data, context, keepScripts) {
exports.parseHTML = function (data, context, keepScripts) {
var parsed;

@@ -174,5 +227,12 @@

/**
* $.root()
* Sometimes you need to work with the top-level root element. To query it, you
* can use `$.root()`.
*
* @alias Cheerio.root
*
* @example
* $.root().append('<ul id="vegetables"></ul>').html();
* //=> <ul id="fruits">...</ul><ul id="vegetables"></ul>
*/
exports.root = function() {
exports.root = function () {
return this(this._root);

@@ -182,6 +242,13 @@ };

/**
* $.contains()
* Checks to see if the `contained` DOM element is a descendant of the
* `container` DOM element.
*
* @param {node} container - Potential parent node.
* @param {node} contained - Potential child node.
* @returns {boolean}
*
* @alias Cheerio.contains
* @see {@link https://api.jquery.com/jQuery.contains}
*/
exports.contains = function(container, contained) {
exports.contains = function (container, contained) {
// According to the jQuery API, an element does not "contain" itself

@@ -205,14 +272,17 @@ if (contained === container) {

/**
* $.merge()
* $.merge().
*
* @param {Array|cheerio} arr1 - First array.
* @param {Array|cheerio} arr2 - Second array.
*
* @alias Cheerio.merge
* @see {@link https://api.jquery.com/jQuery.merge}
*/
exports.merge = function(arr1, arr2) {
if(!(isArrayLike(arr1) && isArrayLike(arr2))){
exports.merge = function (arr1, arr2) {
if (!isArrayLike(arr1) || !isArrayLike(arr2)) {
return;
}
var newLength = arr1.length + arr2.length;
var i = 0;
while(i < arr2.length){
for (var i = 0; i < arr2.length; i++) {
arr1[i + arr1.length] = arr2[i];
i++;
}

@@ -223,26 +293,22 @@ arr1.length = newLength;

function isArrayLike(item){
if(Array.isArray(item)){
function isArrayLike(item) {
if (Array.isArray(item)) {
return true;
}
if(typeof item !== 'object'){
if (
typeof item !== 'object' ||
!Object.prototype.hasOwnProperty.call(item, 'length') ||
typeof item.length !== 'number' ||
item.length < 0
) {
return false;
}
if(!item.hasOwnProperty('length')){
return false;
}
if(typeof item.length !== 'number') {
return false;
}
if(item.length < 0){
return false;
}
var i = 0;
while(i < item.length){
if(!(i in item)){
for (var i = 0; i < item.length; i++) {
if (!(i in item)) {
return false;
}
i++;
}
return true;
}

@@ -1,30 +0,26 @@

var parse = require('./parse'),
render = require('dom-serializer'),
assign = require('lodash/assign');
var htmlparser2 = require('htmlparser2');
var domhandler = require('domhandler');
/**
* HTML Tags
*/
var tags = { tag: true, script: true, style: true };
/**
* Check if the DOM element is a tag
* Check if the DOM element is a tag.
*
* isTag(type) includes <script> and <style> tags
* `isTag(type)` includes `<script>` and `<style>` tags.
*
* @param {node} type - DOM node to check.
* @returns {boolean}
*
* @private
*/
exports.isTag = htmlparser2.DomUtils.isTag;
exports.isTag = function(type) {
if (type.type) type = type.type;
return tags[type] || false;
};
/**
* Convert a string to camel case notation.
* @param {String} str String to be converted.
* @return {String} String in camel case notation.
*
* @param {string} str - String to be converted.
* @returns {string} String in camel case notation.
*
* @private
*/
exports.camelCase = function(str) {
return str.replace(/[_.-](\w|$)/g, function(_, x) {
exports.camelCase = function (str) {
return str.replace(/[_.-](\w|$)/g, function (_, x) {
return x.toUpperCase();

@@ -37,6 +33,9 @@ });

* described by hyphens ("-") and all characters are lower-case.
* @param {String} str String to be converted.
* @return {string} String in "CSS case".
*
* @param {string} str - String to be converted.
* @returns {string} String in "CSS case".
*
* @private
*/
exports.cssCase = function(str) {
exports.cssCase = function (str) {
return str.replace(/[A-Z]/g, '-$&').toLowerCase();

@@ -46,10 +45,14 @@ };

/**
* Iterate over each DOM element without creating intermediary Cheerio instances.
* Iterate over each DOM element without creating intermediary Cheerio
* instances.
*
* This is indented for use internally to avoid otherwise unnecessary memory pressure introduced
* by _make.
* This is indented for use internally to avoid otherwise unnecessary memory
* pressure introduced by _make.
*
* @param {cheerio} cheerio - Cheerio object.
* @param {Function} fn - Function to call.
*/
exports.domEach = function(cheerio, fn) {
var i = 0, len = cheerio.length;
exports.domEach = function (cheerio, fn) {
var i = 0;
var len = cheerio.length;
while (i < len && fn.call(cheerio, i, cheerio[i]) !== false) ++i;

@@ -60,12 +63,23 @@ return cheerio;

/**
* Create a deep copy of the given DOM structure by first rendering it to a
* string and then parsing the resultant markup.
* Create a deep copy of the given DOM structure.
* Sets the parents of the copies of the passed nodes to `null`.
*
* @argument {Object} dom - The htmlparser2-compliant DOM structure
* @argument {Object} options - The parsing/rendering options
* @param {object} dom - The htmlparser2-compliant DOM structure.
* @private
*/
exports.cloneDom = function(dom, options) {
options = assign({}, options, { _useHtmlParser2: true });
exports.cloneDom = function (dom) {
var clone =
'length' in dom
? Array.prototype.map.call(dom, function (el) {
return domhandler.cloneNode(el, true);
})
: [domhandler.cloneNode(dom, true)];
return parse(render(dom, options), options, false).children;
// Add a root node around the cloned nodes
var root = new domhandler.Document(clone);
clone.forEach(function (node) {
node.parent = root;
});
return clone;
};

@@ -76,11 +90,20 @@

*/
var quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w-]*)$)/;
var quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/;
/*
* Check if string is HTML
/**
* Check if string is HTML.
*
* @param {string} str - String to check.
*
* @private
*/
exports.isHtml = function(str) {
exports.isHtml = function (str) {
// Faster than running regex, if str starts with `<` and ends with `>`, assume it's HTML
if (str.charAt(0) === '<' && str.charAt(str.length - 1) === '>' && str.length >= 3) return true;
if (
str.charAt(0) === '<' &&
str.charAt(str.length - 1) === '>' &&
str.length >= 3
) {
return true;
}

@@ -87,0 +110,0 @@ // Run the regex

{
"name": "cheerio",
"version": "1.0.0-rc.3",
"version": "1.0.0-rc.4",
"description": "Tiny, fast, and elegant implementation of core jQuery designed specifically for the server",

@@ -20,31 +20,66 @@ "author": "Matt Mueller <mattmuelle@gmail.com> (mat.io)",

"main": "./index.js",
"types": "types/index.d.ts",
"files": [
"index.js",
"types/index.d.ts",
"lib"
],
"engines": {
"node": ">= 0.6"
"node": ">= 0.12"
},
"dependencies": {
"css-select": "~1.2.0",
"dom-serializer": "~0.1.1",
"entities": "~1.1.1",
"htmlparser2": "^3.9.1",
"lodash": "^4.15.0",
"parse5": "^3.0.1"
"cheerio-select": "github:cheeriojs/cheerio-select",
"dom-serializer": "~1.2.0",
"domhandler": "^4.0.0",
"entities": "~2.1.0",
"htmlparser2": "^6.0.0",
"parse5": "^6.0.0",
"parse5-htmlparser2-tree-adapter": "^6.0.0"
},
"devDependencies": {
"benchmark": "^2.1.0",
"coveralls": "^2.11.9",
"@types/node": "^14.14.10",
"benchmark": "^2.1.4",
"coveralls": "^3.0.2",
"eslint": "^7.10.0",
"eslint-config-prettier": "^7.0.0",
"eslint-plugin-jsdoc": "^30.6.2",
"expect.js": "~0.3.1",
"istanbul": "^0.4.3",
"husky": "^4.2.5",
"jquery": "^3.0.0",
"jsdom": "^9.2.1",
"jshint": "^2.9.2",
"mocha": "^3.1.2",
"xyz": "~1.1.0"
"jsdoc": "^3.6.6",
"jsdom": "^16.2.2",
"lint-staged": "^10.2.2",
"mocha": "^8.1.1",
"nyc": "^15.0.1",
"prettier": "^2.1.1",
"tsd": "^0.14.0",
"xyz": "~4.0.0"
},
"scripts": {
"test": "make test"
"test": "npm run lint && npm run test:mocha && npm run test:types",
"test:mocha": "mocha --recursive --reporter dot --parallel",
"test:types": "tsd",
"lint": "npm run lint:es && npm run lint:prettier",
"lint:es": "eslint --ignore-path .prettierignore .",
"lint:prettier": "npm run format:prettier:raw -- --check",
"format": "npm run format:es && npm run format:prettier",
"format:es": "npm run lint:es -- --fix",
"format:prettier": "npm run format:prettier:raw -- --write",
"format:prettier:raw": "prettier '**/*.{js,ts,md,json,yml}' --ignore-path .prettierignore",
"build:docs": "jsdoc --configure jsdoc-config.json",
"pre-commit": "lint-staged"
},
"prettier": {
"singleQuote": true,
"tabWidth": 2
},
"lint-staged": {
"*.js": [
"prettier --write",
"npm run test:lint -- --fix"
],
"*.{json,md,ts,yml}": [
"prettier --write"
]
}
}

@@ -25,24 +25,32 @@ <h1 align="center">cheerio</h1>

[中文文档 (Chinese Readme)](https://github.com/cheeriojs/cheerio/wiki/Chinese-README)
```js
const cheerio = require('cheerio')
const $ = cheerio.load('<h2 class="title">Hello world</h2>')
const cheerio = require('cheerio');
const $ = cheerio.load('<h2 class="title">Hello world</h2>');
$('h2.title').text('Hello there!')
$('h2').addClass('welcome')
$('h2.title').text('Hello there!');
$('h2').addClass('welcome');
$.html()
$.html();
//=> <html><head></head><body><h2 class="title welcome">Hello there!</h2></body></html>
```
## Note
We are currently working on the 1.0.0 release of cheerio on the `main` branch. The source code for the last published version, `0.22.0`, can be found [here](https://github.com/cheeriojs/cheerio/tree/aa90399c9c02f12432bfff97b8f1c7d8ece7c307).
## Installation
`npm install cheerio`
## Features
__&#10084; Familiar syntax:__
**&#10084; Familiar syntax:**
Cheerio implements a subset of core jQuery. Cheerio removes all the DOM inconsistencies and browser cruft from the jQuery library, revealing its truly gorgeous API.
__&#991; Blazingly fast:__
Cheerio works with a very simple, consistent DOM model. As a result parsing, manipulating, and rendering are incredibly efficient. Preliminary end-to-end benchmarks suggest that cheerio is about __8x__ faster than JSDOM.
**&#991; Blazingly fast:**
Cheerio works with a very simple, consistent DOM model. As a result parsing, manipulating, and rendering are incredibly efficient.
__&#10049; Incredibly flexible:__
**&#10049; Incredibly flexible:**
Cheerio wraps around [parse5](https://github.com/inikulin/parse5) parser and can optionally use @FB55's forgiving [htmlparser2](https://github.com/fb55/htmlparser2/). Cheerio can parse nearly any HTML or XML document.

@@ -52,12 +60,4 @@

Cheerio parses markup and provides an API for traversing/manipulating the resulting data structure. It does not interpret the result as a web browser does. Specifically, it does *not* produce a visual rendering, apply CSS, load external resources, or execute JavaScript. If your use case requires any of this functionality, you should consider projects like [PhantomJS](http://phantomjs.org/) or [JSDom](https://github.com/tmpvar/jsdom).
Cheerio parses markup and provides an API for traversing/manipulating the resulting data structure. It does not interpret the result as a web browser does. Specifically, it does _not_ produce a visual rendering, apply CSS, load external resources, or execute JavaScript. If your use case requires any of this functionality, you should consider projects like [PhantomJS](http://phantomjs.org/) or [JSDom](https://github.com/tmpvar/jsdom).
## Job Board
Looking for a career upgrade? Check out the available Node.js & Javascript positions at these innovative companies:
<a href="https://astro.netlify.com/automattic"><img src="https://astro.netlify.com/static/automattic.png"></a>
<a href="https://astro.netlify.com/segment"><img src="https://astro.netlify.com/static/segment.png"></a>
<a href="https://astro.netlify.com/auth0"><img src="https://astro.netlify.com/static/auth0.png"/></a>
## API

@@ -78,2 +78,3 @@

### Loading
First you need to load in the HTML. This step in jQuery is implicit, since jQuery operates on the one, baked-in DOM. With Cheerio, we need to pass in the HTML document.

@@ -86,4 +87,16 @@

const $ = cheerio.load('<ul id="fruits">...</ul>');
$.html();
//=> <html><head></head><body><ul id="fruits">...</ul></body></html>
```
Similar to web browser contexts, `load` will introduce `<html>`, `<head>`, and `<body>` elements if they are not already present. You can set `load`'s third argument to `false` to disable this.
```js
const $ = cheerio.load('<ul id="fruits">...</ul>', null, false);
$.html();
//=> '<ul id="fruits">...</ul>'
```
Optionally, you can also load in the HTML by passing the string as the context:

@@ -108,16 +121,16 @@

const $ = cheerio.load('<ul id="fruits">...</ul>', {
xml: {
normalizeWhitespace: true,
}
xml: {
normalizeWhitespace: true,
},
});
```
The options in the `xml` object are taken directly from [htmlparser2](https://github.com/fb55/htmlparser2/wiki/Parser-options), therefore any options that can be used in `htmlparser2` are valid in cheerio as well. The default options are:
The options in the `xml` object are taken directly from [htmlparser2](https://github.com/fb55/htmlparser2/wiki/Parser-options), therefore any options that can be used in `htmlparser2` are valid in cheerio as well. When `xml` is set, the default options are:
```js
{
withDomLvl1: true,
normalizeWhitespace: false,
xmlMode: true,
decodeEntities: true
decodeEntities: true, // Decode HTML entities.
withStartIndices: false, // Add a `startIndex` property to nodes.
withEndIndices: false, // Add an `endIndex` property to nodes.
}

@@ -155,3 +168,4 @@ ```

#### $( selector, [context], [root] )
#### \$( selector, [context], [root] )
`selector` searches within the `context` scope which searches within the `root` scope. `selector` and `context` can be a string expression, DOM Element, array of DOM elements, or cheerio object. `root` is typically the HTML document string.

@@ -162,690 +176,42 @@

```js
$('.apple', '#fruits').text()
$('.apple', '#fruits').text();
//=> Apple
$('ul .pear').attr('class')
$('ul .pear').attr('class');
//=> pear
$('li[class=orange]').html()
$('li[class=orange]').html();
//=> Orange
```
### Attributes
Methods for getting and modifying attributes.
##### XML Namespaces
#### .attr( name, value )
Method for getting and setting attributes. Gets the attribute value for only the first element in the matched set. If you set an attribute's value to `null`, you remove that attribute. You may also pass a `map` and `function` like jQuery.
You can select with XML Namespaces but [due to the CSS specification](https://www.w3.org/TR/2011/REC-css3-selectors-20110929/#attribute-selectors), the colon (`:`) needs to be escaped for the selector to be valid.
```js
$('ul').attr('id')
//=> fruits
$('.apple').attr('id', 'favorite').html()
//=> <li class="apple" id="favorite">Apple</li>
$('[xml\\:id="main"');
```
> See http://api.jquery.com/attr/ for more information
### Rendering
#### .prop( name, value )
Method for getting and setting properties. Gets the property value for only the first element in the matched set.
When you're ready to render the document, you can call the `html` method on the "root" selection:
```js
$('input[type="checkbox"]').prop('checked')
//=> false
$('input[type="checkbox"]').prop('checked', true).val()
//=> ok
$.root().html();
//=> <html>
// <head></head>
// <body>
// <ul id="fruits">
// <li class="apple">Apple</li>
// <li class="orange">Orange</li>
// <li class="pear">Pear</li>
// </ul>
// </body>
// </html>
```
> See http://api.jquery.com/prop/ for more information
If you want to render the [`outerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML) of a selection, you can use the `html` utility functon:
#### .data( name, value )
Method for getting and setting data attributes. Gets or sets the data attribute value for only the first element in the matched set.
```js
$('<div data-apple-color="red"></div>').data()
//=> { appleColor: 'red' }
$('<div data-apple-color="red"></div>').data('apple-color')
//=> 'red'
const apple = $('.apple').data('kind', 'mac')
apple.data('kind')
//=> 'mac'
```
> See http://api.jquery.com/data/ for more information
#### .val( [value] )
Method for getting and setting the value of input, select, and textarea. Note: Support for `map`, and `function` has not been added yet.
```js
$('input[type="text"]').val()
//=> input_text
$('input[type="text"]').val('test').html()
//=> <input type="text" value="test"/>
```
#### .removeAttr( name )
Method for removing attributes by `name`.
```js
$('.pear').removeAttr('class').html()
//=> <li>Pear</li>
```
#### .hasClass( className )
Check to see if *any* of the matched elements have the given `className`.
```js
$('.pear').hasClass('pear')
//=> true
$('apple').hasClass('fruit')
//=> false
$('li').hasClass('pear')
//=> true
```
#### .addClass( className )
Adds class(es) to all of the matched elements. Also accepts a `function` like jQuery.
```js
$('.pear').addClass('fruit').html()
//=> <li class="pear fruit">Pear</li>
$('.apple').addClass('fruit red').html()
//=> <li class="apple fruit red">Apple</li>
```
> See http://api.jquery.com/addClass/ for more information.
#### .removeClass( [className] )
Removes one or more space-separated classes from the selected elements. If no `className` is defined, all classes will be removed. Also accepts a `function` like jQuery.
```js
$('.pear').removeClass('pear').html()
//=> <li class="">Pear</li>
$('.apple').addClass('red').removeClass().html()
//=> <li class="">Apple</li>
```
> See http://api.jquery.com/removeClass/ for more information.
#### .toggleClass( className, [switch] )
Add or remove class(es) from the matched elements, depending on either the class's presence or the value of the switch argument. Also accepts a `function` like jQuery.
```js
$('.apple.green').toggleClass('fruit green red').html()
//=> <li class="apple fruit red">Apple</li>
$('.apple.green').toggleClass('fruit green red', true).html()
//=> <li class="apple green fruit red">Apple</li>
```
> See http://api.jquery.com/toggleClass/ for more information.
#### .is( selector )
#### .is( element )
#### .is( selection )
#### .is( function(index) )
Checks the current list of elements and returns `true` if _any_ of the elements match the selector. If using an element or Cheerio selection, returns `true` if _any_ of the elements match. If using a predicate function, the function is executed in the context of the selected element, so `this` refers to the current element.
### Forms
#### .serializeArray()
Encode a set of form elements as an array of names and values.
```js
$('<form><input name="foo" value="bar" /></form>').serializeArray()
//=> [ { name: 'foo', value: 'bar' } ]
```
### Traversing
#### .find(selector)
#### .find(selection)
#### .find(node)
Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element.
```js
$('#fruits').find('li').length
//=> 3
$('#fruits').find($('.apple')).length
//=> 1
```
#### .parent([selector])
Get the parent of each element in the current set of matched elements, optionally filtered by a selector.
```js
$('.pear').parent().attr('id')
//=> fruits
```
#### .parents([selector])
Get a set of parents filtered by `selector` of each element in the current set of match elements.
```js
$('.orange').parents().length
// => 2
$('.orange').parents('#fruits').length
// => 1
```
#### .parentsUntil([selector][,filter])
Get the ancestors of each element in the current set of matched elements, up to but not including the element matched by the selector, DOM node, or cheerio object.
```js
$('.orange').parentsUntil('#food').length
// => 1
```
#### .closest(selector)
For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.
```js
$('.orange').closest()
// => []
$('.orange').closest('.apple')
// => []
$('.orange').closest('li')
// => [<li class="orange">Orange</li>]
$('.orange').closest('#fruits')
// => [<ul id="fruits"> ... </ul>]
```
#### .next([selector])
Gets the next sibling of the first selected element, optionally filtered by a selector.
```js
$('.apple').next().hasClass('orange')
//=> true
```
#### .nextAll([selector])
Gets all the following siblings of the first selected element, optionally filtered by a selector.
```js
$('.apple').nextAll()
//=> [<li class="orange">Orange</li>, <li class="pear">Pear</li>]
$('.apple').nextAll('.orange')
//=> [<li class="orange">Orange</li>]
```
#### .nextUntil([selector], [filter])
Gets all the following siblings up to but not including the element matched by the selector, optionally filtered by another selector.
```js
$('.apple').nextUntil('.pear')
//=> [<li class="orange">Orange</li>]
```
#### .prev([selector])
Gets the previous sibling of the first selected element optionally filtered by a selector.
```js
$('.orange').prev().hasClass('apple')
//=> true
```
#### .prevAll([selector])
Gets all the preceding siblings of the first selected element, optionally filtered by a selector.
```js
$('.pear').prevAll()
//=> [<li class="orange">Orange</li>, <li class="apple">Apple</li>]
$('.pear').prevAll('.orange')
//=> [<li class="orange">Orange</li>]
```
#### .prevUntil([selector], [filter])
Gets all the preceding siblings up to but not including the element matched by the selector, optionally filtered by another selector.
```js
$('.pear').prevUntil('.apple')
//=> [<li class="orange">Orange</li>]
```
#### .slice( start, [end] )
Gets the elements matching the specified range
```js
$('li').slice(1).eq(0).text()
//=> 'Orange'
$('li').slice(1, 2).length
//=> 1
```
#### .siblings([selector])
Gets the first selected element's siblings, excluding itself.
```js
$('.pear').siblings().length
//=> 2
$('.pear').siblings('.orange').length
//=> 1
```
#### .children([selector])
Gets the children of the first selected element.
```js
$('#fruits').children().length
//=> 3
$('#fruits').children('.pear').text()
//=> Pear
```
#### .contents()
Gets the children of each element in the set of matched elements, including text and comment nodes.
```js
$('#fruits').contents().length
//=> 3
```
#### .each( function(index, element) )
Iterates over a cheerio object, executing a function for each matched element. When the callback is fired, the function is fired in the context of the DOM element, so `this` refers to the current element, which is equivalent to the function parameter `element`. To break out of the `each` loop early, return with `false`.
```js
const fruits = [];
$('li').each(function(i, elem) {
fruits[i] = $(this).text();
});
fruits.join(', ');
//=> Apple, Orange, Pear
```
#### .map( function(index, element) )
Pass each element in the current matched set through a function, producing a new Cheerio object containing the return values. The function can return an individual data item or an array of data items to be inserted into the resulting set. If an array is returned, the elements inside the array are inserted into the set. If the function returns null or undefined, no element will be inserted.
```js
$('li').map(function(i, el) {
// this === el
return $(this).text();
}).get().join(' ');
//=> "apple orange pear"
```
#### .filter( selector ) <br /> .filter( selection ) <br /> .filter( element ) <br /> .filter( function(index, element) )
Iterates over a cheerio object, reducing the set of selector elements to those that match the selector or pass the function's test. When a Cheerio selection is specified, return only the elements contained in that selection. When an element is specified, return only that element (if it is contained in the original selection). If using the function method, the function is executed in the context of the selected element, so `this` refers to the current element.
Selector:
```js
$('li').filter('.orange').attr('class');
//=> orange
```
Function:
```js
$('li').filter(function(i, el) {
// this === el
return $(this).attr('class') === 'orange';
}).attr('class')
//=> orange
```
#### .not( selector ) <br /> .not( selection ) <br /> .not( element ) <br /> .not( function(index, elem) )
Remove elements from the set of matched elements. Given a jQuery object that represents a set of DOM elements, the `.not()` method constructs a new jQuery object from a subset of the matching elements. The supplied selector is tested against each element; the elements that don't match the selector will be included in the result. The `.not()` method can take a function as its argument in the same way that `.filter()` does. Elements for which the function returns true are excluded from the filtered set; all other elements are included.
Selector:
```js
$('li').not('.apple').length;
//=> 2
```
Function:
```js
$('li').not(function(i, el) {
// this === el
return $(this).attr('class') === 'orange';
}).length;
//=> 2
```
#### .has( selector ) <br /> .has( element )
Filters the set of matched elements to only those which have the given DOM element as a descendant or which have a descendant that matches the given selector. Equivalent to `.filter(':has(selector)')`.
Selector:
```js
$('ul').has('.pear').attr('id');
//=> fruits
```
Element:
```js
$('ul').has($('.pear')[0]).attr('id');
//=> fruits
```
#### .first()
Will select the first element of a cheerio object
```js
$('#fruits').children().first().text()
//=> Apple
```
#### .last()
Will select the last element of a cheerio object
```js
$('#fruits').children().last().text()
//=> Pear
```
#### .eq( i )
Reduce the set of matched elements to the one at the specified index. Use `.eq(-i)` to count backwards from the last selected element.
```js
$('li').eq(0).text()
//=> Apple
$('li').eq(-1).text()
//=> Pear
```
#### .get( [i] )
Retrieve the DOM elements matched by the Cheerio object. If an index is specified, retrieve one of the elements matched by the Cheerio object:
```js
$('li').get(0).tagName
//=> li
```
If no index is specified, retrieve all elements matched by the Cheerio object:
```js
$('li').get().length
//=> 3
```
#### .index()
#### .index( selector )
#### .index( nodeOrSelection )
Search for a given element from among the matched elements.
```js
$('.pear').index()
//=> 2
$('.orange').index('li')
//=> 1
$('.apple').index($('#fruit, li'))
//=> 1
```
#### .end()
End the most recent filtering operation in the current chain and return the set of matched elements to its previous state.
```js
$('li').eq(0).end().length
//=> 3
```
#### .add( selector [, context] )
#### .add( element )
#### .add( elements )
#### .add( html )
#### .add( selection )
Add elements to the set of matched elements.
```js
$('.apple').add('.orange').length
//=> 2
```
#### .addBack( [filter] )
Add the previous set of elements on the stack to the current set, optionally filtered by a selector.
```js
$('li').eq(0).addBack('.orange').length
//=> 2
```
### Manipulation
Methods for modifying the DOM structure.
#### .append( content, [content, ...] )
Inserts content as the *last* child of each of the selected elements.
```js
$('ul').append('<li class="plum">Plum</li>')
$.html()
//=> <ul id="fruits">
// <li class="apple">Apple</li>
// <li class="orange">Orange</li>
// <li class="pear">Pear</li>
// <li class="plum">Plum</li>
// </ul>
```
#### .appendTo( target )
Insert every element in the set of matched elements to the end of the target.
```js
$('<li class="plum">Plum</li>').appendTo('#fruits')
$.html()
//=> <ul id="fruits">
// <li class="apple">Apple</li>
// <li class="orange">Orange</li>
// <li class="pear">Pear</li>
// <li class="plum">Plum</li>
// </ul>
```
#### .prepend( content, [content, ...] )
Inserts content as the *first* child of each of the selected elements.
```js
$('ul').prepend('<li class="plum">Plum</li>')
$.html()
//=> <ul id="fruits">
// <li class="plum">Plum</li>
// <li class="apple">Apple</li>
// <li class="orange">Orange</li>
// <li class="pear">Pear</li>
// </ul>
```
#### .prependTo( target )
Insert every element in the set of matched elements to the beginning of the target.
```js
$('<li class="plum">Plum</li>').prependTo('#fruits')
$.html()
//=> <ul id="fruits">
// <li class="plum">Plum</li>
// <li class="apple">Apple</li>
// <li class="orange">Orange</li>
// <li class="pear">Pear</li>
// </ul>
```
#### .after( content, [content, ...] )
Insert content next to each element in the set of matched elements.
```js
$('.apple').after('<li class="plum">Plum</li>')
$.html()
//=> <ul id="fruits">
// <li class="apple">Apple</li>
// <li class="plum">Plum</li>
// <li class="orange">Orange</li>
// <li class="pear">Pear</li>
// </ul>
```
#### .insertAfter( target )
Insert every element in the set of matched elements after the target.
```js
$('<li class="plum">Plum</li>').insertAfter('.apple')
$.html()
//=> <ul id="fruits">
// <li class="apple">Apple</li>
// <li class="plum">Plum</li>
// <li class="orange">Orange</li>
// <li class="pear">Pear</li>
// </ul>
```
#### .before( content, [content, ...] )
Insert content previous to each element in the set of matched elements.
```js
$('.apple').before('<li class="plum">Plum</li>')
$.html()
//=> <ul id="fruits">
// <li class="plum">Plum</li>
// <li class="apple">Apple</li>
// <li class="orange">Orange</li>
// <li class="pear">Pear</li>
// </ul>
```
#### .insertBefore( target )
Insert every element in the set of matched elements before the target.
```js
$('<li class="plum">Plum</li>').insertBefore('.apple')
$.html()
//=> <ul id="fruits">
// <li class="plum">Plum</li>
// <li class="apple">Apple</li>
// <li class="orange">Orange</li>
// <li class="pear">Pear</li>
// </ul>
```
#### .remove( [selector] )
Removes the set of matched elements from the DOM and all their children. `selector` filters the set of matched elements to be removed.
```js
$('.pear').remove()
$.html()
//=> <ul id="fruits">
// <li class="apple">Apple</li>
// <li class="orange">Orange</li>
// </ul>
```
#### .replaceWith( content )
Replaces matched elements with `content`.
```js
const plum = $('<li class="plum">Plum</li>')
$('.pear').replaceWith(plum)
$.html()
//=> <ul id="fruits">
// <li class="apple">Apple</li>
// <li class="orange">Orange</li>
// <li class="plum">Plum</li>
// </ul>
```
#### .empty()
Empties an element, removing all its children.
```js
$('ul').empty()
$.html()
//=> <ul id="fruits"></ul>
```
#### .html( [htmlString] )
Gets an html content string from the first selected element. If `htmlString` is specified, each selected element's content is replaced by the new content.
```js
$('.orange').html()
//=> Orange
$('#fruits').html('<li class="mango">Mango</li>').html()
//=> <li class="mango">Mango</li>
```
#### .text( [textString] )
Get the combined text contents of each element in the set of matched elements, including their descendants. If `textString` is specified, each selected element's content is replaced by the new text content.
```js
$('.orange').text()
//=> Orange
$('ul').text()
//=> Apple
// Orange
// Pear
```
#### .wrap( content )
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.
```js
const redFruit = $('<div class="red-fruit"></div>')
$('.apple').wrap(redFruit)
//=> <ul id="fruits">
// <div class="red-fruit">
// <li class="apple">Apple</li>
// </div>
// <li class="orange">Orange</li>
// <li class="plum">Plum</li>
// </ul>
const healthy = $('<div class="healthy"></div>')
$('li').wrap(healthy)
//=> <ul id="fruits">
// <div class="healthy">
// <li class="apple">Apple</li>
// </div>
// <div class="healthy">
// <li class="orange">Orange</li>
// </div>
// <div class="healthy">
// <li class="plum">Plum</li>
// </div>
// </ul>
```
#### .css( [propertName] ) <br /> .css( [ propertyNames] ) <br /> .css( [propertyName], [value] ) <br /> .css( [propertName], [function] ) <br /> .css( [properties] )
Get the value of a style property for the first element in the set of matched elements or set one or more CSS properties for every matched element.
### Rendering
When you're ready to render the document, you can use the `html` utility function:
```js
$.html()
//=> <ul id="fruits">
// <li class="apple">Apple</li>
// <li class="orange">Orange</li>
// <li class="pear">Pear</li>
// </ul>
```
If you want to return the outerHTML you can use `$.html(selector)`:
```js
$.html('.pear')
cheerio.html($('.pear'));
//=> <li class="pear">Pear</li>

@@ -863,3 +229,3 @@ ```

```js
$.xml()
$.xml();
//=> <media:thumbnail url="http://www.foo.com/keyframe.jpg" width="75" height="50" time="12:05:01.123"/>

@@ -871,53 +237,7 @@ ```

```js
const $ = cheerio.load('This is <em>content</em>.')
$.text()
const $ = cheerio.load('This is <em>content</em>.');
cheerio.text($('body'));
//=> This is content.
```
The method may be called on the Cheerio module itself--be sure to pass a collection of nodes!
```js
const $ = cheerio.load('<div>This is <em>content</em>.</div>')
cheerio.text($('div'))
//=> This is content.
```
### Miscellaneous
DOM element methods that don't fit anywhere else
#### .toArray()
Retrieve all the DOM elements contained in the jQuery set as an array.
```js
$('li').toArray()
//=> [ {...}, {...}, {...} ]
```
#### .clone() ####
Clone the cheerio object.
```js
const moreFruit = $('#fruits').clone()
```
### Utilities
#### $.root
Sometimes you need to work with the top-level root element. To query it, you can use `$.root()`.
```js
$.root().append('<ul id="vegetables"></ul>').html();
//=> <ul id="fruits">...</ul><ul id="vegetables"></ul>
```
#### $.contains( container, contained )
Checks to see if the `contained` DOM element is a descendant of the `container` DOM element.
#### $.parseHTML( data [, context ] [, keepScripts ] )
Parses a string into an array of DOM nodes. The `context` argument has no meaning for Cheerio, but it is maintained for API compatability.
#### $.load( html[, options ] )
Create a querying function, bound to a document created from the provided markup. Note that similar to web browser contexts, this operation may introduce `<html>`, `<head>`, and `<body>` elements. See the previous section titled "Loading" for usage information.
### Plugins

@@ -929,3 +249,3 @@

const $ = cheerio.load('<html><body>Hello, <b>world</b>!</body></html>');
$.prototype.logHtml = function() {
$.prototype.logHtml = function () {
console.log(this.html());

@@ -939,3 +259,3 @@ };

Cheerio collections are made up of objects that bear some resemblence to [browser-based DOM nodes](https://developer.mozilla.org/en-US/docs/Web/API/Node). You can expect them to define the following properties:
Cheerio collections are made up of objects that bear some resemblance to [browser-based DOM nodes](https://developer.mozilla.org/en-US/docs/Web/API/Node). You can expect them to define the following properties:

@@ -957,67 +277,2 @@ - `tagName`

## Contributors
These are some of the contributors that have made cheerio possible:
```
project : cheerio
repo age : 2 years, 6 months
active : 285 days
commits : 762
files : 36
authors :
293 Matt Mueller 38.5%
133 Matthew Mueller 17.5%
92 Mike Pennisi 12.1%
54 David Chambers 7.1%
30 kpdecker 3.9%
19 Felix Böhm 2.5%
17 fb55 2.2%
15 Siddharth Mahendraker 2.0%
11 Adam Bretz 1.4%
8 Nazar Leush 1.0%
7 ironchefpython 0.9%
6 Jarno Leppänen 0.8%
5 Ben Sheldon 0.7%
5 Jos Shepherd 0.7%
5 Ryan Schmukler 0.7%
5 Steven Vachon 0.7%
4 Maciej Adwent 0.5%
4 Amir Abu Shareb 0.5%
3 jeremy.dentel@brandingbrand.com 0.4%
3 Andi Neck 0.4%
2 steve 0.3%
2 alexbardas 0.3%
2 finspin 0.3%
2 Ali Farhadi 0.3%
2 Chris Khoo 0.3%
2 Rob Ashton 0.3%
2 Thomas Heymann 0.3%
2 Jaro Spisak 0.3%
2 Dan Dascalescu 0.3%
2 Torstein Thune 0.3%
2 Wayne Larsen 0.3%
1 Timm Preetz 0.1%
1 Xavi 0.1%
1 Alex Shaindlin 0.1%
1 mattym 0.1%
1 Felix Böhm 0.1%
1 Farid Neshat 0.1%
1 Dmitry Mazuro 0.1%
1 Jeremy Hubble 0.1%
1 nevermind 0.1%
1 Manuel Alabor 0.1%
1 Matt Liegey 0.1%
1 Chris O'Hara 0.1%
1 Michael Holroyd 0.1%
1 Michiel De Mey 0.1%
1 Ben Atkin 0.1%
1 Rich Trott 0.1%
1 Rob "Hurricane" Ashton 0.1%
1 Robin Gloster 0.1%
1 Simon Boudrias 0.1%
1 Sindre Sorhus 0.1%
1 xiaohwan 0.1%
```
## Cheerio in the real world

@@ -1038,2 +293,72 @@

## Sponsors
Does your company use Cheerio in production? Please consider [sponsoring this project](https://opencollective.com/cheerio#sponsor). Your help will allow maintainers to dedicate more time and resources to its development and support.
<a href="https://opencollective.com/cheerio/sponsor/0/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/1/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/2/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/3/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/4/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/5/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/6/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/7/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/8/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/9/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/9/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/10/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/10/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/11/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/11/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/12/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/12/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/13/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/13/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/14/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/14/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/15/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/15/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/16/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/16/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/17/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/17/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/18/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/18/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/19/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/19/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/20/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/20/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/21/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/21/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/22/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/22/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/23/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/23/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/24/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/24/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/25/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/25/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/26/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/26/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/27/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/27/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/28/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/28/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/sponsor/29/website" target="_blank"><img src="https://opencollective.com/cheerio/sponsor/29/avatar.svg"></a>
## Backers
[Become a backer](https://opencollective.com/cheerio#backer) to show your support for Cheerio and help us maintain and improve this open source project.
<a href="https://opencollective.com/cheerio/backer/0/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/0/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/1/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/1/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/2/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/2/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/3/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/3/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/4/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/4/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/5/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/5/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/6/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/6/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/7/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/7/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/8/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/8/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/9/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/9/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/10/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/10/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/11/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/11/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/12/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/12/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/13/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/13/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/14/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/14/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/15/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/15/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/16/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/16/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/17/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/17/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/18/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/18/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/19/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/19/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/20/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/20/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/21/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/21/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/22/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/22/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/23/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/23/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/24/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/24/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/25/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/25/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/26/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/26/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/27/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/27/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/28/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/28/avatar.svg"></a>
<a href="https://opencollective.com/cheerio/backer/29/website" target="_blank"><img src="https://opencollective.com/cheerio/backer/29/avatar.svg"></a>
## Special Thanks

@@ -1043,9 +368,9 @@

__&#8226; @FB55 for node-htmlparser2 & CSSSelect:__
**&#8226; @FB55 for node-htmlparser2 & CSSSelect:**
Felix has a knack for writing speedy parsing engines. He completely re-wrote both @tautologistic's `node-htmlparser` and @harry's `node-soupselect` from the ground up, making both of them much faster and more flexible. Cheerio would not be possible without his foundational work
__&#8226; @jQuery team for jQuery:__
**&#8226; @jQuery team for jQuery:**
The core API is the best of its class and despite dealing with all the browser inconsistencies the code base is extremely clean and easy to follow. Much of cheerio's implementation and documentation is from jQuery. Thanks guys.
__&#8226; @visionmedia:__
**&#8226; @visionmedia:**
The style, the structure, the open-source"-ness" of this library comes from studying TJ's style and using many of his libraries. This dude consistently pumps out high-quality libraries and has always been more than willing to help or answer questions. You rock TJ.

@@ -1052,0 +377,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc