dom-iterator
Advanced tools
Comparing version 0.0.5 to 0.1.0
@@ -5,5 +5,11 @@ { | ||
"description": "iterate through DOM nodes", | ||
"version": "0.0.5", | ||
"keywords": ["iterate", "dom"], | ||
"dependencies": {}, | ||
"version": "0.1.0", | ||
"keywords": [ | ||
"iterate", | ||
"dom" | ||
], | ||
"dependencies": { | ||
"component/xor": "0.0.2", | ||
"component/props": "1.1.0" | ||
}, | ||
"development": { | ||
@@ -18,2 +24,2 @@ "component/domify": "*", | ||
] | ||
} | ||
} |
0.1.0 / 2014-02-07 | ||
================== | ||
* tests passing in node | ||
* added: it#select(expr) and it#reject(expr). | ||
* BREAKING removed: it#filter(nodeType) | ||
* added: iterator#revisit(revisit) | ||
* BREAKING traverses closing tags by default | ||
* added: root node | ||
* added: it#closing(), it#opening(), it#atOpening(), it#atClosing() | ||
* BREAKING removed: it#watch(expr, fn) | ||
0.0.5 / 2014-02-05 | ||
@@ -3,0 +15,0 @@ ================== |
292
index.js
@@ -5,3 +5,4 @@ /** | ||
var slice = Array.prototype.slice; | ||
var xor = require('xor'); | ||
var props = require('props'); | ||
@@ -18,2 +19,3 @@ /** | ||
* @param {Node} node | ||
* @param {Node} root | ||
* @return {iterator} self | ||
@@ -23,15 +25,20 @@ * @api public | ||
function iterator(node) { | ||
if (!(this instanceof iterator)) return new iterator(node); | ||
function iterator(node, root) { | ||
if (!(this instanceof iterator)) return new iterator(node, root); | ||
this.node = this.start = this.peaked = node; | ||
this.exprs = []; | ||
this.types = false; | ||
this.visitClosing = false; | ||
this.climbing = false; | ||
this.root = root; | ||
this.closingTag = false; | ||
this._revisit = true; | ||
this._selects = []; | ||
this._rejects = []; | ||
if (this.higher(node)) { | ||
throw new Error('root must be a parent or ancestor to node'); | ||
} | ||
} | ||
/** | ||
* Filter on the type | ||
* Reset the iterator | ||
* | ||
* @param {Number, ...} filters | ||
* @param {Node} node (optional) | ||
* @return {iterator} self | ||
@@ -41,10 +48,13 @@ * @api public | ||
iterator.prototype.filter = function() { | ||
var args = slice.call(arguments); | ||
var types = this.types = this.types || {}; | ||
iterator.prototype.reset = function(node) { | ||
this.node = node || this.start; | ||
return this; | ||
}; | ||
for (var i = 0, len = args.length; i < len; i++) { | ||
types[args[i]] = true; | ||
} | ||
/** | ||
* Revisit element nodes. Defaults to `true` | ||
*/ | ||
iterator.prototype.revisit = function(revisit) { | ||
this._revisit = undefined == revisit ? true : revisit; | ||
return this; | ||
@@ -54,11 +64,7 @@ }; | ||
/** | ||
* Reset the iterator | ||
* | ||
* @param {Node} node (optional) | ||
* @return {iterator} self | ||
* @api public | ||
* Jump to the opening tag | ||
*/ | ||
iterator.prototype.reset = function(node) { | ||
this.node = node || this.start; | ||
iterator.prototype.opening = function() { | ||
if (1 == this.node.nodeType) this.closingTag = false; | ||
return this; | ||
@@ -68,65 +74,47 @@ }; | ||
/** | ||
* Next node | ||
* | ||
* @param {Number} type | ||
* @return {Node|null} | ||
* @api public | ||
* Jump to the closing tag | ||
*/ | ||
iterator.prototype.next = traverse('nextSibling', 'firstChild'); | ||
iterator.prototype.atOpening = function() { | ||
return !this.closingTag; | ||
}; | ||
/** | ||
* Previous node | ||
* | ||
* @param {Number} type | ||
* @return {Node|null} | ||
* @api public | ||
* Jump to the closing tag | ||
*/ | ||
iterator.prototype.previous = | ||
iterator.prototype.prev = traverse('previousSibling', 'lastChild'); | ||
iterator.prototype.closing = function() { | ||
if (1 == this.node.nodeType) this.closingTag = true; | ||
return this; | ||
}; | ||
/** | ||
* Peak in either direction | ||
* `n` nodes. Peak backwards | ||
* using negative numbers. | ||
* | ||
* @param {Number} n (optional) | ||
* @return {Node|null} | ||
* @api public | ||
* Jump to the closing tag | ||
*/ | ||
iterator.prototype.peak = function(n) { | ||
n = undefined == n ? 1 : n; | ||
var node; | ||
iterator.prototype.atClosing = function() { | ||
return this.closingTag; | ||
}; | ||
if (!n) return this.node; | ||
else if (n > 0) while(n--) node = this.next(0, true); | ||
else while(n++) node = this.prev(0, true); | ||
this.peaked = node; | ||
return node; | ||
} | ||
/** | ||
* Visit closing tags | ||
* Next node | ||
* | ||
* @param {Number} type | ||
* @return {Node|null} | ||
* @api public | ||
*/ | ||
iterator.prototype.closing = function(close) { | ||
this.visitClosing = close; | ||
return this; | ||
} | ||
iterator.prototype.next = traverse('nextSibling', 'firstChild'); | ||
/** | ||
* Add a plugin | ||
* Previous node | ||
* | ||
* @param {Function} fn | ||
* @return {iterator} | ||
* @param {Number} type | ||
* @return {Node|null} | ||
* @api public | ||
*/ | ||
iterator.prototype.use = function(fn) { | ||
fn(this); | ||
return this; | ||
} | ||
iterator.prototype.previous = | ||
iterator.prototype.prev = traverse('previousSibling', 'lastChild'); | ||
@@ -143,27 +131,35 @@ /** | ||
function traverse(dir, child) { | ||
return function walk(i, peak) { | ||
var next = dir == 'nextSibling'; | ||
return function walk(expr, peak) { | ||
expr = this.compile(expr); | ||
var node = (peak) ? this.peaked : this.peaked = this.node; | ||
var closing = this.visitClosing; | ||
var start = this.start; | ||
var types = this.types; | ||
var exprs = this.exprs; | ||
var climbing = (closing) ? this.climbing : false; | ||
var closing = this.closingTag; | ||
var revisit = this._revisit; | ||
while (node) { | ||
if (!climbing && node[child]) { | ||
if (xor(next, closing) && node[child]) { | ||
// element with children: <em>...</em> | ||
node = node[child]; | ||
closing = !next; | ||
} else if (1 == node.nodeType && !node[child] && xor(next, closing)) { | ||
// empty element tag: <em></em> | ||
closing = next; | ||
if (!revisit) continue; | ||
} else if (node[dir]) { | ||
// element has a neighbor: ...<em></em>... | ||
node = node[dir]; | ||
climbing = false; | ||
closing = !next; | ||
} else { | ||
// done with current layer, move up. | ||
node = node.parentNode; | ||
climbing = true; | ||
if (!closing) continue; | ||
closing = next; | ||
if (!revisit) continue; | ||
} | ||
this.climbing = climbing; | ||
if (!node || this.higher(node, this.root)) break; | ||
if (!types || types[node.nodeType]) { | ||
if (expr(node) && this.selects(node) && this.rejects(node)) { | ||
if (peak) this.peaked = node; | ||
else this.node = node; | ||
this.closingTag = closing; | ||
return node; | ||
@@ -176,1 +172,143 @@ } | ||
} | ||
/** | ||
* Select nodes that cause `expr(node)` | ||
* to be truthy | ||
* | ||
* @param {Number|String|Function} expr | ||
* @return {iterator} self | ||
* @api public | ||
*/ | ||
iterator.prototype.select = function(expr) { | ||
expr = this.compile(expr); | ||
this._selects.push(expr); | ||
return this; | ||
}; | ||
/** | ||
* Run through the selects ORing each | ||
* | ||
* @return {Boolean} | ||
* @api private | ||
*/ | ||
iterator.prototype.selects = function(node) { | ||
var exprs = this._selects; | ||
var len = exprs.length; | ||
if (!len) return true; | ||
for (var i = 0; i < len; i++) { | ||
if (exprs[i](node)) return true; | ||
}; | ||
return false; | ||
}; | ||
/** | ||
* Select nodes that cause `expr(node)` | ||
* to be falsy | ||
* | ||
* @param {Number|String|Function} expr | ||
* @return {iterator} self | ||
* @api public | ||
*/ | ||
iterator.prototype.reject = function(expr) { | ||
expr = this.compile(expr); | ||
this._rejects.push(expr); | ||
return this; | ||
}; | ||
/** | ||
* Run through the reject expressions ANDing each | ||
* | ||
* @return {Boolean} | ||
* @api private | ||
*/ | ||
iterator.prototype.rejects = function(node) { | ||
var exprs = this._rejects; | ||
var len = exprs.length; | ||
if (!len) return true; | ||
for (var i = 0; i < len; i++) { | ||
if (exprs[i](node)) return false; | ||
}; | ||
return true; | ||
}; | ||
/** | ||
* Check if node is higher | ||
* than root. | ||
* | ||
* @param {Node} node | ||
* @param {Node} root | ||
* @return {Boolean} | ||
* @api private | ||
*/ | ||
iterator.prototype.higher = function(node) { | ||
var root = this.root; | ||
if (!root) return false; | ||
node = node.parentNode; | ||
while (node && node != root) node = node.parentNode; | ||
return node != root; | ||
}; | ||
/** | ||
* Compile an expression | ||
* | ||
* @param {String|Function|Number} expr | ||
* @return {Function} | ||
*/ | ||
iterator.prototype.compile = function(expr) { | ||
switch (typeof expr) { | ||
case 'number': | ||
return function(node) { return expr == node.nodeType; }; | ||
case 'string': | ||
return new Function('node', 'return ' + props(expr, 'node.')); | ||
case 'function': | ||
return expr; | ||
default: | ||
return function() { return true; }; | ||
} | ||
}; | ||
/** | ||
* Peak in either direction | ||
* `n` nodes. Peak backwards | ||
* using negative numbers. | ||
* | ||
* @param {Number} n (optional) | ||
* @return {Node|null} | ||
* @api public | ||
*/ | ||
iterator.prototype.peak = function(expr, n) { | ||
if (arguments.length == 1) n = expr, expr = true; | ||
n = undefined == n ? 1 : n; | ||
var node; | ||
if (!n) return this.node; | ||
else if (n > 0) while(n--) node = this.next(expr, true); | ||
else while(n++) node = this.prev(expr, true); | ||
this.peaked = node; | ||
return node; | ||
}; | ||
/** | ||
* Add a plugin | ||
* | ||
* @param {Function} fn | ||
* @return {iterator} | ||
* @api public | ||
*/ | ||
iterator.prototype.use = function(fn) { | ||
fn(this); | ||
return this; | ||
}; |
{ | ||
"name": "dom-iterator", | ||
"version": "0.0.5", | ||
"version": "0.1.0", | ||
"description": "iterator for mini-html-parser", | ||
@@ -10,6 +10,12 @@ "main": "index.js", | ||
}, | ||
"dependencies": { | ||
"xor": "component/xor#0.0.2", | ||
"props": "component/props#1.0.3" | ||
}, | ||
"devDependencies": { | ||
"component-test": "~0.1.2" | ||
"mini-html-parser": "0.0.3", | ||
"mocha": "~1.17.1" | ||
}, | ||
"scripts": { | ||
"postinstall": "make npm", | ||
"test": "make test" | ||
@@ -32,2 +38,2 @@ }, | ||
"homepage": "https://github.com/MatthewMueller/dom-iterator" | ||
} | ||
} |
111
Readme.md
# dom-iterator | ||
Iterate over DOM nodes. A better [NodeIterator](https://developer.mozilla.org/en-US/docs/Web/API/NodeIterator). Travels in both directions. | ||
Feature-rich, well-tested Iterator for traversing DOM nodes. A better version of [NodeIterator](https://developer.mozilla.org/en-US/docs/Web/API/NodeIterator). Travels in both directions. | ||
Can be used in node.js with [mini-html-parser](http://github.com/matthewmueller/mini-html-parser). | ||
## Installation | ||
@@ -12,2 +14,6 @@ | ||
With node.js: | ||
$ npm install dom-iterator | ||
## Example | ||
@@ -26,6 +32,12 @@ | ||
### `iterator(node)` | ||
### `iterator(node, root)` | ||
Initialize an iterator starting on the `node`. | ||
Initialize an iterator starting on the `node`. Optionally you can | ||
specify a `root` to limit your traversal to a particular subtree. | ||
`root` must be either a parent or an ancestor of `node`. | ||
```js | ||
var it = iterator(el.firstChild, el) | ||
``` | ||
### `iterator#next()` | ||
@@ -40,2 +52,6 @@ | ||
Here's a look at how the DOM is traversed: | ||
![next](https://i.cloudup.com/kl80e5axNP.png) | ||
### `iterator#prev()`, `iterator#previous()` | ||
@@ -50,29 +66,81 @@ | ||
### `iterator.filter(type)` | ||
Here's a look at how the DOM is traversed: | ||
Only select nodes of `type`. Pass additional arguments for each type. | ||
![prev](https://i.cloudup.com/EkaCyvdwvF.png) | ||
### `iterator.select(expr)` | ||
iterate over nodes that pass the expression `expr`. The `expr` can be an | ||
enum, number, string or function. If it's a number, the `nodeType` is compared. | ||
This function can be chained where all expressions are OR-ed. | ||
```js | ||
// using numbers | ||
it.filter(1, 2) | ||
it.select(Node.ElementNode) | ||
.select(8) | ||
.select('nodeValue == "sloth"') | ||
.select(fn) | ||
``` | ||
// using enums | ||
it.filter(Node.COMMENT_NODE, Node.TEXT_NODE) | ||
This is basically saying, "select all element nodes or comment nodes | ||
or nodes with the nodeValue "sloth" or nodes that pass the function `fn`". | ||
### `iterator.reject(expr)` | ||
iterate over nodes that do not pass the expression `expr`. The `expr` can be an | ||
enum, number, string or function. If it's a number, the `nodeType` is compared. | ||
This function can be chained where all expressions are AND-ed. | ||
```js | ||
it.reject(Node.ElementNode) | ||
.reject(8) | ||
.reject('nodeValue == "sloth"') | ||
.reject(fn) | ||
``` | ||
### `iterator.closing(visit)` | ||
This is basically saying, "reject all element nodes and comment nodes | ||
and nodes with the nodeValue sloth and nodes that pass the function `fn`". | ||
Visit the elements as they close. Defaults to `false` | ||
### `iterator.revisit(revisit)` | ||
You can also skip over elements you already visited, by setting `revisit` to false. By default, `revisit` is set to `true`. | ||
```js | ||
var dom = domify('<em>hi</em>') | ||
var it = it(dom).closing(true); | ||
it.next() "EM" | ||
it.next() "hi" | ||
it.next() "EM" | ||
it.revisit(false); | ||
``` | ||
Here's how that would change the iterator: | ||
**it.next():** | ||
![next](https://i.cloudup.com/VX6BbZEuzf.png) | ||
**it.prev()** | ||
![prev](https://i.cloudup.com/NEKe6F4EUX.png) | ||
### `iterator.opening()` | ||
Jump to the opening tag of an element. This is the default. | ||
```js | ||
var dom = domify('<em>hi</em>'); | ||
var it = it(dom).opening() | ||
it.next() // 'hi' | ||
``` | ||
### `iterator.closing()` | ||
Jump to the closing tag of an element | ||
```js | ||
var dom = domify('<em>hi</em>'); | ||
var it = it(dom).closing() | ||
it.prev() // 'hi' | ||
``` | ||
### `iterator.peak([n])` | ||
Sometimes you want to peak on the following or previous node without actually visiting it. With `peak` you can peak forward or backwards `n` steps. If no `n` is given, peak forward 1 step. Peaking chains until you run `it.next()` or `it.prev()`. | ||
Sometimes you want to peak on the following or previous node without actually visiting it. With `peak` you can peak forward or backwards `n` steps. If no `n` is given, peak forward 1 step. | ||
@@ -92,11 +160,2 @@ Peaking forward: | ||
Chaining: | ||
```js | ||
var node; | ||
while (node = it.peak()) { | ||
// ... | ||
} | ||
``` | ||
### `iterator.reset([newNode])` | ||
@@ -103,0 +162,0 @@ |
@@ -5,6 +5,13 @@ /** | ||
var iterator = require('dom-iterator'); | ||
var domify = require('domify'); | ||
var assert = require('assert'); | ||
try { | ||
var iterator = require('dom-iterator'); | ||
var parse = require('domify'); | ||
} catch (e) { | ||
var iterator = require('../'); | ||
var parse = function(str) { return require('mini-html-parser')(str).parse(); } | ||
} | ||
/** | ||
@@ -15,98 +22,209 @@ * Tests | ||
describe('iterator', function() { | ||
var dom, i; | ||
var dom, i, article; | ||
beforeEach(function() { | ||
dom = domify('<body>hi<article><em>whatever</em>omg<strong></strong></article>bye</body>'); | ||
dom = parse('<body>hi<article><em>whatever</em>omg<strong></strong></article>bye</body>'); | ||
article = dom.childNodes[1]; | ||
}); | ||
describe('elements', function() { | ||
describe('(dom)', function() { | ||
it('should iterate from the top', function() { | ||
i = iterator(dom).filter(Node.ELEMENT_NODE); | ||
verify(i, 'next', ['ARTICLE', 'EM', 'STRONG', null]) | ||
i.reset() | ||
verify(i, 'prev', ['ARTICLE', 'STRONG', 'EM', null]) | ||
i = iterator(dom); | ||
assert('<body>' == format(i)); | ||
assert('hi' == format(i.next(), i)); | ||
assert('<article>' == format(i.next(), i)); | ||
assert('<em>' == format(i.next(), i)); | ||
assert('whatever' == format(i.next(), i)); | ||
assert('</em>' == format(i.next(), i)); | ||
assert('omg' == format(i.next(), i)); | ||
assert('<strong>' == format(i.next(), i)); | ||
assert('</strong>' == format(i.next(), i)); | ||
assert('</article>' == format(i.next(), i)); | ||
assert('bye' == format(i.next(), i)); | ||
assert('</body>' == format(i.next(), i)); | ||
assert(null == i.next()); | ||
assert(null == i.next()); | ||
assert(null == i.next()); | ||
assert('</body>' == format(i)) | ||
assert('bye' == format(i.prev(), i)) | ||
assert('</article>' == format(i.prev(), i)) | ||
}) | ||
it('should iterate from the middle', function() { | ||
i = iterator(dom.querySelector('article')).filter(Node.ELEMENT_NODE); | ||
verify(i, 'next', ['EM', 'STRONG', null]) | ||
i.reset() | ||
verify(i, 'prev', ['STRONG', 'EM', null]) | ||
}) | ||
it('should iterate from the middle (opening)', function() { | ||
i = iterator(article) | ||
it('should iterate from the bottom', function() { | ||
i = iterator(dom.querySelector('em').firstChild).filter(Node.ELEMENT_NODE); | ||
verify(i, 'next', ['STRONG', null]) | ||
i.reset() | ||
verify(i, 'prev', [null]) | ||
}) | ||
}); | ||
assert('<article>' == format(i)) | ||
assert('hi' == format(i.prev(), i)) | ||
assert('<body>' == format(i.prev(), i)) | ||
assert(null == i.prev()); | ||
describe('text nodes', function() { | ||
it('should iterate from the top', function() { | ||
i = iterator(dom).filter(Node.TEXT_NODE); | ||
verify(i, 'next', ['hi', 'whatever', 'omg', 'bye', null]) | ||
i.reset() | ||
verify(i, 'prev', ['bye', 'omg', 'whatever', 'hi', null]) | ||
i.reset(); | ||
assert('<article>' == format(i)) | ||
assert('<em>' == format(i.next(), i)); | ||
assert('whatever' == format(i.next(), i)); | ||
assert('</em>' == format(i.next(), i)); | ||
assert('omg' == format(i.next(), i)); | ||
assert('<strong>' == format(i.next(), i)); | ||
assert('</strong>' == format(i.next(), i)); | ||
assert('</article>' == format(i.next(), i)); | ||
assert('bye' == format(i.next(), i)); | ||
assert('</body>' == format(i.next(), i)); | ||
assert(null == i.next()); | ||
}) | ||
it('should iterate from the middle', function() { | ||
i = iterator(dom.querySelector('article')).filter(Node.TEXT_NODE); | ||
verify(i, 'next', ['whatever', 'omg', 'bye', null]) | ||
i.reset() | ||
verify(i, 'prev', ['omg', 'whatever', 'hi', null]) | ||
it('should iterate from the middle (closing)', function() { | ||
i = iterator(article).closing(); | ||
assert('</article>' == format(i)); | ||
assert('bye' == format(i.next(), i)); | ||
assert('</body>' == format(i.next(), i)); | ||
assert(null == i.next()); | ||
i.reset(); | ||
assert('</article>' == format(i)) | ||
assert('</strong>' == format(i.prev(), i)) | ||
assert('<strong>' == format(i.prev(), i)) | ||
assert('omg' == format(i.prev(), i)) | ||
assert('</em>' == format(i.prev(), i)) | ||
assert('whatever' == format(i.prev(), i)) | ||
assert('<em>' == format(i.prev(), i)) | ||
assert('<article>' == format(i.prev(), i)) | ||
assert('hi' == format(i.prev(), i)) | ||
assert('<body>' == format(i.prev(), i)) | ||
assert(null == i.prev()); | ||
}) | ||
it('should iterate from the bottom', function() { | ||
i = iterator(dom.querySelector('em').firstChild).filter(Node.TEXT_NODE); | ||
verify(i, 'next', ['omg', 'bye', null]) | ||
i.reset() | ||
verify(i, 'prev', ['hi', null]) | ||
i = iterator(dom).closing(); | ||
assert('</body>' == format(i)); | ||
assert('bye' == format(i.prev(), i)) | ||
assert('</article>' == format(i.prev(), i)) | ||
assert('</strong>' == format(i.prev(), i)) | ||
assert('<strong>' == format(i.prev(), i)) | ||
assert('omg' == format(i.prev(), i)) | ||
assert('</em>' == format(i.prev(), i)) | ||
assert('whatever' == format(i.prev(), i)) | ||
assert('<em>' == format(i.prev(), i)) | ||
assert('<article>' == format(i.prev(), i)) | ||
assert('hi' == format(i.prev(), i)) | ||
assert('<body>' == format(i.prev(), i)) | ||
assert(null == i.prev()); | ||
assert(null == i.prev()); | ||
assert(null == i.prev()); | ||
assert('<body>' == format(i)) | ||
assert('hi' == format(i.next(), i)) | ||
assert('<article>' == format(i.next(), i)) | ||
}) | ||
}); | ||
it('should work in both directions', function() { | ||
dom = domify('hi <strong>jimbo</strong>') | ||
i = iterator(dom).filter(Node.TEXT_NODE); | ||
assert('hi ' == i.next().nodeValue); | ||
assert('jimbo' == i.next().nodeValue); | ||
describe('(dom, root)', function() { | ||
it('should support roots to limit iterator (opening)', function() { | ||
i = iterator(article.firstChild, article) | ||
assert('<em>' == format(i)) | ||
assert(null == i.prev()); | ||
assert(null == i.prev()); | ||
assert('<em>' == format(i)) | ||
assert('whatever' == format(i.next(), i)); | ||
assert('</em>' == format(i.next(), i)); | ||
assert('omg' == format(i.next(), i)); | ||
assert('<strong>' == format(i.next(), i)); | ||
assert('</strong>' == format(i.next(), i)); | ||
assert(null == i.next()); | ||
assert('hi ' == i.prev().nodeValue); | ||
assert(null == i.next()); | ||
assert('</strong>' == format(i)); | ||
}) | ||
it('should support roots to limit iterator (closing)', function() { | ||
i = iterator(article.lastChild, article).closing(); | ||
assert('</strong>' == format(i)) | ||
assert(null == i.next()); | ||
assert(null == i.next()); | ||
assert('<strong>' == format(i.prev(), i)) | ||
assert('omg' == format(i.prev(), i)) | ||
assert('</em>' == format(i.prev(), i)) | ||
assert('whatever' == format(i.prev(), i)) | ||
assert('<em>' == format(i.prev(), i)) | ||
assert(null == i.prev()); | ||
assert(null == i.prev()); | ||
assert('<em>' == format(i)) | ||
}) | ||
}); | ||
describe('atOpening() & atClosing()', function() { | ||
it('should accurately return atOpening() or atClosing()', function() { | ||
i = iterator(dom); | ||
assert(i.atOpening()); | ||
i.next() // hi | ||
assert(i.atOpening()); | ||
i.next() // article | ||
assert(i.atOpening()); | ||
i.next() // em | ||
assert(i.atOpening()); | ||
i.next() // whatever | ||
assert(i.atOpening()); | ||
i.next() // /em | ||
assert(i.atClosing()); | ||
i.next() // omg | ||
assert(i.atOpening()); | ||
i.next() // strong | ||
assert(i.atOpening()); | ||
i.next() // /strong | ||
assert(i.atClosing()); | ||
i.next() // /article | ||
assert(i.atClosing()); | ||
i.next() // bye | ||
assert(i.atOpening()); | ||
i.next() // /body | ||
assert(i.atClosing()); | ||
i.next() // /body | ||
assert(i.atClosing()); | ||
i.next() // /body | ||
assert(i.atClosing()); | ||
}) | ||
}) | ||
describe('all nodes', function() { | ||
it('should iterate from the top', function() { | ||
i = iterator(dom); | ||
verify(i, 'next', ['hi', 'ARTICLE', 'EM', 'whatever', 'omg', 'STRONG', 'bye', null]); | ||
i.reset(); | ||
verify(i, 'prev', ['bye', 'ARTICLE', 'STRONG', 'omg', 'EM', 'whatever', 'hi', null]); | ||
describe('revisit(false)', function() { | ||
it('from top: should ignore the element if you pass it again', function() { | ||
i = iterator(dom).revisit(false); | ||
assert('<body>' == format(i)); | ||
assert('hi' == format(i.next(), i)); | ||
assert('<article>' == format(i.next(), i)); | ||
assert('<em>' == format(i.next(), i)); | ||
assert('whatever' == format(i.next(), i)); | ||
assert('omg' == format(i.next(), i)); | ||
assert('<strong>' == format(i.next(), i)); | ||
assert('bye' == format(i.next(), i)); | ||
assert(null == i.next()); | ||
assert(null == i.next()); | ||
assert(null == i.next()); | ||
assert('bye' == format(i)); | ||
assert('</article>' == format(i.prev(), i)); | ||
assert('</strong>' == format(i.prev(), i)); | ||
assert('omg' == format(i.prev(), i)); | ||
}); | ||
it('should iterate from middle', function() { | ||
i = iterator(dom.querySelector('article')); | ||
verify(i, 'next', ['EM', 'whatever', 'omg', 'STRONG', 'bye', null]); | ||
i.reset(); | ||
verify(i, 'prev', ['STRONG', 'omg', 'EM', 'whatever', 'hi', null]); | ||
it('from bottom: should ignore the element if you pass it again', function() { | ||
i = iterator(dom).revisit(false).closing(); | ||
assert('</body>' == format(i)); | ||
assert('bye' == format(i.prev(), i)) | ||
assert('</article>' == format(i.prev(), i)) | ||
assert('</strong>' == format(i.prev(), i)) | ||
assert('omg' == format(i.prev(), i)) | ||
assert('</em>' == format(i.prev(), i)) | ||
assert('whatever' == format(i.prev(), i)) | ||
assert('hi' == format(i.prev(), i)) | ||
assert(null == i.prev()); | ||
assert(null == i.prev()); | ||
assert(null == i.prev()); | ||
assert('hi' == format(i)); | ||
assert('<article>' == format(i.next(), i)); | ||
assert('<em>' == format(i.next(), i)); | ||
assert('whatever' == format(i.next(), i)); | ||
}); | ||
}); | ||
it('should iterate from bottom', function() { | ||
i = iterator(dom.querySelector('em').firstChild); | ||
verify(i, 'next', ['omg', 'STRONG', 'bye', null]) | ||
i.reset(); | ||
verify(i, 'prev', ['hi', null]); | ||
}) | ||
}) | ||
describe('reset', function() { | ||
it('should allow you to pass a new node', function() { | ||
i = iterator(dom.querySelector('em').firstChild); | ||
verify(i, 'next', ['omg', 'STRONG', 'bye', null]) | ||
i.reset(dom); | ||
verify(i, 'next', ['hi', 'ARTICLE', 'EM', 'whatever', 'omg', 'STRONG', 'bye', null]); | ||
}) | ||
}) | ||
describe('peak', function() { | ||
@@ -117,3 +235,3 @@ | ||
assert('hi' == i.peak().nodeValue); | ||
assert('BODY' == i.node.tagName); | ||
assert('BODY' == i.node.nodeName); | ||
assert('hi' == i.next().nodeValue) | ||
@@ -123,6 +241,6 @@ }) | ||
it('should allow you to peak behind', function() { | ||
i = iterator(dom.querySelector('article')); | ||
assert('STRONG' == i.peak(-1).tagName); | ||
assert('ARTICLE' == i.node.tagName); | ||
assert('STRONG' == i.prev().tagName) | ||
i = iterator(article).closing(); | ||
assert('STRONG' == i.peak(-1).nodeName); | ||
assert('ARTICLE' == i.node.nodeName); | ||
assert('STRONG' == i.prev().nodeName) | ||
}) | ||
@@ -132,4 +250,4 @@ | ||
i = iterator(dom); | ||
assert('EM' == i.peak(3).tagName); | ||
assert('BODY' == i.node.tagName); | ||
assert('EM' == i.peak(3).nodeName); | ||
assert('BODY' == i.node.nodeName); | ||
assert('hi' == i.next().nodeValue) | ||
@@ -139,37 +257,134 @@ }) | ||
it('should allow you to peak behind multiple nodes', function() { | ||
i = iterator(dom.querySelector('article')); | ||
assert('EM' == i.peak(-3).tagName); | ||
assert('ARTICLE' == i.node.tagName); | ||
assert('STRONG' == i.prev().tagName) | ||
i = iterator(article).closing(); | ||
assert('omg' == i.peak(-3).nodeValue); | ||
assert('ARTICLE' == i.node.nodeName); | ||
assert('STRONG' == i.prev().nodeName) | ||
}) | ||
}) | ||
it('should support chaining, saving the peak offset', function() { | ||
describe('it.{next,prev}(expr)', function() { | ||
it('should work with numbers', function() { | ||
i = iterator(dom); | ||
assert('hi' == i.peak().nodeValue) | ||
assert('ARTICLE' == i.peak().tagName) | ||
assert('BODY' == i.node.tagName); | ||
assert('hi' == i.next().nodeValue) | ||
assert('hi' == i.peak().nodeValue) | ||
assert('hi' == format(i.next(3), i)); | ||
assert('<article>' == format(i.next(1), i)); | ||
assert('<em>' == format(i.next(1), i)); | ||
assert('whatever' == format(i.next(3), i)); | ||
assert('</em>' == format(i.next(1), i)); | ||
assert('<strong>' == format(i.next(1), i)); | ||
assert('</strong>' == format(i.next(1), i)); | ||
assert('omg' == format(i.prev(3), i)); | ||
assert('whatever' == format(i.prev(3), i)); | ||
assert('hi' == format(i.prev(3), i)); | ||
}); | ||
it('should work with strings', function() { | ||
i = iterator(dom); | ||
assert('omg' == i.next('nodeValue == "omg"').nodeValue); | ||
assert('bye' == i.next('nodeType == 3 && nodeValue == "bye"').nodeValue); | ||
assert('omg' == i.prev('nodeType == 3 && nodeValue == "omg"').nodeValue); | ||
assert('BODY' == i.prev('nodeType == 1 && nodeName == "BODY"').nodeName); | ||
assert(null == i.prev('nodeType == 1 && nodeName == "BODY"')); | ||
assert('<body>' == format(i)) | ||
}) | ||
it('should work with functions', function() { | ||
i = iterator(dom); | ||
assert('omg' == i.next(function(node) { return node.nodeValue == 'omg'}).nodeValue); | ||
assert('BODY' == i.prev(function(node) { return node.nodeName == 'BODY'}).nodeName); | ||
}) | ||
}) | ||
describe('closing(true)', function() { | ||
it('should visit closing', function() { | ||
i = iterator(dom).closing(true); | ||
verify(i, 'next', ['hi', 'ARTICLE', 'EM', 'whatever', 'EM', 'omg', 'STRONG', 'ARTICLE', 'bye', 'BODY', null]) | ||
i.reset(); | ||
verify(i, 'prev', [null]) | ||
}); | ||
describe('it.select(expr)', function() { | ||
it('should work with numbers', function() { | ||
i = iterator(dom) | ||
.select(3) | ||
.select(8); | ||
assert('<body>' == format(i)); | ||
assert('hi' == format(i.next(), i)) | ||
assert('whatever' == format(i.next(), i)) | ||
assert('omg' == format(i.next(), i)) | ||
assert('bye' == format(i.next(), i)) | ||
assert(null == i.next()); | ||
assert('bye' == format(i)); | ||
assert('omg' == format(i.prev(), i)) | ||
assert('whatever' == format(i.prev(), i)) | ||
assert('hi' == format(i.prev(), i)) | ||
assert(null == i.prev()); | ||
assert('hi' == format(i)) | ||
}) | ||
it('should work with strings', function() { | ||
i = iterator(dom) | ||
.select('nodeValue == "omg"') | ||
.select('nodeName == "ARTICLE"'); | ||
assert('<body>' == format(i)); | ||
assert('<article>' == format(i.next(), i)); | ||
assert('omg' == format(i.next(), i)); | ||
assert('</article>' == format(i.next(), i)); | ||
assert(null == i.next()); | ||
assert('</article>' == format(i)); | ||
}) | ||
}); | ||
describe('it.reject(expr)', function() { | ||
it('should work with numbers', function() { | ||
i = iterator(dom) | ||
.reject(1) | ||
.reject(8); | ||
assert('<body>' == format(i)); | ||
assert('hi' == format(i.next(), i)) | ||
assert('whatever' == format(i.next(), i)) | ||
assert('omg' == format(i.next(), i)) | ||
assert('bye' == format(i.next(), i)) | ||
assert(null == i.next()); | ||
assert('bye' == format(i)); | ||
assert('omg' == format(i.prev(), i)) | ||
assert('whatever' == format(i.prev(), i)) | ||
assert('hi' == format(i.prev(), i)) | ||
assert(null == i.prev()); | ||
assert('hi' == format(i)) | ||
}) | ||
function verify(it, dir, expected) { | ||
expected.forEach(function(expect) { | ||
var n = it[dir](); | ||
if (null == expect) return assert(null == n, 'it.' + dir + '() should be null'); | ||
assert(n, 'it.' + dir + '() should not be null. Expected: ' + expect) | ||
var prop = n.nodeType == 1 ? 'nodeName' : 'nodeValue'; | ||
assert(expect == n[prop], 'expected ' + expect + ' got ' + n[prop]); | ||
}); | ||
it('should work with strings', function() { | ||
i = iterator(dom) | ||
.reject('nodeValue == "omg"') | ||
.reject('nodeName == "ARTICLE"'); | ||
assert('<body>' == format(i)); | ||
assert('hi' == format(i.next(), i)); | ||
assert('<em>' == format(i.next(), i)); | ||
assert('whatever' == format(i.next(), i)); | ||
assert('</em>' == format(i.next(), i)); | ||
assert('<strong>' == format(i.next(), i)); | ||
assert('</strong>' == format(i.next(), i)); | ||
assert('bye' == format(i.next(), i)); | ||
assert('</body>' == format(i.next(), i)); | ||
assert(null == i.next()); | ||
assert(null == i.next()); | ||
assert(null == i.next()); | ||
assert('</body>' == format(i)) | ||
assert('bye' == format(i.prev(), i)) | ||
assert('</strong>' == format(i.prev(), i)) | ||
}) | ||
}); | ||
function format(node, it) { | ||
if (arguments.length == 1) it = node, node = it.node; | ||
var name = node.nodeName.toLowerCase(); | ||
var type = node.nodeType; | ||
var closing = it.atClosing(); | ||
var out = null; | ||
if (3 == type) { | ||
out = node.nodeValue; | ||
} else if (1 == type) { | ||
out = it.atClosing() ? '</' + name + '>' : '<' + name + '>'; | ||
} | ||
return out; | ||
} | ||
}) |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
GitHub dependency
Supply chain riskContains a dependency which resolves to a GitHub URL. Dependencies fetched from GitHub specifiers are not immutable can be used to inject untrusted code or reduce the likelihood of a reproducible install.
Found 2 instances in 1 package
Install scripts
Supply chain riskInstall scripts are run when the package is installed. The majority of malware in npm is hidden in install scripts.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
25887
666
178
2
2
3
2
+ Addedprops@component/props#1.0.3
+ Addedxor@component/xor#0.0.2