2013-04-07 22:27:06 -05:00
|
|
|
/*jshint strict:false */
|
2011-11-01 22:02:18 -05:00
|
|
|
|
2012-11-12 01:48:24 -05:00
|
|
|
/*
|
|
|
|
Native TreeWalker is buggy in IE and Opera:
|
|
|
|
* IE9/10 sometimes throw errors when calling TreeWalker#nextNode or
|
|
|
|
TreeWalker#previousNode. No way to feature detect this.
|
|
|
|
* Some versions of Opera have a bug in TreeWalker#previousNode which makes
|
|
|
|
it skip to the wrong node.
|
2013-02-25 02:05:32 -05:00
|
|
|
|
2012-11-12 01:48:24 -05:00
|
|
|
Rather than risk further bugs, it's easiest just to implement our own
|
|
|
|
(subset) of the spec in all browsers.
|
|
|
|
*/
|
|
|
|
|
2013-04-07 22:27:06 -05:00
|
|
|
var typeToBitArray = {
|
|
|
|
// ELEMENT_NODE
|
|
|
|
1: 1,
|
|
|
|
// ATTRIBUTE_NODE
|
|
|
|
2: 2,
|
|
|
|
// TEXT_NODE
|
|
|
|
3: 4,
|
|
|
|
// COMMENT_NODE
|
|
|
|
8: 128,
|
|
|
|
// DOCUMENT_NODE
|
|
|
|
9: 256,
|
|
|
|
// DOCUMENT_FRAGMENT_NODE
|
|
|
|
11: 1024
|
|
|
|
};
|
2012-11-12 01:48:24 -05:00
|
|
|
|
2013-04-07 22:27:06 -05:00
|
|
|
function TreeWalker ( root, nodeType, filter ) {
|
|
|
|
this.root = this.currentNode = root;
|
|
|
|
this.nodeType = nodeType;
|
|
|
|
this.filter = filter;
|
|
|
|
}
|
2012-11-12 01:48:24 -05:00
|
|
|
|
2013-04-07 22:27:06 -05:00
|
|
|
TreeWalker.prototype.nextNode = function () {
|
|
|
|
var current = this.currentNode,
|
|
|
|
root = this.root,
|
|
|
|
nodeType = this.nodeType,
|
|
|
|
filter = this.filter,
|
|
|
|
node;
|
|
|
|
while ( true ) {
|
|
|
|
node = current.firstChild;
|
|
|
|
while ( !node && current ) {
|
|
|
|
if ( current === root ) {
|
|
|
|
break;
|
2012-11-12 01:48:24 -05:00
|
|
|
}
|
2013-04-07 22:27:06 -05:00
|
|
|
node = current.nextSibling;
|
|
|
|
if ( !node ) { current = current.parentNode; }
|
|
|
|
}
|
|
|
|
if ( !node ) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if ( ( typeToBitArray[ node.nodeType ] & nodeType ) &&
|
2014-09-04 05:19:02 -05:00
|
|
|
filter( node ) ) {
|
2013-04-07 22:27:06 -05:00
|
|
|
this.currentNode = node;
|
|
|
|
return node;
|
2011-11-02 20:45:41 -05:00
|
|
|
}
|
2013-04-07 22:27:06 -05:00
|
|
|
current = node;
|
|
|
|
}
|
|
|
|
};
|
2012-11-12 01:48:24 -05:00
|
|
|
|
2013-04-07 22:27:06 -05:00
|
|
|
TreeWalker.prototype.previousNode = function () {
|
|
|
|
var current = this.currentNode,
|
|
|
|
root = this.root,
|
|
|
|
nodeType = this.nodeType,
|
|
|
|
filter = this.filter,
|
|
|
|
node;
|
|
|
|
while ( true ) {
|
|
|
|
if ( current === root ) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
node = current.previousSibling;
|
|
|
|
if ( node ) {
|
|
|
|
while ( current = node.lastChild ) {
|
|
|
|
node = current;
|
2012-11-12 01:48:24 -05:00
|
|
|
}
|
2013-04-07 22:27:06 -05:00
|
|
|
} else {
|
|
|
|
node = current.parentNode;
|
2011-11-01 22:02:18 -05:00
|
|
|
}
|
2013-04-07 22:27:06 -05:00
|
|
|
if ( !node ) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if ( ( typeToBitArray[ node.nodeType ] & nodeType ) &&
|
2014-09-04 05:19:02 -05:00
|
|
|
filter( node ) ) {
|
2013-04-07 22:27:06 -05:00
|
|
|
this.currentNode = node;
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
current = node;
|
|
|
|
}
|
|
|
|
};
|
2015-06-21 21:59:34 -05:00
|
|
|
|
|
|
|
// Previous node in post-order.
|
|
|
|
TreeWalker.prototype.previousPONode = function () {
|
|
|
|
var current = this.currentNode,
|
|
|
|
root = this.root,
|
|
|
|
nodeType = this.nodeType,
|
|
|
|
filter = this.filter,
|
|
|
|
node;
|
|
|
|
while ( true ) {
|
|
|
|
node = current.lastChild;
|
|
|
|
while ( !node && current ) {
|
|
|
|
if ( current === root ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
node = current.previousSibling;
|
|
|
|
if ( !node ) { current = current.parentNode; }
|
|
|
|
}
|
|
|
|
if ( !node ) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if ( ( typeToBitArray[ node.nodeType ] & nodeType ) &&
|
|
|
|
filter( node ) ) {
|
|
|
|
this.currentNode = node;
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
current = node;
|
|
|
|
}
|
|
|
|
};
|