Index: node_modules/vulcanize/node_modules/whacko/lib/api/traversing.js |
diff --git a/node_modules/vulcanize/node_modules/whacko/lib/api/traversing.js b/node_modules/vulcanize/node_modules/whacko/lib/api/traversing.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c7209fe727f16a0e0544feff574cd0adbdf3a63d |
--- /dev/null |
+++ b/node_modules/vulcanize/node_modules/whacko/lib/api/traversing.js |
@@ -0,0 +1,416 @@ |
+var _ = require('lodash'), |
+ select = require('CSSselect'), |
+ utils = require('../utils'), |
+ domEach = utils.domEach, |
+ uniqueSort = require('domutils').uniqueSort, |
+ isTag = utils.isTag; |
+ |
+exports.find = function(selectorOrHaystack) { |
+ var elems = _.reduce(this, function(memo, elem) { |
+ return memo.concat(_.filter(elem.children, isTag)); |
+ }, []); |
+ var contains = this.constructor.contains; |
+ var haystack; |
+ |
+ if (selectorOrHaystack && typeof selectorOrHaystack !== 'string') { |
+ if (selectorOrHaystack.cheerio) { |
+ haystack = selectorOrHaystack.get(); |
+ } else { |
+ haystack = [selectorOrHaystack]; |
+ } |
+ |
+ 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; |
+ } |
+ } |
+ }, this)); |
+ } |
+ |
+ return this._make(select(selectorOrHaystack, elems, this.options)); |
+}; |
+ |
+// Get the parent of each element in the current set of matched elements, |
+// optionally filtered by a selector. |
+exports.parent = function(selector) { |
+ var set = []; |
+ |
+ domEach(this, function(idx, elem) { |
+ var parentElem = elem.parent; |
+ if (parentElem && set.indexOf(parentElem) < 0) { |
+ set.push(parentElem); |
+ } |
+ }); |
+ |
+ if (arguments.length) { |
+ set = exports.filter.call(set, selector, this); |
+ } |
+ |
+ return this._make(set); |
+}; |
+ |
+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); |
+}; |
+ |
+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. |
+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); |
+}; |
+ |
+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 ? |
+ exports.filter.call(elems, selector, this) : |
+ this._make(elems); |
+}; |
+ |
+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 ? |
+ exports.filter.call(elems, selector, this) : |
+ this._make(elems); |
+}; |
+ |
+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 ? |
+ exports.filter.call(elems, filterSelector, this) : |
+ this._make(elems); |
+}; |
+ |
+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 ? |
+ exports.filter.call(elems, selector, this) : |
+ this._make(elems); |
+}; |
+ |
+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 ? |
+ exports.filter.call(elems, selector, this) : |
+ this._make(elems); |
+}; |
+ |
+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 ? |
+ exports.filter.call(elems, filterSelector, this) : |
+ this._make(elems); |
+}; |
+ |
+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 exports.filter.call(elems, selector, this); |
+ } else { |
+ return this._make(elems); |
+ } |
+}; |
+ |
+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 exports.filter.call(elems, selector, this); |
+}; |
+ |
+exports.contents = function() { |
+ return this._make(_.reduce(this, function(all, elem) { |
+ all.push.apply(all, elem.children); |
+ return all; |
+ }, [])); |
+}; |
+ |
+exports.each = function(fn) { |
+ var i = 0, len = this.length; |
+ while (i < len && fn.call(this[i], i, this[i]) !== false) ++i; |
+ return this; |
+}; |
+ |
+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 makeFilterMethod = function(filterFn) { |
+ return function(match, container) { |
+ var testFn; |
+ container = container || this; |
+ |
+ 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; |
+ }; |
+ } |
+ |
+ return container._make(filterFn(this, testFn)); |
+ }; |
+}; |
+ |
+exports.filter = makeFilterMethod(_.filter); |
+exports.not = makeFilterMethod(_.reject); |
+ |
+exports.first = function() { |
+ return this.length > 1 ? this._make(this[0]) : this; |
+}; |
+ |
+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) { |
+ 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. |
+exports.get = function(i) { |
+ if (i == null) { |
+ return Array.prototype.slice.call(this); |
+ } else { |
+ 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; |
+ |
+ if (arguments.length === 0) { |
+ $haystack = this.parent().children(); |
+ needle = this[0]; |
+ } else if (typeof selectorOrNeedle === 'string') { |
+ $haystack = this._make(selectorOrNeedle); |
+ needle = this[0]; |
+ } else { |
+ $haystack = this; |
+ needle = selectorOrNeedle.cheerio ? selectorOrNeedle[0] : selectorOrNeedle; |
+ } |
+ |
+ return $haystack.get().indexOf(needle); |
+}; |
+ |
+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 || exports.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. |
+exports.end = function() { |
+ return this.prevObject || this._make([]); |
+}; |
+ |
+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; |
+}; |
+ |
+// Add the previous set of elements on the stack to the current set, optionally |
+// filtered by a selector. |
+exports.addBack = function(selector) { |
+ return this.add( |
+ arguments.length ? this.prevObject.filter(selector) : this.prevObject |
+ ); |
+}; |