1
restaurant-search/scrape/node_modules/cheerio/lib/api/traversing.js
2014-07-08 13:35:52 +09:00

362 lines
9.4 KiB
JavaScript

var _ = require('lodash'),
select = require('CSSselect'),
utils = require('../utils'),
domEach = utils.domEach,
uniqueSort = require('htmlparser2').DomUtils.uniqueSort,
isTag = utils.isTag;
var find = exports.find = function(selector) {
var elems = _.reduce(this, function(memo, elem) {
return memo.concat(_.filter(elem.children, isTag));
}, []);
return this._make(select(selector, elems, this.options));
};
// Get the parent of each element in the current set of matched elements,
// optionally filtered by a selector.
var parent = exports.parent = function(selector) {
var set = [];
var $set;
domEach(this, function(idx, elem) {
var parentElem = elem.parent;
if (parentElem && set.indexOf(parentElem) < 0) {
set.push(parentElem);
}
});
if (arguments.length) {
set = filter.call(set, selector, this);
}
return this._make(set);
};
var parents = exports.parents = function(selector) {
var parentNodes = [];
// When multiple DOM elements are in the original set, the resulting set will
// be in *reverse* order of the original elements as well, with duplicates
// removed.
this.get().reverse().forEach(function(elem) {
traverseParents(this, elem.parent, selector, Infinity)
.forEach(function(node) {
if (parentNodes.indexOf(node) === -1) {
parentNodes.push(node);
}
}
);
}, this);
return this._make(parentNodes);
};
var parentsUntil = exports.parentsUntil = function(selector, filter) {
var parentNodes = [], untilNode, untilNodes;
if (typeof selector === 'string') {
untilNode = select(selector, this.parents().toArray(), this.options)[0];
} else if (selector && selector.cheerio) {
untilNodes = selector.toArray();
} else if (selector) {
untilNode = selector;
}
// When multiple DOM elements are in the original set, the resulting set will
// be in *reverse* order of the original elements as well, with duplicates
// removed.
this.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);
return this._make(filter ? 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.
var closest = exports.closest = function(selector) {
var set = [];
if (!selector) {
return this._make(set);
}
domEach(this, function(idx, elem) {
var closestElem = traverseParents(this, elem, selector, 1)[0];
// Do not add duplicate elements to the set
if (closestElem && set.indexOf(closestElem) < 0) {
set.push(closestElem);
}
}.bind(this));
return this._make(set);
};
var next = exports.next = function(selector) {
if (!this[0]) { return this; }
var elems = [];
_.forEach(this, function(elem) {
while ((elem = elem.next)) {
if (isTag(elem)) {
elems.push(elem);
return;
}
}
});
return selector ? filter.call(elems, selector, this) : this._make(elems);
};
var nextAll = exports.nextAll = function(selector) {
if (!this[0]) { return this; }
var elems = [];
_.forEach(this, function(elem) {
while ((elem = elem.next)) {
if (isTag(elem) && elems.indexOf(elem) === -1) {
elems.push(elem);
}
}
});
return selector ? filter.call(elems, selector, this) : this._make(elems);
};
var nextUntil = exports.nextUntil = function(selector, filterSelector) {
if (!this[0]) { return this; }
var elems = [], untilNode, untilNodes;
if (typeof selector === 'string') {
untilNode = select(selector, this.nextAll().get(), this.options)[0];
} else if (selector && selector.cheerio) {
untilNodes = selector.get();
} else if (selector) {
untilNode = selector;
}
_.forEach(this, function(elem) {
while ((elem = elem.next)) {
if ((untilNode && elem !== untilNode) ||
(untilNodes && untilNodes.indexOf(elem) === -1) ||
(!untilNode && !untilNodes)) {
if (isTag(elem) && elems.indexOf(elem) === -1) {
elems.push(elem);
}
} else {
break;
}
}
});
return filterSelector ?
filter.call(elems, filterSelector, this) :
this._make(elems);
};
var prev = exports.prev = function(selector) {
if (!this[0]) { return this; }
var elems = [];
_.forEach(this, function(elem) {
while ((elem = elem.prev)) {
if (isTag(elem)) {
elems.push(elem);
return;
}
}
});
return selector ? filter.call(elems, selector, this) : this._make(elems);
};
var prevAll = exports.prevAll = function(selector) {
if (!this[0]) { return this; }
var elems = [];
_.forEach(this, function(elem) {
while ((elem = elem.prev)) {
if (isTag(elem) && elems.indexOf(elem) === -1) {
elems.push(elem);
}
}
});
return selector ? filter.call(elems, selector, this) : this._make(elems);
};
var prevUntil = exports.prevUntil = function(selector, filterSelector) {
if (!this[0]) { return this; }
var elems = [], untilNode, untilNodes;
if (typeof selector === 'string') {
untilNode = select(selector, this.prevAll().get(), this.options)[0];
} else if (selector && selector.cheerio) {
untilNodes = selector.get();
} else if (selector) {
untilNode = selector;
}
_.forEach(this, function(elem) {
while ((elem = elem.prev)) {
if ((untilNode && elem !== untilNode) ||
(untilNodes && untilNodes.indexOf(elem) === -1) ||
(!untilNode && !untilNodes)) {
if (isTag(elem) && elems.indexOf(elem) === -1) {
elems.push(elem);
}
} else {
break;
}
}
});
return filterSelector ?
filter.call(elems, filterSelector, this) :
this._make(elems);
};
var siblings = exports.siblings = function(selector) {
var parent = this.parent();
var elems = _.filter(
parent ? parent.children() : this.siblingsAndMe(),
function(elem) { return isTag(elem) && !this.is(elem); },
this
);
if (selector !== undefined) {
return filter.call(elems, selector, this);
} else {
return this._make(elems);
}
};
var children = exports.children = function(selector) {
var elems = _.reduce(this, function(memo, elem) {
return memo.concat(_.filter(elem.children, isTag));
}, []);
if (selector === undefined) return this._make(elems);
else if (typeof selector === 'number') return this._make(elems[selector]);
return filter.call(elems, selector, this);
};
var contents = exports.contents = function() {
return this._make(_.reduce(this, function(all, elem) {
all.push.apply(all, elem.children);
return all;
}, []));
};
var each = exports.each = function(fn) {
var i = 0, len = this.length;
while (i < len && fn.call(this[i], i, this[i]) !== false) ++i;
return this;
};
var map = exports.map = function(fn) {
return this._make(_.reduce(this, function(memo, el, i) {
var val = fn.call(el, i, el);
return val == null ? memo : memo.concat(val);
}, []));
};
var filter = exports.filter = function(match, container) {
container = container || this;
var make = _.bind(container._make, container);
var filterFn;
if (typeof match === 'string') {
filterFn = select.compile(match, container.options);
} else if (typeof match === 'function') {
filterFn = function(el, i) {
return match.call(el, i, el);
};
} else if (match.cheerio) {
filterFn = match.is.bind(match);
} else {
filterFn = function(el) {
return match === el;
};
}
return make(_.filter(this, filterFn));
};
var first = exports.first = function() {
return this.length > 1 ? this._make(this[0]) : this;
};
var 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.
var eq = exports.eq = function(i) {
i = +i;
// Use the first identity optimization if possible
if (i === 0 && this.length <= 1) return this;
if (i < 0) i = this.length + i;
return this[i] ? this._make(this[i]) : this._make([]);
};
// Retrieve the DOM elements matched by the jQuery object.
var get = exports.get = function(i) {
if (i == null) {
return Array.prototype.slice.call(this);
} else {
return this[i < 0 ? (this.length + i) : i];
}
};
var slice = exports.slice = function() {
return this._make([].slice.apply(this, arguments));
};
function traverseParents(self, elem, selector, limit) {
var elems = [];
while (elem && elems.length < limit) {
if (!selector || filter.call([elem], selector, self).length) {
elems.push(elem);
}
elem = elem.parent;
}
return elems;
}
// End the most recent filtering operation in the current chain and return the
// set of matched elements to its previous state.
var end = exports.end = function() {
return this.prevObject || this._make([]);
};
var add = exports.add = function(other, context) {
var selection = this._make(other, context);
var contents = uniqueSort(selection.get().concat(this.get()));
for (var i = 0; i < contents.length; ++i) {
selection[i] = contents[i];
}
selection.length = contents.length;
return selection;
};