diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj
index 13906ec..12727ea 100644
--- a/HTMLKit.xcodeproj/project.pbxproj
+++ b/HTMLKit.xcodeproj/project.pbxproj
@@ -13,6 +13,9 @@
6235CE9B1AA509430026937B /* NSString+HTMLKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 6235CE991AA509430026937B /* NSString+HTMLKit.h */; };
6235CE9C1AA509430026937B /* NSString+HTMLKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 6235CE9A1AA509430026937B /* NSString+HTMLKit.m */; };
6235CE9D1AA509430026937B /* NSString+HTMLKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 6235CE9A1AA509430026937B /* NSString+HTMLKit.m */; };
+ 6235CEA01AA5170A0026937B /* HTMLMarker.h in Headers */ = {isa = PBXBuildFile; fileRef = 6235CE9E1AA5170A0026937B /* HTMLMarker.h */; };
+ 6235CEA11AA5170A0026937B /* HTMLMarker.m in Sources */ = {isa = PBXBuildFile; fileRef = 6235CE9F1AA5170A0026937B /* HTMLMarker.m */; };
+ 6235CEA21AA5170A0026937B /* HTMLMarker.m in Sources */ = {isa = PBXBuildFile; fileRef = 6235CE9F1AA5170A0026937B /* HTMLMarker.m */; };
62362A3C1A9FA70400301989 /* HTMLText.h in Headers */ = {isa = PBXBuildFile; fileRef = 62362A3A1A9FA70400301989 /* HTMLText.h */; };
62362A3D1A9FA70400301989 /* HTMLText.m in Sources */ = {isa = PBXBuildFile; fileRef = 62362A3B1A9FA70400301989 /* HTMLText.m */; };
62362A3E1A9FA70400301989 /* HTMLText.m in Sources */ = {isa = PBXBuildFile; fileRef = 62362A3B1A9FA70400301989 /* HTMLText.m */; };
@@ -121,6 +124,8 @@
6234584D1A9D2FA4009BD491 /* HTMLNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNode.m; sourceTree = ""; };
6235CE991AA509430026937B /* NSString+HTMLKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+HTMLKit.h"; sourceTree = ""; };
6235CE9A1AA509430026937B /* NSString+HTMLKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+HTMLKit.m"; sourceTree = ""; };
+ 6235CE9E1AA5170A0026937B /* HTMLMarker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLMarker.h; sourceTree = ""; };
+ 6235CE9F1AA5170A0026937B /* HTMLMarker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLMarker.m; sourceTree = ""; };
62362A3A1A9FA70400301989 /* HTMLText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLText.h; sourceTree = ""; };
62362A3B1A9FA70400301989 /* HTMLText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLText.m; sourceTree = ""; };
62362A3F1A9FDE8A00301989 /* HTMLNodes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLNodes.h; sourceTree = ""; };
@@ -298,6 +303,8 @@
628B7CE61A080E1000602C87 /* HTMLNamespaces.h */,
6223211D1A969B9300BACED5 /* HTMLElementTypes.h */,
6279F87119E17DC700F12EE5 /* HTMLParserInsertionModes.h */,
+ 6235CE9E1AA5170A0026937B /* HTMLMarker.h */,
+ 6235CE9F1AA5170A0026937B /* HTMLMarker.m */,
6235CE991AA509430026937B /* NSString+HTMLKit.h */,
6235CE9A1AA509430026937B /* NSString+HTMLKit.m */,
623719441AA1472B002E03C8 /* Nodes */,
@@ -382,6 +389,7 @@
624AC8FF19FBF59800BD3C4A /* HTMLTokens.h in Headers */,
62AE594419F992F30043F069 /* HTMLCommentToken.h in Headers */,
624493AC19CD0CBE00BCDDF4 /* HTMLToken.h in Headers */,
+ 6235CEA01AA5170A0026937B /* HTMLMarker.h in Headers */,
62F31FDD19E9DCCF007F0657 /* HTMLTokenizerEntities.h in Headers */,
6279F87419E1808D00F12EE5 /* HTMLElement.h in Headers */,
62AE593F19F9907C0043F069 /* HTMLTagToken.h in Headers */,
@@ -549,6 +557,7 @@
6235CE9D1AA509430026937B /* NSString+HTMLKit.m in Sources */,
6238579D1A9E8934003A45D9 /* HTMLComment.m in Sources */,
624493AE19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */,
+ 6235CEA21AA5170A0026937B /* HTMLMarker.m in Sources */,
62AE594119F9907C0043F069 /* HTMLTagToken.m in Sources */,
62AE594619F992F30043F069 /* HTMLCommentToken.m in Sources */,
62AE593C19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */,
@@ -584,6 +593,7 @@
6235CE9C1AA509430026937B /* NSString+HTMLKit.m in Sources */,
6238579C1A9E8934003A45D9 /* HTMLComment.m in Sources */,
624493AD19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */,
+ 6235CEA11AA5170A0026937B /* HTMLMarker.m in Sources */,
62AE594019F9907C0043F069 /* HTMLTagToken.m in Sources */,
62AE594519F992F30043F069 /* HTMLCommentToken.m in Sources */,
62AE593B19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */,
diff --git a/HTMLKit/HTMLElement.h b/HTMLKit/HTMLElement.h
index 24ba656..ea0f15e 100644
--- a/HTMLKit/HTMLElement.h
+++ b/HTMLKit/HTMLElement.h
@@ -16,6 +16,8 @@
@property (nonatomic, copy, readonly) NSString *tagName;
+@property (nonatomic, strong) NSMutableDictionary *attributes;
+
@property (nonatomic, copy) NSString *id;
@property (nonatomic, copy) NSString *className;
diff --git a/HTMLKit/HTMLMarker.h b/HTMLKit/HTMLMarker.h
new file mode 100644
index 0000000..1aa2ef8
--- /dev/null
+++ b/HTMLKit/HTMLMarker.h
@@ -0,0 +1,15 @@
+//
+// HTMLMarker.h
+// HTMLKit
+//
+// Created by Iska on 02/03/15.
+// Copyright (c) 2015 BrainCookie. All rights reserved.
+//
+
+#import
+
+@interface HTMLMarker : NSObject
+
++ (instancetype)marker;
+
+@end
diff --git a/HTMLKit/HTMLMarker.m b/HTMLKit/HTMLMarker.m
new file mode 100644
index 0000000..c76a655
--- /dev/null
+++ b/HTMLKit/HTMLMarker.m
@@ -0,0 +1,23 @@
+//
+// HTMLMarker.m
+// HTMLKit
+//
+// Created by Iska on 02/03/15.
+// Copyright (c) 2015 BrainCookie. All rights reserved.
+//
+
+#import "HTMLMarker.h"
+
+@implementation HTMLMarker
+
++ (instancetype)marker
+{
+ static dispatch_once_t onceToken;
+ static HTMLMarker *singleton = nil;
+ dispatch_once(&onceToken, ^{
+ singleton = [[self alloc] init];
+ });
+ return singleton;
+}
+
+@end
diff --git a/HTMLKit/HTMLParser.m b/HTMLKit/HTMLParser.m
index 15bf02f..3f70bae 100644
--- a/HTMLKit/HTMLParser.m
+++ b/HTMLKit/HTMLParser.m
@@ -12,6 +12,7 @@
#import "HTMLParserInsertionModes.h"
#import "HTMLNodes.h"
#import "HTMLElementTypes.h"
+#import "HTMLMarker.h"
#import "NSString+HTMLKit.h"
@interface HTMLParser ()
@@ -25,6 +26,7 @@
HTMLInsertionMode _originalInsertionMode;
NSMutableArray *_stackOfOpenElements;
+ NSMutableArray *_listOfActiveFormattingElements;
HTMLDocument *_document;
@@ -56,6 +58,7 @@
[self setupStateMachine];
_stackOfOpenElements = [NSMutableArray new];
+ _listOfActiveFormattingElements = [NSMutableArray new];
_tokenizer = [[HTMLTokenizer alloc] initWithString:string];
_scriptingFlag = NO;
@@ -424,6 +427,59 @@
[self switchInsertionMode:HTMLInsertionModeText];
}
+- (void)reconstructActiveFormattingElements
+{
+ if (_listOfActiveFormattingElements.count == 0) {
+ return;
+ }
+
+ id last = _listOfActiveFormattingElements.lastObject;
+ if (last == [HTMLMarker marker] || [_stackOfOpenElements containsObject:last]) {
+ return;
+ }
+
+ __block NSUInteger index = _listOfActiveFormattingElements.count - 1;
+ __block id entry = _listOfActiveFormattingElements[index];
+
+ // Reconstruct the active formatting elements
+ // https://html.spec.whatwg.org/multipage/syntax.html#reconstruct-the-active-formatting-elements
+ // No cycles ~> blocks instead of gotos
+
+ dispatch_block_t advance = ^{
+ entry = _listOfActiveFormattingElements[index++];
+ };
+
+ dispatch_block_t create = ^{
+ HTMLElement *entry = _listOfActiveFormattingElements[index];
+ HTMLStartTagToken *token = [[HTMLStartTagToken alloc] initWithTagName:entry.tagName
+ attributes:entry.attributes];
+ HTMLElement *element = [self insertElementForToken:token];
+ [_listOfActiveFormattingElements replaceObjectAtIndex:index withObject:element];
+ if (index++ != _listOfActiveFormattingElements.count) {
+ advance();
+ }
+ };
+
+ dispatch_block_t rewind = ^{
+ if (index == 0) {
+ create();
+ }
+ entry = _listOfActiveFormattingElements[index--];
+ if (entry != [HTMLMarker marker] && ![_stackOfOpenElements containsObject:entry]) {
+ rewind();
+ }
+ };
+
+ rewind();
+}
+
+- (void)clearListOfActiveFormattingElementsUptoLastMarker
+{
+ while (_listOfActiveFormattingElements.lastObject != [HTMLMarker marker]) {
+ [_listOfActiveFormattingElements removeLastObject];
+ }
+}
+
#pragma mark - Insertion Modes
- (void)HTMLInsertionModeInitial:(HTMLToken *)token
diff --git a/HTMLKit/HTMLTagToken.h b/HTMLKit/HTMLTagToken.h
index ee04aa2..7b9987f 100644
--- a/HTMLKit/HTMLTagToken.h
+++ b/HTMLKit/HTMLTagToken.h
@@ -17,6 +17,7 @@
@property (nonatomic, assign, getter = isSelfClosing) BOOL selfClosing;
- (instancetype)initWithTagName:(NSString *)tagName;
+- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSMutableDictionary *)attributes;
- (void)appendStringToTagName:(NSString *)string;
diff --git a/HTMLKit/HTMLTagToken.m b/HTMLKit/HTMLTagToken.m
index 2526991..39eee74 100644
--- a/HTMLKit/HTMLTagToken.m
+++ b/HTMLKit/HTMLTagToken.m
@@ -21,10 +21,19 @@
@synthesize tagName = _tagName;
- (instancetype)initWithTagName:(NSString *)tagName
+{
+ return [self initWithTagName:tagName attributes:nil];
+}
+
+- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSMutableDictionary *)attributes
{
self = [super init];
if (self) {
_tagName = [tagName mutableCopy];
+ _attributes = [NSMutableDictionary new];
+ if (attributes != nil) {
+ [_attributes addEntriesFromDictionary:attributes];
+ }
}
return self;
}
@@ -44,7 +53,12 @@
- (instancetype)initWithTagName:(NSString *)tagName
{
- self = [super initWithTagName:tagName];
+ return [self initWithTagName:tagName attributes:nil];
+}
+
+- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSMutableDictionary *)attributes
+{
+ self = [super initWithTagName:tagName attributes:attributes];
if (self) {
self.type = HTMLTokenTypeStartTag;
}
@@ -80,7 +94,12 @@
- (instancetype)initWithTagName:(NSString *)tagName
{
- self = [super initWithTagName:tagName];
+ return [self initWithTagName:tagName attributes:nil];
+}
+
+- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSMutableDictionary *)attributes
+{
+ self = [super initWithTagName:tagName attributes:attributes];
if (self) {
self.type = HTMLTokenTypeEndTag;
}