Add implementation for the Tree Walker
https://dom.spec.whatwg.org/#interface-treewalker
This commit is contained in:
@@ -71,6 +71,9 @@
|
||||
6247171A1B22333200C11912 /* HTMLNodeTraversal.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717171B22333200C11912 /* HTMLNodeTraversal.m */; };
|
||||
624717B81B21FE5400B38302 /* HTMLNodeFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */; };
|
||||
624717B91B21FE5400B38302 /* HTMLNodeFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */; };
|
||||
624717BC1B22009200B38302 /* HTMLTreeWalker.h in Headers */ = {isa = PBXBuildFile; fileRef = 624717BA1B22009200B38302 /* HTMLTreeWalker.h */; };
|
||||
624717BD1B22009200B38302 /* HTMLTreeWalker.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717BB1B22009200B38302 /* HTMLTreeWalker.m */; };
|
||||
624717BE1B22009200B38302 /* HTMLTreeWalker.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717BB1B22009200B38302 /* HTMLTreeWalker.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 */; };
|
||||
@@ -219,6 +222,8 @@
|
||||
624717161B22333200C11912 /* HTMLNodeTraversal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeTraversal.h; sourceTree = "<group>"; };
|
||||
624717171B22333200C11912 /* HTMLNodeTraversal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeTraversal.m; sourceTree = "<group>"; };
|
||||
624717B71B21FE5400B38302 /* HTMLNodeFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeFilter.m; sourceTree = "<group>"; };
|
||||
624717BA1B22009200B38302 /* HTMLTreeWalker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLTreeWalker.h; sourceTree = "<group>"; };
|
||||
624717BB1B22009200B38302 /* HTMLTreeWalker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLTreeWalker.m; sourceTree = "<group>"; };
|
||||
6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeIterator.h; sourceTree = "<group>"; };
|
||||
6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeIterator.m; sourceTree = "<group>"; };
|
||||
6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeFilter.h; sourceTree = "<group>"; };
|
||||
@@ -345,6 +350,8 @@
|
||||
623406E01ADB04F9004677A3 /* HTMLTemplate.m */,
|
||||
6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */,
|
||||
6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */,
|
||||
624717BA1B22009200B38302 /* HTMLTreeWalker.h */,
|
||||
624717BB1B22009200B38302 /* HTMLTreeWalker.m */,
|
||||
624717161B22333200C11912 /* HTMLNodeTraversal.h */,
|
||||
624717171B22333200C11912 /* HTMLNodeTraversal.m */,
|
||||
6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */,
|
||||
@@ -596,6 +603,7 @@
|
||||
623857961A9E8606003A45D9 /* HTMLDocumentType.h in Headers */,
|
||||
623406E61ADB05AD004677A3 /* HTMLDocumentFragment.h in Headers */,
|
||||
62363C3D1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h in Headers */,
|
||||
624717BC1B22009200B38302 /* HTMLTreeWalker.h in Headers */,
|
||||
62362A3C1A9FA70400301989 /* HTMLText.h in Headers */,
|
||||
6234584E1A9D2FA4009BD491 /* HTMLNode.h in Headers */,
|
||||
);
|
||||
@@ -755,6 +763,7 @@
|
||||
624493AE19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */,
|
||||
6235CEA21AA5170A0026937B /* HTMLMarker.m in Sources */,
|
||||
6234248A1AB467B200726190 /* HTMLOrderedDictionary.m in Sources */,
|
||||
624717BE1B22009200B38302 /* HTMLTreeWalker.m in Sources */,
|
||||
62AE594119F9907C0043F069 /* HTMLTagToken.m in Sources */,
|
||||
62AE594619F992F30043F069 /* HTMLCommentToken.m in Sources */,
|
||||
62363C3F1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */,
|
||||
@@ -809,6 +818,7 @@
|
||||
624493AD19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */,
|
||||
6235CEA11AA5170A0026937B /* HTMLMarker.m in Sources */,
|
||||
623424891AB467B200726190 /* HTMLOrderedDictionary.m in Sources */,
|
||||
624717BD1B22009200B38302 /* HTMLTreeWalker.m in Sources */,
|
||||
62AE594019F9907C0043F069 /* HTMLTagToken.m in Sources */,
|
||||
62AE594519F992F30043F069 /* HTMLCommentToken.m in Sources */,
|
||||
62363C3E1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */,
|
||||
|
||||
@@ -13,4 +13,5 @@
|
||||
|
||||
extern HTMLNode * PrecedingNode(HTMLNode *node, HTMLNode *root);
|
||||
extern HTMLNode * FollowingNode(HTMLNode *node, HTMLNode *root);
|
||||
extern HTMLNode * FollowingNodeSkippingChildren(HTMLNode *node, HTMLNode *root);
|
||||
extern BOOL FilterNode(id<HTMLNodeFilter> filter, HTMLNodeFilterShowOptions whatToShow, HTMLNode *node);
|
||||
|
||||
@@ -46,7 +46,22 @@ HTMLNode * FollowingNode(HTMLNode *node, HTMLNode *root)
|
||||
return nil;
|
||||
}
|
||||
|
||||
extern BOOL FilterNode(id<HTMLNodeFilter> filter, HTMLNodeFilterShowOptions whatToShow, HTMLNode *node)
|
||||
HTMLNode * FollowingNodeSkippingChildren(HTMLNode *node, HTMLNode *root)
|
||||
{
|
||||
do {
|
||||
if (node == root) {
|
||||
return nil;
|
||||
}
|
||||
if (node.nextSibling != nil) {
|
||||
return node.nextSibling;
|
||||
}
|
||||
node = node.parentNode;
|
||||
} while (node != nil);
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
BOOL FilterNode(id<HTMLNodeFilter> filter, HTMLNodeFilterShowOptions whatToShow, HTMLNode *node)
|
||||
{
|
||||
unsigned long nthBit = (1 << (node.nodeType - 1)) & whatToShow;
|
||||
if (!nthBit) {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// HTMLTreeWalker.h
|
||||
// HTMLKit
|
||||
//
|
||||
// Created by Iska on 05/06/15.
|
||||
// Copyright (c) 2015 BrainCookie. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "HTMLNodeFilter.h"
|
||||
|
||||
@class HTMLNode;
|
||||
|
||||
@interface HTMLTreeWalker : NSObject
|
||||
|
||||
@property (nonatomic, strong, readonly) HTMLNode *root;
|
||||
@property (nonatomic, assign, readonly) HTMLNodeFilterShowOptions whatToShow;
|
||||
@property (nonatomic, strong, readonly) id<HTMLNodeFilter> filter;
|
||||
@property (nonatomic, strong) HTMLNode *currentNode;
|
||||
|
||||
- (HTMLNode *)parentNode;
|
||||
- (HTMLNode *)firstChild;
|
||||
- (HTMLNode *)lastChild;
|
||||
- (HTMLNode *)previousSibling;
|
||||
- (HTMLNode *)nextSibling;
|
||||
- (HTMLNode *)previousNode;
|
||||
- (HTMLNode *)nextNode;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,222 @@
|
||||
//
|
||||
// HTMLTreeWalker.m
|
||||
// HTMLKit
|
||||
//
|
||||
// Created by Iska on 05/06/15.
|
||||
// Copyright (c) 2015 BrainCookie. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HTMLTreeWalker.h"
|
||||
#import "HTMLNode.h"
|
||||
#import "HTMLNodeFilter.h"
|
||||
#import "HTMLNodeTraversal.h"
|
||||
|
||||
typedef NS_ENUM(short, HTMLTreeWalkerChildrenType)
|
||||
{
|
||||
HTMLTreeWalkerChildrenTypeFirst,
|
||||
HTMLTreeWalkerChildrenTypeLast
|
||||
};
|
||||
|
||||
typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType)
|
||||
{
|
||||
HTMLTreeWalkerSiblingsTypeNext,
|
||||
HTMLTreeWalkerSiblingsTypePrevious
|
||||
};
|
||||
|
||||
@implementation HTMLTreeWalker
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (instancetype)initWithNode:(HTMLNode *)node
|
||||
showOptions:(HTMLNodeFilterShowOptions)showOptions
|
||||
filter:(id<HTMLNodeFilter>)filter
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_root = node;
|
||||
_filter = filter;
|
||||
_whatToShow = showOptions;
|
||||
_currentNode = _root;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Traversal
|
||||
|
||||
- (HTMLNode *)traverseChildrenOfType:(HTMLTreeWalkerChildrenType)type
|
||||
{
|
||||
HTMLNode *node = _currentNode;
|
||||
|
||||
node = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.firstChiledNode : node.lastChildNode;
|
||||
|
||||
while (node != nil) {
|
||||
BOOL result = FilterNode(self.filter, self.whatToShow, node);
|
||||
if (result) {
|
||||
_currentNode = node;
|
||||
return node;
|
||||
}
|
||||
|
||||
HTMLNode *child = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.firstChiledNode : node.lastChildNode;
|
||||
if (child != nil) {
|
||||
node = child;
|
||||
continue;
|
||||
}
|
||||
|
||||
while (node != nil) {
|
||||
HTMLNode *sibling = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.nextSibling : node.previousSibling;
|
||||
if (sibling != nil) {
|
||||
node = sibling;
|
||||
continue;
|
||||
}
|
||||
|
||||
HTMLNode *parent = node.parentNode;
|
||||
if (parent == nil || parent == _root || parent == _currentNode) {
|
||||
return nil;
|
||||
}
|
||||
node = parent;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (HTMLNode *)parentNode
|
||||
{
|
||||
HTMLNode *node = _currentNode;
|
||||
|
||||
while (node != nil && node != _root) {
|
||||
node = node.parentNode;
|
||||
if (node != nil && FilterNode(self.filter, self.whatToShow, node)) {
|
||||
_currentNode = node;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (HTMLNode *)firstChild
|
||||
{
|
||||
return [self traverseChildrenOfType:HTMLTreeWalkerChildrenTypeFirst];
|
||||
}
|
||||
|
||||
- (HTMLNode *)lastChild
|
||||
{
|
||||
return [self traverseChildrenOfType:HTMLTreeWalkerChildrenTypeLast];
|
||||
}
|
||||
|
||||
- (HTMLNode *)traverseSiblingsOfType:(HTMLTreeWalkerSiblingsType)type
|
||||
{
|
||||
HTMLNode *node = _currentNode;
|
||||
|
||||
if (node == _root) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
while (YES) {
|
||||
HTMLNode *sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.nextSibling : node.previousSibling;
|
||||
while (sibling != nil) {
|
||||
node = sibling;
|
||||
BOOL result = FilterNode(self.filter, self.whatToShow, node);
|
||||
if (result) {
|
||||
_currentNode = node;
|
||||
return node;
|
||||
}
|
||||
|
||||
HTMLNode *sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.firstChiledNode : node.lastChildNode;
|
||||
if (sibling == nil) {
|
||||
sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.nextSibling : node.previousSibling;
|
||||
}
|
||||
}
|
||||
|
||||
node = node.parentNode;
|
||||
if (node == nil || node == _root) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (FilterNode(self.filter, self.whatToShow, node)) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (HTMLNode *)previousSibling
|
||||
{
|
||||
return [self traverseSiblingsOfType:HTMLTreeWalkerSiblingsTypePrevious];
|
||||
}
|
||||
|
||||
- (HTMLNode *)nextSibling
|
||||
{
|
||||
return [self traverseSiblingsOfType:HTMLTreeWalkerSiblingsTypeNext];
|
||||
}
|
||||
|
||||
- (HTMLNode *)previousNode
|
||||
{
|
||||
HTMLNode *node = _currentNode;
|
||||
|
||||
while (node != _root) {
|
||||
HTMLNode *sibling = node.previousSibling;
|
||||
|
||||
while (sibling != nil) {
|
||||
node = sibling;
|
||||
|
||||
BOOL result = FilterNode(self.filter, self.whatToShow, node);
|
||||
while (result && node.hasChildNodes) {
|
||||
node = node.lastChildNode;
|
||||
result = FilterNode(self.filter, self.whatToShow, node);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
_currentNode = node;
|
||||
return node;
|
||||
}
|
||||
|
||||
sibling = node.previousSibling;
|
||||
}
|
||||
|
||||
if (node == _root || node.parentNode == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
node = node.parentNode;
|
||||
if (FilterNode(self.filter, self.whatToShow, node)) {
|
||||
_currentNode = node;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (HTMLNode *)nextNode
|
||||
{
|
||||
HTMLNode *node = _currentNode;
|
||||
|
||||
BOOL result = YES;
|
||||
|
||||
while (YES) {
|
||||
while (!result && node.hasChildNodes) {
|
||||
node = node.firstChiledNode;
|
||||
result = FilterNode(self.filter, self.whatToShow, node);
|
||||
if (result) {
|
||||
_currentNode = node;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
HTMLNode *nextSibling;
|
||||
while ((nextSibling = FollowingNodeSkippingChildren(node, _root)) != nil) {
|
||||
node = nextSibling;
|
||||
result = FilterNode(self.filter, self.whatToShow, node);
|
||||
if (result) {
|
||||
_currentNode = node;
|
||||
return node;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user