You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

148 lines
3.0 KiB

3 years ago
  1. /*
  2. Module dependencies
  3. */
  4. var parse = require('./parse'),
  5. isHtml = require('./utils').isHtml,
  6. _ = {
  7. extend: require('lodash.assignin'),
  8. bind: require('lodash.bind'),
  9. forEach: require('lodash.foreach'),
  10. defaults: require('lodash.defaults')
  11. };
  12. /*
  13. * The API
  14. */
  15. var api = [
  16. require('./api/attributes'),
  17. require('./api/traversing'),
  18. require('./api/manipulation'),
  19. require('./api/css'),
  20. require('./api/forms')
  21. ];
  22. /*
  23. * Instance of cheerio
  24. */
  25. var Cheerio = module.exports = function(selector, context, root, options) {
  26. if (!(this instanceof Cheerio)) return new Cheerio(selector, context, root, options);
  27. this.options = _.defaults(options || {}, this.options);
  28. // $(), $(null), $(undefined), $(false)
  29. if (!selector) return this;
  30. if (root) {
  31. if (typeof root === 'string') root = parse(root, this.options);
  32. this._root = Cheerio.call(this, root);
  33. }
  34. // $($)
  35. if (selector.cheerio) return selector;
  36. // $(dom)
  37. if (isNode(selector))
  38. selector = [selector];
  39. // $([dom])
  40. if (Array.isArray(selector)) {
  41. _.forEach(selector, _.bind(function(elem, idx) {
  42. this[idx] = elem;
  43. }, this));
  44. this.length = selector.length;
  45. return this;
  46. }
  47. // $(<html>)
  48. if (typeof selector === 'string' && isHtml(selector)) {
  49. return Cheerio.call(this, parse(selector, this.options).children);
  50. }
  51. // If we don't have a context, maybe we have a root, from loading
  52. if (!context) {
  53. context = this._root;
  54. } else if (typeof context === 'string') {
  55. if (isHtml(context)) {
  56. // $('li', '<ul>...</ul>')
  57. context = parse(context, this.options);
  58. context = Cheerio.call(this, context);
  59. } else {
  60. // $('li', 'ul')
  61. selector = [context, selector].join(' ');
  62. context = this._root;
  63. }
  64. // $('li', node), $('li', [nodes])
  65. } else if (!context.cheerio) {
  66. context = Cheerio.call(this, context);
  67. }
  68. // If we still don't have a context, return
  69. if (!context) return this;
  70. // #id, .class, tag
  71. return context.find(selector);
  72. };
  73. /**
  74. * Mix in `static`
  75. */
  76. _.extend(Cheerio, require('./static'));
  77. /*
  78. * Set a signature of the object
  79. */
  80. Cheerio.prototype.cheerio = '[cheerio object]';
  81. /*
  82. * Cheerio default options
  83. */
  84. Cheerio.prototype.options = {
  85. withDomLvl1: true,
  86. normalizeWhitespace: false,
  87. xmlMode: false,
  88. decodeEntities: true
  89. };
  90. /*
  91. * Make cheerio an array-like object
  92. */
  93. Cheerio.prototype.length = 0;
  94. Cheerio.prototype.splice = Array.prototype.splice;
  95. /*
  96. * Make a cheerio object
  97. *
  98. * @api private
  99. */
  100. Cheerio.prototype._make = function(dom, context) {
  101. var cheerio = new this.constructor(dom, context, this._root, this.options);
  102. cheerio.prevObject = this;
  103. return cheerio;
  104. };
  105. /**
  106. * Turn a cheerio object into an array
  107. */
  108. Cheerio.prototype.toArray = function() {
  109. return this.get();
  110. };
  111. /**
  112. * Plug in the API
  113. */
  114. api.forEach(function(mod) {
  115. _.extend(Cheerio.prototype, mod);
  116. });
  117. var isNode = function(obj) {
  118. return obj.name || obj.type === 'text' || obj.type === 'comment';
  119. };