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