diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj
index d906780..88ae430 100644
--- a/HTMLKit.xcodeproj/project.pbxproj
+++ b/HTMLKit.xcodeproj/project.pbxproj
@@ -69,6 +69,9 @@
624493AC19CD0CBE00BCDDF4 /* HTMLToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493AA19CD0CBE00BCDDF4 /* HTMLToken.h */; };
624493AD19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */; };
624493AE19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */; };
+ 6247A9431B152F4F00CCF25C /* HTMLNodeIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */; };
+ 6247A9441B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */; };
+ 6247A9451B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */; };
6247A9471B152F8C00CCF25C /* HTMLNodeFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */; };
624AC8FF19FBF59800BD3C4A /* HTMLTokens.h in Headers */ = {isa = PBXBuildFile; fileRef = 624AC8FE19FBF4F700BD3C4A /* HTMLTokens.h */; };
624AC90119FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AC90019FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m */; };
@@ -213,6 +216,8 @@
624493A919CCE84A00BCDDF4 /* HTMLTokenizerStates.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLTokenizerStates.h; sourceTree = ""; };
624493AA19CD0CBE00BCDDF4 /* HTMLToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLToken.h; sourceTree = ""; };
624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLToken.m; sourceTree = ""; };
+ 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeIterator.h; sourceTree = ""; };
+ 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeIterator.m; sourceTree = ""; };
6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeFilter.h; sourceTree = ""; };
624AC8FE19FBF4F700BD3C4A /* HTMLTokens.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLTokens.h; sourceTree = ""; };
624AC90019FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitTokenizerTests.m; sourceTree = ""; };
@@ -335,6 +340,8 @@
62362A3B1A9FA70400301989 /* HTMLText.m */,
623406DF1ADB04F9004677A3 /* HTMLTemplate.h */,
623406E01ADB04F9004677A3 /* HTMLTemplate.m */,
+ 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */,
+ 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */,
6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */,
623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */,
623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */,
@@ -568,6 +575,7 @@
62AE593A19F97E1C0043F069 /* HTMLDOCTYPEToken.h in Headers */,
6247A9471B152F8C00CCF25C /* HTMLNodeFilter.h in Headers */,
623424881AB467B200726190 /* HTMLOrderedDictionary.h in Headers */,
+ 6247A9431B152F4F00CCF25C /* HTMLNodeIterator.h in Headers */,
625A150419C783EB00AD0C32 /* HTMLKit.h in Headers */,
625A150819C78ABA00AD0C32 /* HTMLInputStreamReader.h in Headers */,
6238579B1A9E8934003A45D9 /* HTMLComment.h in Headers */,
@@ -729,6 +737,7 @@
files = (
62362A3E1A9FA70400301989 /* HTMLText.m in Sources */,
6235CE9D1AA509430026937B /* NSString+HTMLKit.m in Sources */,
+ 6247A9451B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */,
6238579D1A9E8934003A45D9 /* HTMLComment.m in Sources */,
6234BEF01AABBF1400DEB15F /* HTMLStackOfOpenElements.m in Sources */,
624493AE19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */,
@@ -781,6 +790,7 @@
files = (
62362A3D1A9FA70400301989 /* HTMLText.m in Sources */,
6235CE9C1AA509430026937B /* NSString+HTMLKit.m in Sources */,
+ 6247A9441B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */,
6238579C1A9E8934003A45D9 /* HTMLComment.m in Sources */,
6234BEEF1AABBF1400DEB15F /* HTMLStackOfOpenElements.m in Sources */,
624493AD19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */,
diff --git a/HTMLKit/HTMLNodeFilter.h b/HTMLKit/HTMLNodeFilter.h
index 2f302f2..31cbdfa 100644
--- a/HTMLKit/HTMLNodeFilter.h
+++ b/HTMLKit/HTMLNodeFilter.h
@@ -13,19 +13,19 @@ typedef NS_ENUM(unsigned short, HTMLNodeFilterValue)
HTMLNodeFilterAccept = 1,
HTMLNodeFilterReject = 2,
HTMLNodeFilterSkip = 3
-} HTMLNodeFilterValue;
+};
typedef NS_ENUM(unsigned long, HTMLNodeFilterShowOptions)
{
- HTMLNodeFilterShowAll = 0xFFFFFFFF;
- HTMLNodeFilterShowElement = 0x1;
- HTMLNodeFilterShowText = 0x4;
- HTMLNodeFilterShowProcessingInstruction = 0x40;
- HTMLNodeFilterShowComment = 0x80;
- HTMLNodeFilterShowDocument = 0x100;
- HTMLNodeFilterShowDocumentType = 0x200;
- HTMLNodeFilterShowDocumentFragment = 0x400;
-} HTMLNodeFilterShowOptions;
+ HTMLNodeFilterShowAll = 0xFFFFFFFF,
+ HTMLNodeFilterShowElement = 0x1,
+ HTMLNodeFilterShowText = 0x4,
+ HTMLNodeFilterShowProcessingInstruction = 0x40,
+ HTMLNodeFilterShowComment = 0x80,
+ HTMLNodeFilterShowDocument = 0x100,
+ HTMLNodeFilterShowDocumentType = 0x200,
+ HTMLNodeFilterShowDocumentFragment = 0x400
+};
@class HTMLNode;
diff --git a/HTMLKit/HTMLNodeIterator.h b/HTMLKit/HTMLNodeIterator.h
new file mode 100644
index 0000000..f082809
--- /dev/null
+++ b/HTMLKit/HTMLNodeIterator.h
@@ -0,0 +1,27 @@
+//
+// HTMLNodeIterator.h
+// HTMLKit
+//
+// Created by Iska on 27/05/15.
+// Copyright (c) 2015 BrainCookie. All rights reserved.
+//
+
+#import
+#import "HTMLNodeFilter.h"
+
+@class HTMLNode;
+
+@interface HTMLNodeIterator : NSEnumerator
+
+@property (nonatomic, strong, readonly) HTMLNode *root;
+@property (nonatomic, strong, readonly) HTMLNode *referenceNode;
+@property (nonatomic, assign, readonly) BOOL pointerBeforeReferenceNode;
+@property (nonatomic, assign, readonly) HTMLNodeFilterShowOptions whatToShow;
+@property (nonatomic, strong, readonly) id filter;
+
+- (instancetype)initWithNode:(HTMLNode *)node;
+
+- (HTMLNode *)nextNode;
+- (HTMLNode *)previousNode;
+
+@end
diff --git a/HTMLKit/HTMLNodeIterator.m b/HTMLKit/HTMLNodeIterator.m
new file mode 100644
index 0000000..1439bdb
--- /dev/null
+++ b/HTMLKit/HTMLNodeIterator.m
@@ -0,0 +1,131 @@
+//
+// HTMLNodeIterator.m
+// HTMLKit
+//
+// Created by Iska on 27/05/15.
+// Copyright (c) 2015 BrainCookie. All rights reserved.
+//
+
+#import "HTMLNodeIterator.h"
+#import "HTMLNode.h"
+
+typedef NS_ENUM(short, TraverseDirection)
+{
+ TraverseDirectionNext,
+ TraverseDirectionPrevious
+};
+
+@interface HTMLNodeIterator ()
+{
+ HTMLNode *_root;
+}
+@end
+
+@implementation HTMLNodeIterator
+
+- (instancetype)initWithNode:(HTMLNode *)node
+{
+ self = [super init];
+ if (self) {
+ _root = node;
+ _whatToShow = HTMLNodeFilterShowAll;
+ }
+ return self;
+}
+
+- (HTMLNode *)traverseInDirection:(TraverseDirection)direction
+{
+ HTMLNode *node = self.referenceNode;
+ BOOL beforeNode = self.pointerBeforeReferenceNode;
+
+ do {
+ if (direction == TraverseDirectionNext) {
+ if (!beforeNode) {
+ node = FollowingNode(node, self.root);
+ if (node == nil) {
+ return nil;
+ }
+ beforeNode = NO;
+ }
+ } else {
+ if (beforeNode) {
+ node = PrecedingNode(node, self.root);
+ if (node == nil) {
+ return nil;
+ }
+ beforeNode = YES;
+ }
+ }
+ } while (FilterNode(self, node) != HTMLNodeFilterAccept);
+
+ _referenceNode = node;
+ _pointerBeforeReferenceNode = beforeNode;
+ return node;
+}
+
+NS_INLINE HTMLNode * PrecedingNode(HTMLNode *node, HTMLNode *root)
+{
+ HTMLNode *previous = node.previousSibling;
+ if (previous != nil) {
+ while (node.lastChildNode != nil) {
+ node = node.lastChildNode;
+ }
+ return node;
+ }
+
+ if (node == root) {
+ return nil;
+ }
+
+ return node.parentNode;
+}
+
+NS_INLINE HTMLNode * FollowingNode(HTMLNode *node, HTMLNode *root)
+{
+ if (node.firstChiledNode != nil) {
+ return node.firstChiledNode;
+ }
+
+ do {
+ if (node == root) {
+ return nil;
+ }
+ if (node.nextSibling != nil) {
+ return node.nextSibling;
+ }
+ node = node.parentNode;
+ } while (node != nil);
+
+ return nil;
+}
+
+NS_INLINE HTMLNodeFilterValue FilterNode(HTMLNodeIterator *iterator, HTMLNode *node)
+{
+ unsigned long nthBit = (1 << (node.nodeType - 1)) & iterator.whatToShow;
+ if (!nthBit) {
+ return HTMLNodeFilterSkip;
+ }
+
+ if (iterator.filter == nil) {
+ return HTMLNodeFilterAccept;
+ }
+
+ return [iterator.filter acceptNode:node];
+}
+
+- (id)nextObject
+{
+ return self.nextNode;
+}
+
+- (HTMLNode *)nextNode
+{
+ return [self traverseInDirection:TraverseDirectionNext];
+}
+
+- (HTMLNode *)previousNode
+{
+ return [self traverseInDirection:TraverseDirectionPrevious];
+}
+
+@end