diff --git a/HTMLKit/HTMLInputStreamReader.h b/HTMLKit/HTMLInputStreamReader.h
index 1cc77f9..1ffdce8 100644
--- a/HTMLKit/HTMLInputStreamReader.h
+++ b/HTMLKit/HTMLInputStreamReader.h
@@ -39,5 +39,6 @@ typedef void (^ HTMLStreamReaderErrorCallback)(NSString *reason);
- (void)markCurrentLocation;
- (void)rewindToMarkedLocation;
+- (void)reset;
@end
diff --git a/HTMLKit/HTMLInputStreamReader.m b/HTMLKit/HTMLInputStreamReader.m
index e69b83f..72b4644 100644
--- a/HTMLKit/HTMLInputStreamReader.m
+++ b/HTMLKit/HTMLInputStreamReader.m
@@ -225,4 +225,12 @@
_consume = 0;
}
+- (void)reset
+{
+ _mark = 0;
+ _location = 0;
+ _scanner.scanLocation = 0;
+ _consume = 0;
+}
+
@end
diff --git a/HTMLKit/HTMLParser.h b/HTMLKit/HTMLParser.h
index 642f0f0..5f29bfa 100644
--- a/HTMLKit/HTMLParser.h
+++ b/HTMLKit/HTMLParser.h
@@ -11,7 +11,12 @@
@interface HTMLParser : NSObject
+@property (nonatomic, strong, readonly) NSArray *parseErrors;
+@property (nonatomic, strong, readonly) HTMLDocument *document;
-- (instancetype)initWithString:(NSString *)string context:(HTMLElement *)context;
+- (instancetype)initWithString:(NSString *)string;
+
+- (HTMLDocument *)parseDocument;
+- (NSArray *)parseFragmentWithContextElement:(HTMLElement *)contextElement;
@end
diff --git a/HTMLKit/HTMLParser.m b/HTMLKit/HTMLParser.m
index 1106c52..d453490 100644
--- a/HTMLKit/HTMLParser.m
+++ b/HTMLKit/HTMLParser.m
@@ -60,12 +60,14 @@
#pragma mark - Lifecycle
-- (instancetype)initWithString:(NSString *)string context:(HTMLElement *)context
+- (instancetype)initWithString:(NSString *)string
{
self = [super init];
if (self) {
- _contextElement = context;
- _fragmentParsingAlgorithm = (context != nil);
+ _framesetOkFlag = YES;
+ _fragmentParsingAlgorithm = NO;
+ _fosterParenting = NO;
+ _ignoreNextLineFeedCharacterToken = NO;
_errors = [NSMutableArray new];
@@ -83,11 +85,6 @@
_headElementPointer = nil;
_formElementPointer = nil;
-
- _framesetOkFlag = YES;
- _fragmentParsingAlgorithm = NO;
- _fosterParenting = NO;
- _ignoreNextLineFeedCharacterToken = NO;
}
return self;
}
@@ -110,50 +107,83 @@
- (HTMLDocument *)document
{
- if (_document == nil) {
- [self parse];
- }
- return _document;
+ return _document ?: [self parseDocument];
}
#pragma mark - Parse
-- (void)parse
+- (void)initializeDocument
{
- _document = [HTMLDocument new];
- if (_fragmentParsingAlgorithm) {
- if (_contextElement != nil) {
- if ([_contextElement.tagName isEqualToAny:@"title", @"textarea", nil]) {
- _tokenizer.state = HTMLTokenizerStateRCDATA;
- } else if ([_contextElement.tagName isEqualToAny:@"style", @"xmp", @"iframe", @"noembed", @"noframes", nil]) {
- _tokenizer.state = HTMLTokenizerStateRAWTEXT;
- } else if ([_contextElement.tagName isEqualToString:@"script"]) {
- _tokenizer.state = HTMLTokenizerStateScriptData;
- } else if ([_contextElement.tagName isEqualToString:@"noscript"]) {
- _tokenizer.state = HTMLTokenizerStateRAWTEXT;
- } else if ([_contextElement.tagName isEqualToString:@"plaintext"]) {
- _tokenizer.state = HTMLTokenizerStatePLAINTEXT;
- } else {
- _tokenizer.state = HTMLTokenizerStateData;
- }
- }
+ if (_document == nil) {
+ _document = [HTMLDocument new];
+ }
+ _document.quirksMode = HTMLQuirksModeNoQuirks;
+ _document.documentType = nil;
+ [_document removeAllChildNodes];
- HTMLElement *root = [[HTMLElement alloc] initWithTagName:@"html"];
- [_document appendNode:root];
- [_stackOfOpenElements pushElement:root];
+ _fragmentParsingAlgorithm = NO;
+}
- if ([_contextElement.tagName isEqualToString:@"template"]) {
-#warning Implement HTML Template
- }
+- (HTMLDocument *)parseDocument
+{
+ [self initializeDocument];
+ [self runParser];
+ return _document;
+}
- [self resetInsertionModeAppropriately];
-
- _formElementPointer = _contextElement;
- while (_formElementPointer != nil && ![_formElementPointer.tagName isEqualToString:@"form"]) {
- _formElementPointer = _formElementPointer.parentElement;
- }
+- (NSArray *)parseFragmentWithContextElement:(HTMLElement *)contextElement
+{
+ if (contextElement == nil) {
+ return nil;
}
+ if ([_contextElement isEqualTo:contextElement]) {
+ return [_document childNodeAtIndex:0].childNodes.array;
+ }
+
+ [self initializeDocument];
+ [_tokenizer reset];
+ _contextElement = contextElement;
+ _fragmentParsingAlgorithm = YES;
+
+ _document.quirksMode = _contextElement.ownerDocument ? _contextElement.ownerDocument.quirksMode : HTMLQuirksModeNoQuirks;
+
+ if ([_contextElement.tagName isEqualToAny:@"title", @"textarea", nil]) {
+ _tokenizer.state = HTMLTokenizerStateRCDATA;
+ } else if ([_contextElement.tagName isEqualToAny:@"style", @"xmp", @"iframe", @"noembed", @"noframes", nil]) {
+ _tokenizer.state = HTMLTokenizerStateRAWTEXT;
+ } else if ([_contextElement.tagName isEqualToString:@"script"]) {
+ _tokenizer.state = HTMLTokenizerStateScriptData;
+ } else if ([_contextElement.tagName isEqualToString:@"noscript"]) {
+ _tokenizer.state = HTMLTokenizerStateRAWTEXT;
+ } else if ([_contextElement.tagName isEqualToString:@"plaintext"]) {
+ _tokenizer.state = HTMLTokenizerStatePLAINTEXT;
+ } else {
+ _tokenizer.state = HTMLTokenizerStateData;
+ }
+
+ HTMLElement *root = [[HTMLElement alloc] initWithTagName:@"html"];
+ [_document appendNode:root];
+ [_stackOfOpenElements pushElement:root];
+
+ if ([_contextElement.tagName isEqualToString:@"template"]) {
+#warning Implement HTML Template
+ }
+
+ [self resetInsertionModeAppropriately];
+
+ _formElementPointer = _contextElement;
+ while (_formElementPointer != nil && ![_formElementPointer.tagName isEqualToString:@"form"]) {
+ _formElementPointer = _formElementPointer.parentElement;
+ }
+
+ [self runParser];
+
+ return root.childNodes.array;
+}
+
+- (void)runParser
+{
for (HTMLToken *token in _tokenizer) {
if (_document.readyState == HTMLDocumentComplete) {
break;
diff --git a/HTMLKit/HTMLTokenizer.h b/HTMLKit/HTMLTokenizer.h
index 359404d..15ce13c 100644
--- a/HTMLKit/HTMLTokenizer.h
+++ b/HTMLKit/HTMLTokenizer.h
@@ -24,4 +24,6 @@
- (instancetype)initWithString:(NSString *)string;
+- (void)reset;
+
@end
diff --git a/HTMLKit/HTMLTokenizer.m b/HTMLKit/HTMLTokenizer.m
index bc7ac3f..d8f3cd9 100644
--- a/HTMLKit/HTMLTokenizer.m
+++ b/HTMLKit/HTMLTokenizer.m
@@ -83,6 +83,14 @@
#pragma mark - State Machine
+- (void)reset
+{
+ _currentState = HTMLTokenizerStateData;
+ [_tokens removeAllObjects];
+ [_inputStreamReader reset];
+ _eof = NO;
+}
+
- (id)nextObject
{
while (_eof == NO && _tokens.count == 0) {