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