diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index 41c9a71..1f786ab 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -7,6 +7,12 @@ objects = { /* Begin PBXBuildFile section */ + 623406E11ADB04F9004677A3 /* HTMLTemplate.h in Headers */ = {isa = PBXBuildFile; fileRef = 623406DF1ADB04F9004677A3 /* HTMLTemplate.h */; }; + 623406E21ADB04F9004677A3 /* HTMLTemplate.m in Sources */ = {isa = PBXBuildFile; fileRef = 623406E01ADB04F9004677A3 /* HTMLTemplate.m */; }; + 623406E31ADB04F9004677A3 /* HTMLTemplate.m in Sources */ = {isa = PBXBuildFile; fileRef = 623406E01ADB04F9004677A3 /* HTMLTemplate.m */; }; + 623406E61ADB05AD004677A3 /* HTMLDocumentFragment.h in Headers */ = {isa = PBXBuildFile; fileRef = 623406E41ADB05AD004677A3 /* HTMLDocumentFragment.h */; }; + 623406E71ADB05AD004677A3 /* HTMLDocumentFragment.m in Sources */ = {isa = PBXBuildFile; fileRef = 623406E51ADB05AD004677A3 /* HTMLDocumentFragment.m */; }; + 623406E81ADB05AD004677A3 /* HTMLDocumentFragment.m in Sources */ = {isa = PBXBuildFile; fileRef = 623406E51ADB05AD004677A3 /* HTMLDocumentFragment.m */; }; 623424881AB467B200726190 /* HTMLOrderedDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 623424861AB467B200726190 /* HTMLOrderedDictionary.h */; }; 623424891AB467B200726190 /* HTMLOrderedDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 623424871AB467B200726190 /* HTMLOrderedDictionary.m */; }; 6234248A1AB467B200726190 /* HTMLOrderedDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 623424871AB467B200726190 /* HTMLOrderedDictionary.m */; }; @@ -146,6 +152,10 @@ /* Begin PBXFileReference section */ 6223211D1A969B9300BACED5 /* HTMLElementTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLElementTypes.h; sourceTree = ""; }; + 623406DF1ADB04F9004677A3 /* HTMLTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLTemplate.h; sourceTree = ""; }; + 623406E01ADB04F9004677A3 /* HTMLTemplate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLTemplate.m; sourceTree = ""; }; + 623406E41ADB05AD004677A3 /* HTMLDocumentFragment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLDocumentFragment.h; sourceTree = ""; }; + 623406E51ADB05AD004677A3 /* HTMLDocumentFragment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLDocumentFragment.m; sourceTree = ""; }; 623424861AB467B200726190 /* HTMLOrderedDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLOrderedDictionary.h; sourceTree = ""; }; 623424871AB467B200726190 /* HTMLOrderedDictionary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLOrderedDictionary.m; sourceTree = ""; }; 6234584C1A9D2FA4009BD491 /* HTMLNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNode.h; sourceTree = ""; }; @@ -300,6 +310,10 @@ 6238579A1A9E8934003A45D9 /* HTMLComment.m */, 62362A3A1A9FA70400301989 /* HTMLText.h */, 62362A3B1A9FA70400301989 /* HTMLText.m */, + 623406DF1ADB04F9004677A3 /* HTMLTemplate.h */, + 623406E01ADB04F9004677A3 /* HTMLTemplate.m */, + 623406E41ADB05AD004677A3 /* HTMLDocumentFragment.h */, + 623406E51ADB05AD004677A3 /* HTMLDocumentFragment.m */, 623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */, 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */, ); @@ -492,6 +506,7 @@ 624493AC19CD0CBE00BCDDF4 /* HTMLToken.h in Headers */, 6235CEA01AA5170A0026937B /* HTMLMarker.h in Headers */, 62F31FDD19E9DCCF007F0657 /* HTMLTokenizerEntities.h in Headers */, + 623406E11ADB04F9004677A3 /* HTMLTemplate.h in Headers */, 6234BEEE1AABBF1400DEB15F /* HTMLStackOfOpenElements.h in Headers */, 6238C9851AB8D6330006512E /* HTMLKitExceptions.h in Headers */, 6238039F1AB63A8C008A53D0 /* HTMLEOFToken.h in Headers */, @@ -507,6 +522,7 @@ 62AE593519F97D880043F069 /* HTMLParseErrorToken.h in Headers */, 62AE594919F9948A0043F069 /* HTMLCharacterToken.h in Headers */, 623857961A9E8606003A45D9 /* HTMLDocumentType.h in Headers */, + 623406E61ADB05AD004677A3 /* HTMLDocumentFragment.h in Headers */, 62363C3D1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h in Headers */, 62362A3C1A9FA70400301989 /* HTMLText.h in Headers */, 6234584E1A9D2FA4009BD491 /* HTMLNode.h in Headers */, @@ -671,12 +687,14 @@ 62363C3F1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */, 623916C51AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */, 62AE593C19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */, + 623406E31ADB04F9004677A3 /* HTMLTemplate.m in Sources */, 6238C9871AB8D6330006512E /* HTMLKitExceptions.m in Sources */, 62F873ED19E088C90062683C /* HTMLParser.m in Sources */, 623803A11AB63A8C008A53D0 /* HTMLEOFToken.m in Sources */, 6279F87619E1808D00F12EE5 /* HTMLElement.m in Sources */, 625A150A19C78ABA00AD0C32 /* HTMLInputStreamReader.m in Sources */, 623857931A9E772B003A45D9 /* HTMLDocument.m in Sources */, + 623406E81ADB05AD004677A3 /* HTMLDocumentFragment.m in Sources */, 624493A819CCC54100BCDDF4 /* HTMLTokenizer.m in Sources */, 625A14BE19C7829400AD0C32 /* HTMLKit.m in Sources */, 623857981A9E8606003A45D9 /* HTMLDocumentType.m in Sources */, @@ -717,12 +735,14 @@ 62363C3E1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */, 623916C41AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */, 62AE593B19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */, + 623406E21ADB04F9004677A3 /* HTMLTemplate.m in Sources */, 6238C9861AB8D6330006512E /* HTMLKitExceptions.m in Sources */, 62F873EC19E088C90062683C /* HTMLParser.m in Sources */, 623803A01AB63A8C008A53D0 /* HTMLEOFToken.m in Sources */, 6279F87519E1808D00F12EE5 /* HTMLElement.m in Sources */, 625A150919C78ABA00AD0C32 /* HTMLInputStreamReader.m in Sources */, 623857921A9E772B003A45D9 /* HTMLDocument.m in Sources */, + 623406E71ADB05AD004677A3 /* HTMLDocumentFragment.m in Sources */, 624493A719CCC54100BCDDF4 /* HTMLTokenizer.m in Sources */, 625A150219C783DE00AD0C32 /* HTMLKit.m in Sources */, 623857971A9E8606003A45D9 /* HTMLDocumentType.m in Sources */, diff --git a/HTMLKit/HTMLDocument.h b/HTMLKit/HTMLDocument.h index 1fb38fd..ddcec2b 100644 --- a/HTMLKit/HTMLDocument.h +++ b/HTMLKit/HTMLDocument.h @@ -29,4 +29,6 @@ typedef NS_ENUM(short, HTMLDocumentReadyState) - (HTMLNode *)adoptNode:(HTMLNode *)node; +- (HTMLDocument *)associatedInertTemplateDocument; + @end diff --git a/HTMLKit/HTMLDocument.m b/HTMLKit/HTMLDocument.m index 2bd8ed6..11e2a42 100644 --- a/HTMLKit/HTMLDocument.m +++ b/HTMLKit/HTMLDocument.m @@ -10,15 +10,21 @@ #import "HTMLKitExceptions.h" @interface HTMLNode (Private) +@property (nonatomic, weak) HTMLDocument *ownerDocument; @property (nonatomic, weak) HTMLNode *parentNode; @end @interface HTMLDocument () +{ + HTMLDocument *_inertTemplateDocument; +} @property (nonatomic, assign) HTMLDocumentReadyState readyState; @end @implementation HTMLDocument +#pragma mark - Init + - (instancetype)init { self = [super initWithName:@"#document" type:HTMLNodeDocument]; @@ -28,6 +34,13 @@ return self; } +#pragma mark - Accessors + +- (void)setOwnerDocument:(HTMLDocument *)ownerDocument +{ + [self doesNotRecognizeSelector:_cmd]; +} + - (void)setDocumentType:(HTMLDocumentType *)documentType { if (documentType == nil) { @@ -44,6 +57,8 @@ } } +#pragma mark - Mutation Algorithms + - (HTMLNode *)adoptNode:(HTMLNode *)node { if (node == nil) { @@ -56,9 +71,22 @@ } [node.parentNode removeChildNode:node]; + node.ownerDocument = self; return node; } +#pragma mark - Template + +- (HTMLDocument *)associatedInertTemplateDocument +{ + if (_inertTemplateDocument == nil) { + _inertTemplateDocument = [HTMLDocument new]; + _inertTemplateDocument.readyState = HTMLDocumentComplete; + } + + return _inertTemplateDocument; +} + #pragma mark - Description - (id)debugQuickLookObject diff --git a/HTMLKit/HTMLDocumentFragment.h b/HTMLKit/HTMLDocumentFragment.h new file mode 100644 index 0000000..270c573 --- /dev/null +++ b/HTMLKit/HTMLDocumentFragment.h @@ -0,0 +1,15 @@ +// +// HTMLDocumentFragment.h +// HTMLKit +// +// Created by Iska on 12/04/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLNode.h" + +@interface HTMLDocumentFragment : HTMLNode + +- (instancetype)initWithDocument:(HTMLDocument *)document; + +@end diff --git a/HTMLKit/HTMLDocumentFragment.m b/HTMLKit/HTMLDocumentFragment.m new file mode 100644 index 0000000..fc03c9a --- /dev/null +++ b/HTMLKit/HTMLDocumentFragment.m @@ -0,0 +1,44 @@ +// +// HTMLDocumentFragment.m +// HTMLKit +// +// Created by Iska on 12/04/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLDocumentFragment.h" +#import "HTMLText.h" + +@interface HTMLNode () +@property (nonatomic, weak) HTMLDocument *ownerDocument; +@end + +@implementation HTMLDocumentFragment + +- (instancetype)initWithDocument:(HTMLDocument *)document +{ + self = [super initWithName:@"#document-fragment" type:HTMLNodeDocumentFragment]; + if (self) { + self.ownerDocument = document; + } + return self; +} + +- (NSString *)textContent +{ + NSMutableString *content = [NSMutableString string]; + for (HTMLNode *node in self.treeEnumerator) { + if (node.type == HTMLNodeText) { + [content appendString:[(HTMLText *)node data]]; + } + } + return content; +} + +- (void)setTextContent:(NSString *)textContent +{ + HTMLText *node = [[HTMLText alloc] initWithData:textContent]; + [self replaceAllChildNodesWithNode:node]; +} + +@end diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index 475aa21..368a196 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -19,9 +19,11 @@ { NSMutableOrderedSet *_childNodes; } +@property (nonatomic, weak) HTMLDocument *ownerDocument; @end @implementation HTMLNode +@synthesize ownerDocument = _ownerDocument; #pragma mark - Init @@ -42,17 +44,21 @@ { if (_type == HTMLNodeDocument) { return (HTMLDocument *)self; - } else if (_parentNode == nil) { - return nil; } else { - return _parentNode.ownerDocument; + return _ownerDocument; } } +- (void)setOwnerDocument:(HTMLDocument *)ownerDocument +{ + _ownerDocument = ownerDocument; + [self.childNodes.array makeObjectsPerformSelector:@selector(setOwnerDocument:) withObject:ownerDocument]; +} + - (void)setBaseURI:(NSString *)baseURI { _baseURI = [baseURI copy]; - [_childNodes.array makeObjectsPerformSelector:@selector(setBaseURI:) withObject:baseURI]; + [self.childNodes.array makeObjectsPerformSelector:@selector(setBaseURI:) withObject:baseURI]; } - (void)setParentNode:(HTMLNode *)parentNode @@ -67,12 +73,12 @@ - (HTMLNode *)firstChiledNode { - return _childNodes.firstObject; + return self.childNodes.firstObject; } - (HTMLNode *)lastChildNode { - return _childNodes.lastObject; + return self.childNodes.lastObject; } - (HTMLNode *)previousSibling @@ -102,12 +108,12 @@ - (BOOL)hasChildNodes { - return _childNodes.count > 0; + return self.childNodes.count > 0; } - (BOOL)hasChildNodeOfType:(HTMLNodeType)type { - NSUInteger index = [_childNodes indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { + NSUInteger index = [self.childNodes indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { if ([(HTMLNode *)obj type] == type) { *stop = YES; return YES; @@ -120,17 +126,17 @@ - (NSUInteger)childNodesCount { - return _childNodes.count; + return self.childNodes.count; } - (HTMLNode *)childNodeAtIndex:(NSUInteger)index { - return [_childNodes objectAtIndex:index]; + return [self.childNodes objectAtIndex:index]; } - (NSUInteger)indexOfChildNode:(HTMLNode *)node { - return [_childNodes indexOfObject:node]; + return [self.childNodes indexOfObject:node]; } - (HTMLNode *)insertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child @@ -161,7 +167,7 @@ [self.ownerDocument adoptNode:node]; NSUInteger index = [self indexOfChildNode:child]; node.parentNode = self; - [_childNodes replaceObjectAtIndex:index withObject:node]; + [(NSMutableOrderedSet *)self.childNodes replaceObjectAtIndex:index withObject:node]; return child; } @@ -183,7 +189,7 @@ NSStringFromSelector(_cmd), child]; } - [_childNodes removeObject:child]; + [(NSMutableOrderedSet *)self.childNodes removeObject:child]; child.parentNode = nil; return child; } @@ -196,7 +202,7 @@ - (void)reparentChildNodesIntoNode:(HTMLNode *)node { - for (HTMLNode *child in _childNodes.array) { + for (HTMLNode *child in self.childNodes.array) { [node appendNode:child]; } [self removeAllChildNodes]; @@ -204,7 +210,7 @@ - (void)removeAllChildNodes { - [_childNodes removeAllObjects]; + [(NSMutableOrderedSet *)self.childNodes removeAllObjects]; } #pragma mark - Enumeration @@ -215,7 +221,7 @@ return; } - [_childNodes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + [self.childNodes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { block(obj, idx, stop); }]; } @@ -238,9 +244,9 @@ [self.ownerDocument adoptNode:node]; NSUInteger index = [self indexOfChildNode:child]; if (index != NSNotFound) { - [_childNodes insertObject:node atIndex:index]; + [(NSMutableOrderedSet *)self.childNodes insertObject:node atIndex:index]; } else { - [_childNodes addObject:node]; + [(NSMutableOrderedSet *)self.childNodes addObject:node]; } return node; @@ -249,6 +255,7 @@ NS_INLINE void CheckParentValid(HTMLNode *node, NSString *cmd) { if (node.type != HTMLNodeDocument && + node.type != HTMLNodeDocumentFragment && node.type != HTMLNodeElement) { [NSException raise:HTMLKitHierarchyRequestError format:@"%@: Hierarchy Request Error, inserting into %@ is not allowed. The operation would yield an incorrect node tree.", @@ -268,8 +275,8 @@ NS_INLINE void CheckChildsParent(HTMLNode *parent, HTMLNode *child, NSString *cm NS_INLINE void CheckInsertedNodeValid(HTMLNode *node, NSString *cmd) { - if (node.type != HTMLNodeDocumentType && - node.type != HTMLNodeElement && + if (node.type != HTMLNodeDocumentFragment && + node.type != HTMLNodeDocumentType && node.type != HTMLNodeElement && node.type != HTMLNodeText && node.type != HTMLNodeComment) { @@ -312,6 +319,18 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin if (self.type == HTMLNodeDocument) { switch (node.type) { + case HTMLNodeDocumentFragment: + if (self.childNodesCount > 1 || + [self hasChildNodeOfType:HTMLNodeText]) { + hierarchyError(); + } else if (self.childNodesCount == 1) { + if (self.hasChildNodes || + child.type == HTMLNodeDocumentType || + child.nextSibling.type == HTMLNodeDocumentType) { + hierarchyError(); + } + } + break; case HTMLNodeElement: if ([self hasChildNodeOfType:HTMLNodeElement] || child.type == HTMLNodeDocumentType || @@ -332,7 +351,7 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin } } -- (void)ensureReplacementValidityOfChildNode:(HTMLNode *)node withNode:(HTMLNode *)child +- (void)ensureReplacementValidityOfChildNode:(HTMLNode *)child withNode:(HTMLNode *)node { CheckParentValid(self, NSStringFromSelector(_cmd)); @@ -350,6 +369,17 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin if (self.type == HTMLNodeDocument) { switch (node.type) { + case HTMLNodeDocumentFragment: + if (self.childNodesCount > 1 || + [self hasChildNodeOfType:HTMLNodeText]) { + hierarchyError(); + } else if (self.childNodesCount == 1) { + if (self.firstChiledNode != node || + child.nextSibling.type == HTMLNodeDocumentType) { + hierarchyError(); + } + } + break; case HTMLNodeElement: { if (child.nextSibling.type == HTMLNodeDocumentType) { @@ -400,7 +430,7 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin - (NSString *)innerHTML { - return [[_childNodes.array valueForKey:@"outerHTML"] componentsJoinedByString:@""]; + return [[self.childNodes.array valueForKey:@"outerHTML"] componentsJoinedByString:@""]; } #pragma mark - Description diff --git a/HTMLKit/HTMLNodes.h b/HTMLKit/HTMLNodes.h index e2bb0ff..f0ffa0e 100644 --- a/HTMLKit/HTMLNodes.h +++ b/HTMLKit/HTMLNodes.h @@ -12,3 +12,5 @@ #import "HTMLElement.h" #import "HTMLComment.h" #import "HTMLText.h" +#import "HTMLTemplate.h" +#import "HTMLDocumentFragment.h" diff --git a/HTMLKit/HTMLTemplate.h b/HTMLKit/HTMLTemplate.h new file mode 100644 index 0000000..454e903 --- /dev/null +++ b/HTMLKit/HTMLTemplate.h @@ -0,0 +1,16 @@ +// +// HTMLTemplate.h +// HTMLKit +// +// Created by Iska on 12/04/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLElement.h" +#import "HTMLDocumentFragment.h" + +@interface HTMLTemplate : HTMLElement + +@property (nonatomic, strong) HTMLDocumentFragment *content; + +@end diff --git a/HTMLKit/HTMLTemplate.m b/HTMLKit/HTMLTemplate.m new file mode 100644 index 0000000..57835dc --- /dev/null +++ b/HTMLKit/HTMLTemplate.m @@ -0,0 +1,34 @@ +// +// HTMLTemplate.m +// HTMLKit +// +// Created by Iska on 12/04/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLTemplate.h" +#import "HTMLDocument.h" + +@implementation HTMLTemplate + +- (instancetype)init +{ + self = [super initWithTagName:@"template"]; + return self; +} + +- (HTMLDocumentFragment *)content +{ + if (_content == nil) { + _content = [[HTMLDocumentFragment alloc] initWithDocument:self.ownerDocument.associatedInertTemplateDocument]; + } + + return _content; +} + +- (NSOrderedSet *)childNodes +{ + return self.content.childNodes; +} + +@end