From e08b4762ba1ea39dfeaf2d04687ac9abfa78d4f6 Mon Sep 17 00:00:00 2001 From: Torstein Thune Date: Thu, 19 Sep 2013 17:12:44 +0200 Subject: [PATCH 01/11] Fix for keeping and using custom Cheerio options (Issue #273) Cheerio.prototype.options are now overwritten if Cheerio is instantiated with an options object. cheerioinstace.options is now passed to parser when adding new dom nodes to an existing Cheerio instance. --- lib/cheerio.js | 4 ++-- lib/static.js | 5 +++++ test/xml.js | 19 +++++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/cheerio.js b/lib/cheerio.js index 943571d83a..f378fa431e 100644 --- a/lib/cheerio.js +++ b/lib/cheerio.js @@ -38,7 +38,7 @@ var Cheerio = module.exports = function(selector, context, root) { if (!selector) return this; if (root) { - if (typeof root === 'string') root = parse(root); + if (typeof root === 'string') root = parse(root, this.options); this._root = this.make(root, this); } @@ -51,7 +51,7 @@ var Cheerio = module.exports = function(selector, context, root) { // $() if (typeof selector === 'string' && isHtml(selector)) { - return this.make(parse(selector).children); + return this.make(parse(selector, this.options).children); } // If we don't have a context, maybe we have a root, from loading diff --git a/lib/static.js b/lib/static.js index 10eabf5a2d..8f321a5ca3 100644 --- a/lib/static.js +++ b/lib/static.js @@ -15,6 +15,11 @@ var load = exports.load = function(str, options) { var Cheerio = require('./cheerio'), root = parse(str, options); + // Overwrite default options if custom options are present + if(typeof options === "object") { + Cheerio.prototype.options = options; + } + var initialize = function(selector, context, r) { return new Cheerio(selector, context, r || root); }; diff --git a/test/xml.js b/test/xml.js index d3a0bea6fb..0bf50d5eda 100644 --- a/test/xml.js +++ b/test/xml.js @@ -8,6 +8,11 @@ var xml = function(str, options) { return dom.xml(); }; +var dom = function(str, options) { + $ = cheerio.load('', options); + return $(str).html(); +} + describe('render', function() { describe('(xml)', function() { @@ -24,4 +29,18 @@ describe('render', function() { }); + describe('(dom)', function () { + + it('should keep camelCase for new nodes', function() { + var str = 'hello'; + expect(dom(str, {xmlMode: false})).to.equal('hello'); + }); + + it('should keep camelCase for new nodes', function() { + var str = 'hello'; + expect(dom(str, {xmlMode: true})).to.equal('hello'); + }); + + }); + }); From f9cdfcaa6f97dbf10e237fc22096c17d00896356 Mon Sep 17 00:00:00 2001 From: Torstein Thune Date: Fri, 20 Sep 2013 09:55:11 +0200 Subject: [PATCH 02/11] Updated to fit Cheerio's code conventions. --- lib/static.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/static.js b/lib/static.js index 8f321a5ca3..53423113d4 100644 --- a/lib/static.js +++ b/lib/static.js @@ -16,7 +16,7 @@ var load = exports.load = function(str, options) { root = parse(str, options); // Overwrite default options if custom options are present - if(typeof options === "object") { + if (typeof options === 'object') { Cheerio.prototype.options = options; } From 71216faaf812a014932618483e370abc5e02819b Mon Sep 17 00:00:00 2001 From: fb55 Date: Tue, 8 Apr 2014 15:14:13 +0200 Subject: [PATCH 03/11] added test case for preserving options as proposed by @jugglinmike in #274 --- test/xml.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/xml.js b/test/xml.js index 0db7c30806..1b01368416 100644 --- a/test/xml.js +++ b/test/xml.js @@ -41,6 +41,14 @@ describe('render', function() { expect(dom(str, {xmlMode: true})).to.equal('hello'); }); + it('should maintain the parsing options of distinct contexts independently', function() { + var str = 'hello'; + var $x = cheerio.load('', { xmlMode: false }); + var $h = cheerio.load('', { xmlMode: true }); + + expect($x(str).html()).to.equal('hello'); + }); + }); }); From d163a2dfc7f26089a6725766dc773101c9ebf5e1 Mon Sep 17 00:00:00 2001 From: fb55 Date: Tue, 8 Apr 2014 15:17:09 +0200 Subject: [PATCH 04/11] added `options` argument to constructor as proposed by @jugglinmike in #274, but using `this.options` instead of the prototype to enable `Cheerio.call` calls to work properly --- lib/cheerio.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/cheerio.js b/lib/cheerio.js index 18d0d1d126..1ffcecba7b 100644 --- a/lib/cheerio.js +++ b/lib/cheerio.js @@ -34,8 +34,10 @@ var $ = require('./static'); * Instance of cheerio */ -var Cheerio = module.exports = function(selector, context, root) { - if (!(this instanceof Cheerio)) return new Cheerio(selector, context, root); +var Cheerio = module.exports = function(selector, context, root, options) { + if (!(this instanceof Cheerio)) return new Cheerio(selector, context, root, options); + + this.options = _.defaults(options || {}, this.options); // $(), $(null), $(undefined), $(false) if (!selector) return this; From f55bd3518be2c25262b09085c84ca2cd48af7baf Mon Sep 17 00:00:00 2001 From: fb55 Date: Tue, 8 Apr 2014 15:21:17 +0200 Subject: [PATCH 05/11] preserve options in _make --- lib/cheerio.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cheerio.js b/lib/cheerio.js index 1ffcecba7b..96c74cd375 100644 --- a/lib/cheerio.js +++ b/lib/cheerio.js @@ -141,7 +141,7 @@ var isHtml = function(str) { */ Cheerio.prototype._make = function(dom) { - var cheerio = new Cheerio(dom); + var cheerio = new Cheerio(dom, undefined, undefined, this.options); cheerio.prevObject = this; return cheerio; }; From 82da920405149856d403ccb1975ca2eed1014ac3 Mon Sep 17 00:00:00 2001 From: fb55 Date: Tue, 8 Apr 2014 15:26:25 +0200 Subject: [PATCH 06/11] fixed jshint errors in tests --- test/xml.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/xml.js b/test/xml.js index 1b01368416..2a1f2197e8 100644 --- a/test/xml.js +++ b/test/xml.js @@ -9,9 +9,9 @@ var xml = function(str, options) { }; var dom = function(str, options) { - $ = cheerio.load('', options); + var $ = cheerio.load('', options); return $(str).html(); -} +}; describe('render', function() { From e47997084fae4114ccc4484a5ba4f8fe28d3ae7c Mon Sep 17 00:00:00 2001 From: fb55 Date: Tue, 8 Apr 2014 15:45:34 +0200 Subject: [PATCH 07/11] pass options to `parse` and `evaluate` --- lib/api/manipulation.js | 28 +++++++++++++++++----------- lib/cheerio.js | 7 +++---- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/lib/api/manipulation.js b/lib/api/manipulation.js index 4b443184c1..0af788c6d8 100644 --- a/lib/api/manipulation.js +++ b/lib/api/manipulation.js @@ -9,15 +9,17 @@ var _ = require('lodash'), slice = Array.prototype.slice; // Create an array of nodes, recursing into arrays and parsing strings if // necessary -var makeDomArray = function(elem) { +var makeDomArray = function(elem, options) { if (elem == null) { return []; } else if (elem.cheerio) { return elem.get(); } else if (_.isArray(elem)) { - return _.flatten(elem.map(makeDomArray)); + return _.flatten(elem.map(function(elem){ + return makeDomArray(elem, options); + })); } else if (_.isString(elem)) { - return evaluate(elem); + return evaluate(elem, options); } else { return [elem]; } @@ -26,11 +28,11 @@ var makeDomArray = function(elem) { var _insert = function(concatenator) { return function() { var elems = slice.call(arguments), - dom = makeDomArray(elems); + dom = makeDomArray(elems, this.options); if (_.isFunction(elems[0])) { return this.each(function(i, el) { - dom = makeDomArray(elems[0].call(el, i, this.html())); + dom = makeDomArray(elems[0].call(el, i, this.html()), this.options); concatenator(dom, el.children, el); }); } else { @@ -96,7 +98,8 @@ var prepend = exports.prepend = _insert(function(dom, children, parent) { var after = exports.after = function() { var elems = slice.call(arguments), - dom = makeDomArray(elems); + dom = makeDomArray(elems, this.options), + opts = this.options; domEach(this, function(i, el) { var parent = el.parent || el.root, @@ -107,7 +110,7 @@ var after = exports.after = function() { if (!~index) return; if (_.isFunction(elems[0])) { - dom = makeDomArray(elems[0].call(el, i)); + dom = makeDomArray(elems[0].call(el, i), opts); } // Add element after `this` element @@ -119,7 +122,8 @@ var after = exports.after = function() { var before = exports.before = function() { var elems = slice.call(arguments), - dom = makeDomArray(elems); + dom = makeDomArray(elems, this.options), + opts = this.options; domEach(this, function(i, el) { var parent = el.parent || el.root, @@ -130,7 +134,7 @@ var before = exports.before = function() { if (!~index) return; if (_.isFunction(elems[0])) { - dom = makeDomArray(elems[0].call(el, i)); + dom = makeDomArray(elems[0].call(el, i), opts); } // Add element before `el` element @@ -172,10 +176,12 @@ var remove = exports.remove = function(selector) { }; var replaceWith = exports.replaceWith = function(content) { + var opts = this.options; + domEach(this, function(i, el) { var parent = el.parent || el.root, siblings = parent.children, - dom = makeDomArray(_.isFunction(content) ? content.call(el, i, el) : content), + dom = makeDomArray(_.isFunction(content) ? content.call(el, i, el) : content, opts), index; // In the case that `dom` contains nodes that already exist in other @@ -212,7 +218,7 @@ var html = exports.html = function(str) { return $.html(this[0].children); } - str = str.cheerio ? str.get() : evaluate(str); + str = str.cheerio ? str.get() : evaluate(str, this.options); domEach(this, function(i, el) { _.each(el.children, function(el) { diff --git a/lib/cheerio.js b/lib/cheerio.js index 96c74cd375..3eef7bdc6a 100644 --- a/lib/cheerio.js +++ b/lib/cheerio.js @@ -4,7 +4,6 @@ var path = require('path'), parse = require('./parse'), - evaluate = parse.evaluate, _ = require('lodash'); /* @@ -43,7 +42,7 @@ var Cheerio = module.exports = function(selector, context, root, options) { if (!selector) return this; if (root) { - if (typeof root === 'string') root = parse(root); + if (typeof root === 'string') root = parse(root, this.options); this._root = Cheerio.call(this, root); } @@ -65,7 +64,7 @@ var Cheerio = module.exports = function(selector, context, root, options) { // $() if (typeof selector === 'string' && isHtml(selector)) { - return Cheerio.call(this, parse(selector).children); + return Cheerio.call(this, parse(selector, this.options).children); } // If we don't have a context, maybe we have a root, from loading @@ -74,7 +73,7 @@ var Cheerio = module.exports = function(selector, context, root, options) { } else if (typeof context === 'string') { if (isHtml(context)) { // $('li', '
    ...
') - context = parse(context); + context = parse(context, this.options); context = Cheerio.call(this, context); } else { // $('li', 'ul') From cc8a10ab7f2c711616611c88317b140e84d8408c Mon Sep 17 00:00:00 2001 From: fb55 Date: Tue, 8 Apr 2014 15:50:26 +0200 Subject: [PATCH 08/11] added options argument to .load --- lib/static.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/static.js b/lib/static.js index a8ce11a159..aedf6e0a30 100644 --- a/lib/static.js +++ b/lib/static.js @@ -5,18 +5,23 @@ var select = require('CSSselect'), parse = require('./parse'), render = require('./render'), - decode = require('./utils').decode; + decode = require('./utils').decode, + _ = require('lodash'); /** * $.load(str) */ var load = exports.load = function(content, options) { - var Cheerio = require('./cheerio'), - root = parse(content, options); + var Cheerio = require('./cheerio'); - var initialize = function(selector, context, r) { - return new Cheerio(selector, context, r || root); + options = _.defaults(options || {}, Cheerio.prototype.options); + + var root = parse(content, options); + + var initialize = function(selector, context, r, opts) { + opts = _.defaults(opts || {}, options); + return new Cheerio(selector, context, r || root, opts); }; // Add in the static methods From 0c595d1b4ca7ae8896ad43aadf661ebbc7d452ba Mon Sep 17 00:00:00 2001 From: fb55 Date: Tue, 8 Apr 2014 16:02:09 +0200 Subject: [PATCH 09/11] remove lowerCaseTags from default options caused failure of test cases (htmlparser2's behavior depends on the existence of the property) as a reminder for future optimizations, I've added the `decodeEntities` option instead --- lib/cheerio.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cheerio.js b/lib/cheerio.js index 3eef7bdc6a..d776c29b35 100644 --- a/lib/cheerio.js +++ b/lib/cheerio.js @@ -111,7 +111,7 @@ Cheerio.prototype.cheerio = '[cheerio object]'; Cheerio.prototype.options = { normalizeWhitespace: false, xmlMode: false, - lowerCaseTags: false + decodeEntities: false }; /* From a15e9e3b20b694ea95620c26c84b2ff5a3bbb113 Mon Sep 17 00:00:00 2001 From: fb55 Date: Tue, 8 Apr 2014 16:05:45 +0200 Subject: [PATCH 10/11] replace remaining `new Cheerio` with `.call` I'm not sure the behavior is the same (it's now equal to the code above) --- lib/cheerio.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cheerio.js b/lib/cheerio.js index d776c29b35..750ecc2293 100644 --- a/lib/cheerio.js +++ b/lib/cheerio.js @@ -82,7 +82,7 @@ var Cheerio = module.exports = function(selector, context, root, options) { } // $('li', node), $('li', [nodes]) } else if (!context.cheerio) { - context = new Cheerio(context); + context = Cheerio.call(this, context); } // If we still don't have a context, return From a1f289680fe8d713ca7f6e41caf1ae61434f666d Mon Sep 17 00:00:00 2001 From: fb55 Date: Tue, 8 Apr 2014 18:35:57 +0200 Subject: [PATCH 11/11] move makeDomArray to prototype --- lib/api/manipulation.js | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/lib/api/manipulation.js b/lib/api/manipulation.js index 0af788c6d8..cbe352d710 100644 --- a/lib/api/manipulation.js +++ b/lib/api/manipulation.js @@ -7,19 +7,18 @@ var _ = require('lodash'), domEach = utils.domEach, encode = utils.encode, slice = Array.prototype.slice; + // Create an array of nodes, recursing into arrays and parsing strings if // necessary -var makeDomArray = function(elem, options) { +exports._makeDomArray = function makeDomArray(elem) { if (elem == null) { return []; } else if (elem.cheerio) { return elem.get(); } else if (_.isArray(elem)) { - return _.flatten(elem.map(function(elem){ - return makeDomArray(elem, options); - })); + return _.flatten(elem.map(makeDomArray, this)); } else if (_.isString(elem)) { - return evaluate(elem, options); + return evaluate(elem, this.options); } else { return [elem]; } @@ -28,11 +27,11 @@ var makeDomArray = function(elem, options) { var _insert = function(concatenator) { return function() { var elems = slice.call(arguments), - dom = makeDomArray(elems, this.options); + dom = this._makeDomArray(elems); if (_.isFunction(elems[0])) { return this.each(function(i, el) { - dom = makeDomArray(elems[0].call(el, i, this.html()), this.options); + dom = this._makeDomArray(elems[0].call(el, i, this.html())); concatenator(dom, el.children, el); }); } else { @@ -98,8 +97,8 @@ var prepend = exports.prepend = _insert(function(dom, children, parent) { var after = exports.after = function() { var elems = slice.call(arguments), - dom = makeDomArray(elems, this.options), - opts = this.options; + dom = this._makeDomArray(elems), + self = this; domEach(this, function(i, el) { var parent = el.parent || el.root, @@ -110,7 +109,7 @@ var after = exports.after = function() { if (!~index) return; if (_.isFunction(elems[0])) { - dom = makeDomArray(elems[0].call(el, i), opts); + dom = self._makeDomArray(elems[0].call(el, i)); } // Add element after `this` element @@ -122,8 +121,8 @@ var after = exports.after = function() { var before = exports.before = function() { var elems = slice.call(arguments), - dom = makeDomArray(elems, this.options), - opts = this.options; + dom = this._makeDomArray(elems), + self = this; domEach(this, function(i, el) { var parent = el.parent || el.root, @@ -134,7 +133,7 @@ var before = exports.before = function() { if (!~index) return; if (_.isFunction(elems[0])) { - dom = makeDomArray(elems[0].call(el, i), opts); + dom = self._makeDomArray(elems[0].call(el, i)); } // Add element before `el` element @@ -176,12 +175,12 @@ var remove = exports.remove = function(selector) { }; var replaceWith = exports.replaceWith = function(content) { - var opts = this.options; + var self = this; domEach(this, function(i, el) { var parent = el.parent || el.root, siblings = parent.children, - dom = makeDomArray(_.isFunction(content) ? content.call(el, i, el) : content, opts), + dom = self._makeDomArray(_.isFunction(content) ? content.call(el, i, el) : content), index; // In the case that `dom` contains nodes that already exist in other