diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ad1d028 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "HTMLKitTests/html5lib-tests"] + path = HTMLKitTests/html5lib-tests + url = https://github.com/html5lib/html5lib-tests.git diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj new file mode 100644 index 0000000..1a94f56 --- /dev/null +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -0,0 +1,1102 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + 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 */; }; + 6234584E1A9D2FA4009BD491 /* HTMLNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 6234584C1A9D2FA4009BD491 /* HTMLNode.h */; }; + 6234584F1A9D2FA4009BD491 /* HTMLNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 6234584D1A9D2FA4009BD491 /* HTMLNode.m */; }; + 623458501A9D2FA4009BD491 /* HTMLNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 6234584D1A9D2FA4009BD491 /* HTMLNode.m */; }; + 6234BEEE1AABBF1400DEB15F /* HTMLStackOfOpenElements.h in Headers */ = {isa = PBXBuildFile; fileRef = 6234BEEC1AABBF1400DEB15F /* HTMLStackOfOpenElements.h */; }; + 6234BEEF1AABBF1400DEB15F /* HTMLStackOfOpenElements.m in Sources */ = {isa = PBXBuildFile; fileRef = 6234BEED1AABBF1400DEB15F /* HTMLStackOfOpenElements.m */; }; + 6234BEF01AABBF1400DEB15F /* HTMLStackOfOpenElements.m in Sources */ = {isa = PBXBuildFile; fileRef = 6234BEED1AABBF1400DEB15F /* HTMLStackOfOpenElements.m */; }; + 6234CF571ABF892400B6077D /* libHTMLKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14DE19C7834000AD0C32 /* libHTMLKit.a */; }; + 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 */; }; + 62363C3D1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h in Headers */ = {isa = PBXBuildFile; fileRef = 62363C3B1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h */; }; + 62363C3E1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */ = {isa = PBXBuildFile; fileRef = 62363C3C1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m */; }; + 62363C3F1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */ = {isa = PBXBuildFile; fileRef = 62363C3C1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m */; }; + 6236738E1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 6236738D1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m */; }; + 6236738F1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 6236738D1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m */; }; + 6238039F1AB63A8C008A53D0 /* HTMLEOFToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 6238039D1AB63A8C008A53D0 /* HTMLEOFToken.h */; }; + 623803A01AB63A8C008A53D0 /* HTMLEOFToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238039E1AB63A8C008A53D0 /* HTMLEOFToken.m */; }; + 623803A11AB63A8C008A53D0 /* HTMLEOFToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238039E1AB63A8C008A53D0 /* HTMLEOFToken.m */; }; + 623857911A9E772B003A45D9 /* HTMLDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = 6238578F1A9E772B003A45D9 /* HTMLDocument.h */; }; + 623857921A9E772B003A45D9 /* HTMLDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 623857901A9E772B003A45D9 /* HTMLDocument.m */; }; + 623857931A9E772B003A45D9 /* HTMLDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 623857901A9E772B003A45D9 /* HTMLDocument.m */; }; + 623857961A9E8606003A45D9 /* HTMLDocumentType.h in Headers */ = {isa = PBXBuildFile; fileRef = 623857941A9E8606003A45D9 /* HTMLDocumentType.h */; }; + 623857971A9E8606003A45D9 /* HTMLDocumentType.m in Sources */ = {isa = PBXBuildFile; fileRef = 623857951A9E8606003A45D9 /* HTMLDocumentType.m */; }; + 623857981A9E8606003A45D9 /* HTMLDocumentType.m in Sources */ = {isa = PBXBuildFile; fileRef = 623857951A9E8606003A45D9 /* HTMLDocumentType.m */; }; + 6238579B1A9E8934003A45D9 /* HTMLComment.h in Headers */ = {isa = PBXBuildFile; fileRef = 623857991A9E8934003A45D9 /* HTMLComment.h */; }; + 6238579C1A9E8934003A45D9 /* HTMLComment.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238579A1A9E8934003A45D9 /* HTMLComment.m */; }; + 6238579D1A9E8934003A45D9 /* HTMLComment.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238579A1A9E8934003A45D9 /* HTMLComment.m */; }; + 6238C9851AB8D6330006512E /* HTMLKitExceptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 6238C9831AB8D6330006512E /* HTMLKitExceptions.h */; }; + 6238C9861AB8D6330006512E /* HTMLKitExceptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238C9841AB8D6330006512E /* HTMLKitExceptions.m */; }; + 6238C9871AB8D6330006512E /* HTMLKitExceptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238C9841AB8D6330006512E /* HTMLKitExceptions.m */; }; + 623916C31AC707250066B4FE /* HTMLNodeTreeEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */; }; + 623916C41AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */; }; + 623916C51AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */; }; + 623916C71AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */; }; + 623916C81AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */; }; + 6239755A1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623975591AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m */; }; + 6239755B1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623975591AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m */; }; + 6239755E1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6239755D1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m */; }; + 6239755F1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6239755D1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m */; }; + 623CAF9D1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 623CAF9C1AD88BEA00E34C32 /* HTMLKitParserPerformance.m */; }; + 623CAF9E1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 623CAF9C1AD88BEA00E34C32 /* HTMLKitParserPerformance.m */; }; + 624493A619CCC54100BCDDF4 /* HTMLTokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493A419CCC54100BCDDF4 /* HTMLTokenizer.h */; }; + 624493A719CCC54100BCDDF4 /* HTMLTokenizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493A519CCC54100BCDDF4 /* HTMLTokenizer.m */; }; + 624493A819CCC54100BCDDF4 /* HTMLTokenizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493A519CCC54100BCDDF4 /* HTMLTokenizer.m */; }; + 624493AC19CD0CBE00BCDDF4 /* HTMLToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493AA19CD0CBE00BCDDF4 /* HTMLToken.h */; }; + 624493AD19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */; }; + 624493AE19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */; }; + 624AC8FF19FBF59800BD3C4A /* HTMLTokens.h in Headers */ = {isa = PBXBuildFile; fileRef = 624AC8FE19FBF4F700BD3C4A /* HTMLTokens.h */; }; + 624AC90119FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AC90019FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m */; }; + 624AC90219FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AC90019FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m */; }; + 624AC90519FBFE9A00BD3C4A /* html5lib-tests in Resources */ = {isa = PBXBuildFile; fileRef = 624AC90419FBFE8A00BD3C4A /* html5lib-tests */; }; + 624AC90619FBFEA000BD3C4A /* html5lib-tests in Resources */ = {isa = PBXBuildFile; fileRef = 624AC90419FBFE8A00BD3C4A /* html5lib-tests */; }; + 624AC90E19FC702E00BD3C4A /* HTML Standard.html in Resources */ = {isa = PBXBuildFile; fileRef = 624AC90D19FC702E00BD3C4A /* HTML Standard.html */; }; + 624AC90F19FC702E00BD3C4A /* HTML Standard.html in Resources */ = {isa = PBXBuildFile; fileRef = 624AC90D19FC702E00BD3C4A /* HTML Standard.html */; }; + 624B9FB21AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB11AE0313300646C4C /* HTMLKitStringCategoryTests.m */; }; + 624B9FB31AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB11AE0313300646C4C /* HTMLKitStringCategoryTests.m */; }; + 624B9FB51AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */; }; + 624B9FB61AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */; }; + 625A14B019C7829400AD0C32 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14AF19C7829400AD0C32 /* Cocoa.framework */; }; + 625A14BA19C7829400AD0C32 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 625A14B819C7829400AD0C32 /* InfoPlist.strings */; }; + 625A14BE19C7829400AD0C32 /* HTMLKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 625A14BD19C7829400AD0C32 /* HTMLKit.m */; }; + 625A14C519C7829400AD0C32 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14C419C7829400AD0C32 /* XCTest.framework */; }; + 625A14C619C7829400AD0C32 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14AF19C7829400AD0C32 /* Cocoa.framework */; }; + 625A14C919C7829400AD0C32 /* HTMLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14AC19C7829400AD0C32 /* HTMLKit.framework */; }; + 625A14CF19C7829400AD0C32 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 625A14CD19C7829400AD0C32 /* InfoPlist.strings */; }; + 625A14E019C7834100AD0C32 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14DF19C7834100AD0C32 /* Foundation.framework */; }; + 625A14ED19C7834100AD0C32 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14C419C7829400AD0C32 /* XCTest.framework */; }; + 625A14EE19C7834100AD0C32 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14DF19C7834100AD0C32 /* Foundation.framework */; }; + 625A150219C783DE00AD0C32 /* HTMLKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 625A14BD19C7829400AD0C32 /* HTMLKit.m */; }; + 625A150319C783E200AD0C32 /* HTMLKit.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 625A14BC19C7829400AD0C32 /* HTMLKit.h */; }; + 625A150419C783EB00AD0C32 /* HTMLKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 625A14BC19C7829400AD0C32 /* HTMLKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 625A150819C78ABA00AD0C32 /* HTMLInputStreamReader.h in Headers */ = {isa = PBXBuildFile; fileRef = 625A150619C78ABA00AD0C32 /* HTMLInputStreamReader.h */; }; + 625A150919C78ABA00AD0C32 /* HTMLInputStreamReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 625A150719C78ABA00AD0C32 /* HTMLInputStreamReader.m */; }; + 625A150A19C78ABA00AD0C32 /* HTMLInputStreamReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 625A150719C78ABA00AD0C32 /* HTMLInputStreamReader.m */; }; + 6279F87419E1808D00F12EE5 /* HTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 6279F87219E1808D00F12EE5 /* HTMLElement.h */; }; + 6279F87519E1808D00F12EE5 /* HTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 6279F87319E1808D00F12EE5 /* HTMLElement.m */; }; + 6279F87619E1808D00F12EE5 /* HTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 6279F87319E1808D00F12EE5 /* HTMLElement.m */; }; + 62AE593519F97D880043F069 /* HTMLParseErrorToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE593319F97D880043F069 /* HTMLParseErrorToken.h */; }; + 62AE593619F97D880043F069 /* HTMLParseErrorToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE593419F97D880043F069 /* HTMLParseErrorToken.m */; }; + 62AE593719F97D880043F069 /* HTMLParseErrorToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE593419F97D880043F069 /* HTMLParseErrorToken.m */; }; + 62AE593A19F97E1C0043F069 /* HTMLDOCTYPEToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE593819F97E1C0043F069 /* HTMLDOCTYPEToken.h */; }; + 62AE593B19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE593919F97E1C0043F069 /* HTMLDOCTYPEToken.m */; }; + 62AE593C19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE593919F97E1C0043F069 /* HTMLDOCTYPEToken.m */; }; + 62AE593F19F9907C0043F069 /* HTMLTagToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE593D19F9907C0043F069 /* HTMLTagToken.h */; }; + 62AE594019F9907C0043F069 /* HTMLTagToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE593E19F9907C0043F069 /* HTMLTagToken.m */; }; + 62AE594119F9907C0043F069 /* HTMLTagToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE593E19F9907C0043F069 /* HTMLTagToken.m */; }; + 62AE594419F992F30043F069 /* HTMLCommentToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE594219F992F30043F069 /* HTMLCommentToken.h */; }; + 62AE594519F992F30043F069 /* HTMLCommentToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE594319F992F30043F069 /* HTMLCommentToken.m */; }; + 62AE594619F992F30043F069 /* HTMLCommentToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE594319F992F30043F069 /* HTMLCommentToken.m */; }; + 62AE594919F9948A0043F069 /* HTMLCharacterToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE594719F9948A0043F069 /* HTMLCharacterToken.h */; }; + 62AE594A19F9948A0043F069 /* HTMLCharacterToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE594819F9948A0043F069 /* HTMLCharacterToken.m */; }; + 62AE594B19F9948A0043F069 /* HTMLCharacterToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE594819F9948A0043F069 /* HTMLCharacterToken.m */; }; + 62D8345919FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 62D8345819FB1AC4009205A9 /* HTML5LibTokenizerTest.m */; }; + 62D8345A19FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 62D8345819FB1AC4009205A9 /* HTML5LibTokenizerTest.m */; }; + 62F31FDD19E9DCCF007F0657 /* HTMLTokenizerEntities.h in Headers */ = {isa = PBXBuildFile; fileRef = 62F31FDB19E9DCCF007F0657 /* HTMLTokenizerEntities.h */; }; + 62F31FDE19E9DCCF007F0657 /* HTMLTokenizerEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F31FDC19E9DCCF007F0657 /* HTMLTokenizerEntities.m */; }; + 62F31FDF19E9DCCF007F0657 /* HTMLTokenizerEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F31FDC19E9DCCF007F0657 /* HTMLTokenizerEntities.m */; }; + 62F873EB19E088C90062683C /* HTMLParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 62F873E919E088C90062683C /* HTMLParser.h */; }; + 62F873EC19E088C90062683C /* HTMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F873EA19E088C90062683C /* HTMLParser.m */; }; + 62F873ED19E088C90062683C /* HTMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F873EA19E088C90062683C /* HTMLParser.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 625A14C719C7829400AD0C32 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 625A14A319C7829400AD0C32 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 625A14AB19C7829400AD0C32; + remoteInfo = HTMLKit; + }; + 625A14F119C7834100AD0C32 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 625A14A319C7829400AD0C32 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 625A14DD19C7834000AD0C32; + remoteInfo = HTMLKit; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 625A14DC19C7834000AD0C32 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + 625A150319C783E200AD0C32 /* HTMLKit.h in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* 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 = ""; }; + 6234584D1A9D2FA4009BD491 /* HTMLNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNode.m; sourceTree = ""; }; + 6234BEEC1AABBF1400DEB15F /* HTMLStackOfOpenElements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLStackOfOpenElements.h; sourceTree = ""; }; + 6234BEED1AABBF1400DEB15F /* HTMLStackOfOpenElements.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLStackOfOpenElements.m; sourceTree = ""; }; + 6234C3361AB3BF710046F527 /* HTMLElementAdjustment.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLElementAdjustment.h; 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 = ""; }; + 62363C3B1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLListOfActiveFormattingElements.h; sourceTree = ""; }; + 62363C3C1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLListOfActiveFormattingElements.m; sourceTree = ""; }; + 6236738D1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitTokenizerPerformance.m; sourceTree = ""; }; + 623719431AA12EE8002E03C8 /* HTMLQuirksMode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLQuirksMode.h; sourceTree = ""; }; + 6238039D1AB63A8C008A53D0 /* HTMLEOFToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLEOFToken.h; sourceTree = ""; }; + 6238039E1AB63A8C008A53D0 /* HTMLEOFToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLEOFToken.m; sourceTree = ""; }; + 6238578F1A9E772B003A45D9 /* HTMLDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLDocument.h; sourceTree = ""; }; + 623857901A9E772B003A45D9 /* HTMLDocument.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLDocument.m; sourceTree = ""; }; + 623857941A9E8606003A45D9 /* HTMLDocumentType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLDocumentType.h; sourceTree = ""; }; + 623857951A9E8606003A45D9 /* HTMLDocumentType.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLDocumentType.m; sourceTree = ""; }; + 623857991A9E8934003A45D9 /* HTMLComment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLComment.h; sourceTree = ""; }; + 6238579A1A9E8934003A45D9 /* HTMLComment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLComment.m; sourceTree = ""; }; + 6238C9831AB8D6330006512E /* HTMLKitExceptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLKitExceptions.h; sourceTree = ""; }; + 6238C9841AB8D6330006512E /* HTMLKitExceptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitExceptions.m; sourceTree = ""; }; + 623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeTreeEnumerator.h; sourceTree = ""; }; + 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeTreeEnumerator.m; sourceTree = ""; }; + 623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitNodeTreeEnumratorTests.m; sourceTree = ""; }; + 623975591AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitTreeConstructionTests.m; sourceTree = ""; }; + 6239755C1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTML5LibTreeConstructionTest.h; sourceTree = ""; }; + 6239755D1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTML5LibTreeConstructionTest.m; sourceTree = ""; }; + 623CAF9C1AD88BEA00E34C32 /* HTMLKitParserPerformance.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitParserPerformance.m; sourceTree = ""; }; + 624493A419CCC54100BCDDF4 /* HTMLTokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLTokenizer.h; sourceTree = ""; }; + 624493A519CCC54100BCDDF4 /* HTMLTokenizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLTokenizer.m; sourceTree = ""; }; + 624493A919CCE84A00BCDDF4 /* HTMLTokenizerStates.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLTokenizerStates.h; sourceTree = ""; }; + 624493AA19CD0CBE00BCDDF4 /* HTMLToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLToken.h; sourceTree = ""; }; + 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLToken.m; sourceTree = ""; }; + 624AC8FE19FBF4F700BD3C4A /* HTMLTokens.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLTokens.h; sourceTree = ""; }; + 624AC90019FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitTokenizerTests.m; sourceTree = ""; }; + 624AC90419FBFE8A00BD3C4A /* html5lib-tests */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "html5lib-tests"; sourceTree = ""; }; + 624AC90D19FC702E00BD3C4A /* HTML Standard.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "HTML Standard.html"; sourceTree = ""; }; + 624B9FB11AE0313300646C4C /* HTMLKitStringCategoryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitStringCategoryTests.m; sourceTree = ""; }; + 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitOrderedDictionaryTests.m; sourceTree = ""; }; + 625A14AC19C7829400AD0C32 /* HTMLKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = HTMLKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 625A14AF19C7829400AD0C32 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + 625A14B219C7829400AD0C32 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 625A14B319C7829400AD0C32 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + 625A14B419C7829400AD0C32 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; + 625A14B719C7829400AD0C32 /* HTMLKit-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "HTMLKit-Info.plist"; sourceTree = ""; }; + 625A14B919C7829400AD0C32 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 625A14BB19C7829400AD0C32 /* HTMLKit-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "HTMLKit-Prefix.pch"; sourceTree = ""; }; + 625A14BC19C7829400AD0C32 /* HTMLKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLKit.h; sourceTree = ""; }; + 625A14BD19C7829400AD0C32 /* HTMLKit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HTMLKit.m; sourceTree = ""; }; + 625A14C319C7829400AD0C32 /* HTMLKitFrameworkTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HTMLKitFrameworkTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 625A14C419C7829400AD0C32 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + 625A14CC19C7829400AD0C32 /* HTMLKitTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "HTMLKitTests-Info.plist"; sourceTree = ""; }; + 625A14CE19C7829400AD0C32 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 625A14DE19C7834000AD0C32 /* libHTMLKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libHTMLKit.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 625A14DF19C7834100AD0C32 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 625A14EC19C7834100AD0C32 /* HTMLKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HTMLKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 625A150619C78ABA00AD0C32 /* HTMLInputStreamReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLInputStreamReader.h; sourceTree = ""; }; + 625A150719C78ABA00AD0C32 /* HTMLInputStreamReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLInputStreamReader.m; sourceTree = ""; }; + 6279F87119E17DC700F12EE5 /* HTMLParserInsertionModes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLParserInsertionModes.h; sourceTree = ""; }; + 6279F87219E1808D00F12EE5 /* HTMLElement.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.objc; fileEncoding = 4; path = HTMLElement.h; sourceTree = ""; }; + 6279F87319E1808D00F12EE5 /* HTMLElement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLElement.m; sourceTree = ""; }; + 628B7CE61A080E1000602C87 /* HTMLNamespaces.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLNamespaces.h; sourceTree = ""; }; + 62AE593319F97D880043F069 /* HTMLParseErrorToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLParseErrorToken.h; sourceTree = ""; }; + 62AE593419F97D880043F069 /* HTMLParseErrorToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLParseErrorToken.m; sourceTree = ""; }; + 62AE593819F97E1C0043F069 /* HTMLDOCTYPEToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLDOCTYPEToken.h; sourceTree = ""; }; + 62AE593919F97E1C0043F069 /* HTMLDOCTYPEToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLDOCTYPEToken.m; sourceTree = ""; }; + 62AE593D19F9907C0043F069 /* HTMLTagToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLTagToken.h; sourceTree = ""; }; + 62AE593E19F9907C0043F069 /* HTMLTagToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLTagToken.m; sourceTree = ""; }; + 62AE594219F992F30043F069 /* HTMLCommentToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLCommentToken.h; sourceTree = ""; }; + 62AE594319F992F30043F069 /* HTMLCommentToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLCommentToken.m; sourceTree = ""; }; + 62AE594719F9948A0043F069 /* HTMLCharacterToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLCharacterToken.h; sourceTree = ""; }; + 62AE594819F9948A0043F069 /* HTMLCharacterToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLCharacterToken.m; sourceTree = ""; }; + 62D8345719FB1AC4009205A9 /* HTML5LibTokenizerTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTML5LibTokenizerTest.h; sourceTree = ""; }; + 62D8345819FB1AC4009205A9 /* HTML5LibTokenizerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTML5LibTokenizerTest.m; sourceTree = ""; }; + 62E7CAAE19CDFFB500465A83 /* HTMLTokenizerCharacters.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLTokenizerCharacters.h; sourceTree = ""; }; + 62F31FDB19E9DCCF007F0657 /* HTMLTokenizerEntities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLTokenizerEntities.h; sourceTree = ""; }; + 62F31FDC19E9DCCF007F0657 /* HTMLTokenizerEntities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLTokenizerEntities.m; sourceTree = ""; }; + 62F873E919E088C90062683C /* HTMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLParser.h; sourceTree = ""; }; + 62F873EA19E088C90062683C /* HTMLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLParser.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 625A14A819C7829400AD0C32 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 625A14B019C7829400AD0C32 /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 625A14C019C7829400AD0C32 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 625A14C919C7829400AD0C32 /* HTMLKit.framework in Frameworks */, + 625A14C619C7829400AD0C32 /* Cocoa.framework in Frameworks */, + 625A14C519C7829400AD0C32 /* XCTest.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 625A14DB19C7834000AD0C32 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 625A14E019C7834100AD0C32 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 625A14E919C7834100AD0C32 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 6234CF571ABF892400B6077D /* libHTMLKit.a in Frameworks */, + 625A14EE19C7834100AD0C32 /* Foundation.framework in Frameworks */, + 625A14ED19C7834100AD0C32 /* XCTest.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6236738C1AC0CD2400FF89B3 /* Tokenizer */ = { + isa = PBXGroup; + children = ( + 62D8345719FB1AC4009205A9 /* HTML5LibTokenizerTest.h */, + 62D8345819FB1AC4009205A9 /* HTML5LibTokenizerTest.m */, + 624AC90019FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m */, + 6236738D1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m */, + ); + name = Tokenizer; + sourceTree = ""; + }; + 623719441AA1472B002E03C8 /* Nodes */ = { + isa = PBXGroup; + children = ( + 623719431AA12EE8002E03C8 /* HTMLQuirksMode.h */, + 62362A3F1A9FDE8A00301989 /* HTMLNodes.h */, + 6234584C1A9D2FA4009BD491 /* HTMLNode.h */, + 6234584D1A9D2FA4009BD491 /* HTMLNode.m */, + 6279F87219E1808D00F12EE5 /* HTMLElement.h */, + 6279F87319E1808D00F12EE5 /* HTMLElement.m */, + 6238578F1A9E772B003A45D9 /* HTMLDocument.h */, + 623857901A9E772B003A45D9 /* HTMLDocument.m */, + 623857941A9E8606003A45D9 /* HTMLDocumentType.h */, + 623857951A9E8606003A45D9 /* HTMLDocumentType.m */, + 623857991A9E8934003A45D9 /* HTMLComment.h */, + 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 */, + ); + name = Nodes; + sourceTree = ""; + }; + 623975581AC362A5007E26F1 /* Tree Construction */ = { + isa = PBXGroup; + children = ( + 6239755C1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.h */, + 6239755D1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m */, + 623975591AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m */, + 623CAF9C1AD88BEA00E34C32 /* HTMLKitParserPerformance.m */, + ); + name = "Tree Construction"; + sourceTree = ""; + }; + 624B9FB71AE072CB00646C4C /* Nodes */ = { + isa = PBXGroup; + children = ( + 623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */, + 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */, + ); + name = Nodes; + sourceTree = ""; + }; + 624B9FB81AE072D500646C4C /* Categories */ = { + isa = PBXGroup; + children = ( + 624B9FB11AE0313300646C4C /* HTMLKitStringCategoryTests.m */, + ); + name = Categories; + sourceTree = ""; + }; + 625A14A219C7829400AD0C32 = { + isa = PBXGroup; + children = ( + 625A14B519C7829400AD0C32 /* Source Code */, + 625A14CA19C7829400AD0C32 /* Tests */, + 625A14AE19C7829400AD0C32 /* Frameworks */, + 625A14AD19C7829400AD0C32 /* Products */, + ); + sourceTree = ""; + }; + 625A14AD19C7829400AD0C32 /* Products */ = { + isa = PBXGroup; + children = ( + 625A14AC19C7829400AD0C32 /* HTMLKit.framework */, + 625A14C319C7829400AD0C32 /* HTMLKitFrameworkTests.xctest */, + 625A14DE19C7834000AD0C32 /* libHTMLKit.a */, + 625A14EC19C7834100AD0C32 /* HTMLKitTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 625A14AE19C7829400AD0C32 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 625A14AF19C7829400AD0C32 /* Cocoa.framework */, + 625A14C419C7829400AD0C32 /* XCTest.framework */, + 625A14DF19C7834100AD0C32 /* Foundation.framework */, + 625A14B119C7829400AD0C32 /* Other Frameworks */, + ); + name = Frameworks; + sourceTree = ""; + }; + 625A14B119C7829400AD0C32 /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + 625A14B219C7829400AD0C32 /* Foundation.framework */, + 625A14B319C7829400AD0C32 /* CoreData.framework */, + 625A14B419C7829400AD0C32 /* AppKit.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 625A14B519C7829400AD0C32 /* Source Code */ = { + isa = PBXGroup; + children = ( + 625A14BC19C7829400AD0C32 /* HTMLKit.h */, + 625A14BD19C7829400AD0C32 /* HTMLKit.m */, + 6238C9831AB8D6330006512E /* HTMLKitExceptions.h */, + 6238C9841AB8D6330006512E /* HTMLKitExceptions.m */, + 62AE593219F97CCA0043F069 /* Tokenizing */, + 628E16EC1ADAE71700B15A06 /* Parsing */, + 623719441AA1472B002E03C8 /* Nodes */, + 628E16ED1ADAE73700B15A06 /* Categories */, + 628E16EE1ADAE75300B15A06 /* Structures */, + 625A14B619C7829400AD0C32 /* Supporting Files */, + ); + name = "Source Code"; + path = HTMLKit; + sourceTree = ""; + }; + 625A14B619C7829400AD0C32 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 625A14B719C7829400AD0C32 /* HTMLKit-Info.plist */, + 625A14B819C7829400AD0C32 /* InfoPlist.strings */, + 625A14BB19C7829400AD0C32 /* HTMLKit-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 625A14CA19C7829400AD0C32 /* Tests */ = { + isa = PBXGroup; + children = ( + 6236738C1AC0CD2400FF89B3 /* Tokenizer */, + 623975581AC362A5007E26F1 /* Tree Construction */, + 624B9FB71AE072CB00646C4C /* Nodes */, + 624B9FB81AE072D500646C4C /* Categories */, + 625A14CB19C7829400AD0C32 /* Supporting Files */, + ); + name = Tests; + path = HTMLKitTests; + sourceTree = ""; + }; + 625A14CB19C7829400AD0C32 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 624AC90D19FC702E00BD3C4A /* HTML Standard.html */, + 624AC90419FBFE8A00BD3C4A /* html5lib-tests */, + 625A14CC19C7829400AD0C32 /* HTMLKitTests-Info.plist */, + 625A14CD19C7829400AD0C32 /* InfoPlist.strings */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 628E16EC1ADAE71700B15A06 /* Parsing */ = { + isa = PBXGroup; + children = ( + 62F873E919E088C90062683C /* HTMLParser.h */, + 62F873EA19E088C90062683C /* HTMLParser.m */, + 6234BEEC1AABBF1400DEB15F /* HTMLStackOfOpenElements.h */, + 6234BEED1AABBF1400DEB15F /* HTMLStackOfOpenElements.m */, + 62363C3B1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h */, + 62363C3C1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m */, + 628B7CE61A080E1000602C87 /* HTMLNamespaces.h */, + 6223211D1A969B9300BACED5 /* HTMLElementTypes.h */, + 6234C3361AB3BF710046F527 /* HTMLElementAdjustment.h */, + 6279F87119E17DC700F12EE5 /* HTMLParserInsertionModes.h */, + 6235CE9E1AA5170A0026937B /* HTMLMarker.h */, + 6235CE9F1AA5170A0026937B /* HTMLMarker.m */, + ); + name = Parsing; + sourceTree = ""; + }; + 628E16ED1ADAE73700B15A06 /* Categories */ = { + isa = PBXGroup; + children = ( + 6235CE991AA509430026937B /* NSString+HTMLKit.h */, + 6235CE9A1AA509430026937B /* NSString+HTMLKit.m */, + ); + name = Categories; + sourceTree = ""; + }; + 628E16EE1ADAE75300B15A06 /* Structures */ = { + isa = PBXGroup; + children = ( + 623424861AB467B200726190 /* HTMLOrderedDictionary.h */, + 623424871AB467B200726190 /* HTMLOrderedDictionary.m */, + ); + name = Structures; + sourceTree = ""; + }; + 62AE593219F97CCA0043F069 /* Tokenizing */ = { + isa = PBXGroup; + children = ( + 625A150619C78ABA00AD0C32 /* HTMLInputStreamReader.h */, + 625A150719C78ABA00AD0C32 /* HTMLInputStreamReader.m */, + 624493A419CCC54100BCDDF4 /* HTMLTokenizer.h */, + 624493A519CCC54100BCDDF4 /* HTMLTokenizer.m */, + 624493A919CCE84A00BCDDF4 /* HTMLTokenizerStates.h */, + 62E7CAAE19CDFFB500465A83 /* HTMLTokenizerCharacters.h */, + 62F31FDB19E9DCCF007F0657 /* HTMLTokenizerEntities.h */, + 62F31FDC19E9DCCF007F0657 /* HTMLTokenizerEntities.m */, + 624AC8FE19FBF4F700BD3C4A /* HTMLTokens.h */, + 624493AA19CD0CBE00BCDDF4 /* HTMLToken.h */, + 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */, + 62AE594719F9948A0043F069 /* HTMLCharacterToken.h */, + 62AE594819F9948A0043F069 /* HTMLCharacterToken.m */, + 62AE594219F992F30043F069 /* HTMLCommentToken.h */, + 62AE594319F992F30043F069 /* HTMLCommentToken.m */, + 62AE593819F97E1C0043F069 /* HTMLDOCTYPEToken.h */, + 62AE593919F97E1C0043F069 /* HTMLDOCTYPEToken.m */, + 62AE593319F97D880043F069 /* HTMLParseErrorToken.h */, + 62AE593419F97D880043F069 /* HTMLParseErrorToken.m */, + 62AE593D19F9907C0043F069 /* HTMLTagToken.h */, + 62AE593E19F9907C0043F069 /* HTMLTagToken.m */, + 6238039D1AB63A8C008A53D0 /* HTMLEOFToken.h */, + 6238039E1AB63A8C008A53D0 /* HTMLEOFToken.m */, + ); + name = Tokenizing; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 625A14A919C7829400AD0C32 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 6235CE9B1AA509430026937B /* NSString+HTMLKit.h in Headers */, + 62F873EB19E088C90062683C /* HTMLParser.h in Headers */, + 624AC8FF19FBF59800BD3C4A /* HTMLTokens.h in Headers */, + 623916C31AC707250066B4FE /* HTMLNodeTreeEnumerator.h in Headers */, + 62AE594419F992F30043F069 /* HTMLCommentToken.h in Headers */, + 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 */, + 6279F87419E1808D00F12EE5 /* HTMLElement.h in Headers */, + 62AE593F19F9907C0043F069 /* HTMLTagToken.h in Headers */, + 623857911A9E772B003A45D9 /* HTMLDocument.h in Headers */, + 624493A619CCC54100BCDDF4 /* HTMLTokenizer.h in Headers */, + 62AE593A19F97E1C0043F069 /* HTMLDOCTYPEToken.h in Headers */, + 623424881AB467B200726190 /* HTMLOrderedDictionary.h in Headers */, + 625A150419C783EB00AD0C32 /* HTMLKit.h in Headers */, + 625A150819C78ABA00AD0C32 /* HTMLInputStreamReader.h in Headers */, + 6238579B1A9E8934003A45D9 /* HTMLComment.h in Headers */, + 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 */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 625A14AB19C7829400AD0C32 /* HTMLKitFramework */ = { + isa = PBXNativeTarget; + buildConfigurationList = 625A14D419C7829400AD0C32 /* Build configuration list for PBXNativeTarget "HTMLKitFramework" */; + buildPhases = ( + 625A14A719C7829400AD0C32 /* Sources */, + 625A14A819C7829400AD0C32 /* Frameworks */, + 625A14A919C7829400AD0C32 /* Headers */, + 625A14AA19C7829400AD0C32 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = HTMLKitFramework; + productName = HTMLKit; + productReference = 625A14AC19C7829400AD0C32 /* HTMLKit.framework */; + productType = "com.apple.product-type.framework"; + }; + 625A14C219C7829400AD0C32 /* HTMLKitFrameworkTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 625A14D719C7829400AD0C32 /* Build configuration list for PBXNativeTarget "HTMLKitFrameworkTests" */; + buildPhases = ( + 625A14BF19C7829400AD0C32 /* Sources */, + 625A14C019C7829400AD0C32 /* Frameworks */, + 625A14C119C7829400AD0C32 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 625A14C819C7829400AD0C32 /* PBXTargetDependency */, + ); + name = HTMLKitFrameworkTests; + productName = HTMLKitTests; + productReference = 625A14C319C7829400AD0C32 /* HTMLKitFrameworkTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 625A14DD19C7834000AD0C32 /* HTMLKit */ = { + isa = PBXNativeTarget; + buildConfigurationList = 625A14FC19C7834100AD0C32 /* Build configuration list for PBXNativeTarget "HTMLKit" */; + buildPhases = ( + 625A14DA19C7834000AD0C32 /* Sources */, + 625A14DB19C7834000AD0C32 /* Frameworks */, + 625A14DC19C7834000AD0C32 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = HTMLKit; + productName = HTMLKit; + productReference = 625A14DE19C7834000AD0C32 /* libHTMLKit.a */; + productType = "com.apple.product-type.library.static"; + }; + 625A14EB19C7834100AD0C32 /* HTMLKitTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 625A14FF19C7834100AD0C32 /* Build configuration list for PBXNativeTarget "HTMLKitTests" */; + buildPhases = ( + 625A14E819C7834100AD0C32 /* Sources */, + 625A14E919C7834100AD0C32 /* Frameworks */, + 625A14EA19C7834100AD0C32 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 625A14F219C7834100AD0C32 /* PBXTargetDependency */, + ); + name = HTMLKitTests; + productName = HTMLKitTests; + productReference = 625A14EC19C7834100AD0C32 /* HTMLKitTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 625A14A319C7829400AD0C32 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0610; + ORGANIZATIONNAME = BrainCookie; + TargetAttributes = { + 625A14C219C7829400AD0C32 = { + TestTargetID = 625A14AB19C7829400AD0C32; + }; + 625A14EB19C7834100AD0C32 = { + TestTargetID = 625A14AB19C7829400AD0C32; + }; + }; + }; + buildConfigurationList = 625A14A619C7829400AD0C32 /* Build configuration list for PBXProject "HTMLKit" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 625A14A219C7829400AD0C32; + productRefGroup = 625A14AD19C7829400AD0C32 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 625A14DD19C7834000AD0C32 /* HTMLKit */, + 625A14AB19C7829400AD0C32 /* HTMLKitFramework */, + 625A14EB19C7834100AD0C32 /* HTMLKitTests */, + 625A14C219C7829400AD0C32 /* HTMLKitFrameworkTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 625A14AA19C7829400AD0C32 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 625A14BA19C7829400AD0C32 /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 625A14C119C7829400AD0C32 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 624AC90F19FC702E00BD3C4A /* HTML Standard.html in Resources */, + 624AC90519FBFE9A00BD3C4A /* html5lib-tests in Resources */, + 625A14CF19C7829400AD0C32 /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 625A14EA19C7834100AD0C32 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 624AC90619FBFEA000BD3C4A /* html5lib-tests in Resources */, + 624AC90E19FC702E00BD3C4A /* HTML Standard.html in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 625A14A719C7829400AD0C32 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 62362A3E1A9FA70400301989 /* HTMLText.m in Sources */, + 6235CE9D1AA509430026937B /* NSString+HTMLKit.m in Sources */, + 6238579D1A9E8934003A45D9 /* HTMLComment.m in Sources */, + 6234BEF01AABBF1400DEB15F /* HTMLStackOfOpenElements.m in Sources */, + 624493AE19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */, + 6235CEA21AA5170A0026937B /* HTMLMarker.m in Sources */, + 6234248A1AB467B200726190 /* HTMLOrderedDictionary.m in Sources */, + 62AE594119F9907C0043F069 /* HTMLTagToken.m in Sources */, + 62AE594619F992F30043F069 /* HTMLCommentToken.m in Sources */, + 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 */, + 62AE594B19F9948A0043F069 /* HTMLCharacterToken.m in Sources */, + 623458501A9D2FA4009BD491 /* HTMLNode.m in Sources */, + 62F31FDF19E9DCCF007F0657 /* HTMLTokenizerEntities.m in Sources */, + 62AE593719F97D880043F069 /* HTMLParseErrorToken.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 625A14BF19C7829400AD0C32 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6239755B1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */, + 623CAF9E1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */, + 624B9FB61AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */, + 6236738F1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */, + 624AC90219FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */, + 624B9FB31AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */, + 623916C81AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */, + 62D8345A19FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */, + 6239755F1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 625A14DA19C7834000AD0C32 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 62362A3D1A9FA70400301989 /* HTMLText.m in Sources */, + 6235CE9C1AA509430026937B /* NSString+HTMLKit.m in Sources */, + 6238579C1A9E8934003A45D9 /* HTMLComment.m in Sources */, + 6234BEEF1AABBF1400DEB15F /* HTMLStackOfOpenElements.m in Sources */, + 624493AD19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */, + 6235CEA11AA5170A0026937B /* HTMLMarker.m in Sources */, + 623424891AB467B200726190 /* HTMLOrderedDictionary.m in Sources */, + 62AE594019F9907C0043F069 /* HTMLTagToken.m in Sources */, + 62AE594519F992F30043F069 /* HTMLCommentToken.m in Sources */, + 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 */, + 62AE594A19F9948A0043F069 /* HTMLCharacterToken.m in Sources */, + 6234584F1A9D2FA4009BD491 /* HTMLNode.m in Sources */, + 62F31FDE19E9DCCF007F0657 /* HTMLTokenizerEntities.m in Sources */, + 62AE593619F97D880043F069 /* HTMLParseErrorToken.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 625A14E819C7834100AD0C32 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6239755A1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */, + 623CAF9D1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */, + 624B9FB51AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */, + 6236738E1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */, + 624AC90119FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */, + 624B9FB21AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */, + 623916C71AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */, + 62D8345919FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */, + 6239755E1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 625A14C819C7829400AD0C32 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 625A14AB19C7829400AD0C32 /* HTMLKitFramework */; + targetProxy = 625A14C719C7829400AD0C32 /* PBXContainerItemProxy */; + }; + 625A14F219C7834100AD0C32 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 625A14DD19C7834000AD0C32 /* HTMLKit */; + targetProxy = 625A14F119C7834100AD0C32 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 625A14B819C7829400AD0C32 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 625A14B919C7829400AD0C32 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + 625A14CD19C7829400AD0C32 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 625A14CE19C7829400AD0C32 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 625A14D219C7829400AD0C32 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 625A14D319C7829400AD0C32 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + SDKROOT = macosx; + }; + name = Release; + }; + 625A14D519C7829400AD0C32 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch"; + INFOPLIST_FILE = "HTMLKit/HTMLKit-Info.plist"; + PRODUCT_NAME = HTMLKit; + WRAPPER_EXTENSION = framework; + }; + name = Debug; + }; + 625A14D619C7829400AD0C32 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch"; + INFOPLIST_FILE = "HTMLKit/HTMLKit-Info.plist"; + PRODUCT_NAME = HTMLKit; + WRAPPER_EXTENSION = framework; + }; + name = Release; + }; + 625A14D819C7829400AD0C32 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = ""; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "HTMLKitTests/HTMLKitTests-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = xctest; + }; + name = Debug; + }; + 625A14D919C7829400AD0C32 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = ""; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch"; + INFOPLIST_FILE = "HTMLKitTests/HTMLKitTests-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = xctest; + }; + name = Release; + }; + 625A14FD19C7834100AD0C32 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DSTROOT = /tmp/HTMLKit.dst; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = HTMLKit; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 625A14FE19C7834100AD0C32 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DSTROOT = /tmp/HTMLKit.dst; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = HTMLKit; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 625A150019C7834100AD0C32 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "HTMLKitTests/HTMLKitTests-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = xctest; + }; + name = Debug; + }; + 625A150119C7834100AD0C32 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch"; + INFOPLIST_FILE = "HTMLKitTests/HTMLKitTests-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + TEST_HOST = "$(BUNDLE_LOADER)"; + VALIDATE_PRODUCT = YES; + WRAPPER_EXTENSION = xctest; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 625A14A619C7829400AD0C32 /* Build configuration list for PBXProject "HTMLKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 625A14D219C7829400AD0C32 /* Debug */, + 625A14D319C7829400AD0C32 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 625A14D419C7829400AD0C32 /* Build configuration list for PBXNativeTarget "HTMLKitFramework" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 625A14D519C7829400AD0C32 /* Debug */, + 625A14D619C7829400AD0C32 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 625A14D719C7829400AD0C32 /* Build configuration list for PBXNativeTarget "HTMLKitFrameworkTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 625A14D819C7829400AD0C32 /* Debug */, + 625A14D919C7829400AD0C32 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 625A14FC19C7834100AD0C32 /* Build configuration list for PBXNativeTarget "HTMLKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 625A14FD19C7834100AD0C32 /* Debug */, + 625A14FE19C7834100AD0C32 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 625A14FF19C7834100AD0C32 /* Build configuration list for PBXNativeTarget "HTMLKitTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 625A150019C7834100AD0C32 /* Debug */, + 625A150119C7834100AD0C32 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 625A14A319C7829400AD0C32 /* Project object */; +} diff --git a/HTMLKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/HTMLKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..cc496ed --- /dev/null +++ b/HTMLKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/HTMLKit.xcodeproj/xcshareddata/xcbaselines/625A14C219C7829400AD0C32.xcbaseline/5AB53A08-CDD0-43FB-B47F-2EE38B3FE707.plist b/HTMLKit.xcodeproj/xcshareddata/xcbaselines/625A14C219C7829400AD0C32.xcbaseline/5AB53A08-CDD0-43FB-B47F-2EE38B3FE707.plist new file mode 100644 index 0000000..1ca3272 --- /dev/null +++ b/HTMLKit.xcodeproj/xcshareddata/xcbaselines/625A14C219C7829400AD0C32.xcbaseline/5AB53A08-CDD0-43FB-B47F-2EE38B3FE707.plist @@ -0,0 +1,48 @@ + + + + + classNames + + HTMLKitParserPerformance + + testParserPerformance + + com.apple.XCTPerformanceMetric_WallClockTime + + baselineAverage + 18.23 + baselineIntegrationDisplayName + Local Baseline + + + + HTMLKitTokenizerPerformance + + testTokenizerPerformance + + com.apple.XCTPerformanceMetric_WallClockTime + + baselineAverage + 14.55 + baselineIntegrationDisplayName + Local Baseline + + + + HTMLTokenizerTests + + testTokenizerPerformance + + com.apple.XCTPerformanceMetric_WallClockTime + + baselineAverage + 15.47 + baselineIntegrationDisplayName + Local Baseline + + + + + + diff --git a/HTMLKit.xcodeproj/xcshareddata/xcbaselines/625A14C219C7829400AD0C32.xcbaseline/Info.plist b/HTMLKit.xcodeproj/xcshareddata/xcbaselines/625A14C219C7829400AD0C32.xcbaseline/Info.plist new file mode 100644 index 0000000..da6ac2c --- /dev/null +++ b/HTMLKit.xcodeproj/xcshareddata/xcbaselines/625A14C219C7829400AD0C32.xcbaseline/Info.plist @@ -0,0 +1,33 @@ + + + + + runDestinationsByUUID + + 5AB53A08-CDD0-43FB-B47F-2EE38B3FE707 + + localComputer + + busSpeedInMHz + 1600 + cpuCount + 2 + cpuKind + Quad-Core Intel Xeon + cpuSpeedInMHz + 2800 + logicalCPUCoresPerPackage + 4 + modelCode + MacPro3,1 + physicalCPUCoresPerPackage + 4 + platformIdentifier + com.apple.platform.macosx + + targetArchitecture + x86_64 + + + + diff --git a/HTMLKit/HTMLCharacterToken.h b/HTMLKit/HTMLCharacterToken.h new file mode 100644 index 0000000..0341991 --- /dev/null +++ b/HTMLKit/HTMLCharacterToken.h @@ -0,0 +1,27 @@ +// +// HTMLCharacterToken.h +// HTMLKit +// +// Created by Iska on 23/10/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import +#import "HTMLToken.h" + +@interface HTMLCharacterToken : HTMLToken + +@property (nonatomic, copy) NSString *characters; + +- (instancetype)initWithString:(NSString *)string; + +- (void)appendString:(NSString *)string; +- (BOOL)isWhitespaceToken; +- (BOOL)isEmpty; + +- (void)retainLeadingWhitespace; +- (void)trimLeadingWhitespace; +- (void)trimFormIndex:(NSUInteger)index; +- (HTMLCharacterToken *)tokenBySplitingLeadingWhiteSpace; + +@end diff --git a/HTMLKit/HTMLCharacterToken.m b/HTMLKit/HTMLCharacterToken.m new file mode 100644 index 0000000..f866509 --- /dev/null +++ b/HTMLKit/HTMLCharacterToken.m @@ -0,0 +1,101 @@ +// +// HTMLCharacterToken.m +// HTMLKit +// +// Created by Iska on 23/10/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import "HTMLCharacterToken.h" +#import "NSString+HTMLKit.h" + +@interface HTMLCharacterToken () +{ + NSMutableString *_characters; +} +@end + +@implementation HTMLCharacterToken + +- (instancetype)initWithString:(NSString *)string +{ + self = [super init]; + if (self) { + _characters = [string mutableCopy]; + } + return self; +} + +- (void)appendString:(NSString *)string +{ + if (_characters == nil) { + _characters = [NSMutableString new]; + } + [_characters appendString:string]; +} + +- (BOOL)isWhitespaceToken +{ + return [_characters isHTMLWhitespaceString]; +} + +- (BOOL)isEmpty +{ + return _characters.length == 0; +} + +- (void)retainLeadingWhitespace +{ + NSUInteger index = _characters.leadingHTMLWhitespaceLength; + if (index > 0) { + [_characters setString:[_characters substringToIndex:index]]; + } +} + +- (void)trimLeadingWhitespace +{ + NSUInteger index = _characters.leadingHTMLWhitespaceLength; + if (index > 0) { + [_characters setString:[_characters substringFromIndex:index]]; + } +} + +- (void)trimFormIndex:(NSUInteger)index +{ + [_characters setString:[_characters substringFromIndex:index]]; +} + +- (HTMLCharacterToken *)tokenBySplitingLeadingWhiteSpace +{ + NSUInteger index = _characters.leadingHTMLWhitespaceLength; + if (index > 0) { + NSString *leading = [_characters substringToIndex:index]; + [_characters setString:[_characters substringFromIndex:index]]; + return [[HTMLCharacterToken alloc] initWithString:leading]; + } + + return nil; +} + +#pragma mark - NSObject + +- (BOOL)isEqual:(id)other +{ + if ([other isKindOfClass:[self class]]) { + HTMLCharacterToken *token = (HTMLCharacterToken *)other; + return bothNilOrEqual(self.characters, token.characters); + } + return NO; +} + +- (NSUInteger)hash +{ + return self.characters.hash; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: %p Characters='%@'>", self.class, self, _characters]; +} + +@end diff --git a/HTMLKit/HTMLComment.h b/HTMLKit/HTMLComment.h new file mode 100644 index 0000000..100f13c --- /dev/null +++ b/HTMLKit/HTMLComment.h @@ -0,0 +1,17 @@ +// +// HTMLComment.h +// HTMLKit +// +// Created by Iska on 25/02/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLNode.h" + +@interface HTMLComment : HTMLNode + +@property (nonatomic, copy) NSString *data; + +- (instancetype)initWithData:(NSString *)data; + +@end diff --git a/HTMLKit/HTMLComment.m b/HTMLKit/HTMLComment.m new file mode 100644 index 0000000..23194bc --- /dev/null +++ b/HTMLKit/HTMLComment.m @@ -0,0 +1,55 @@ +// +// HTMLComment.m +// HTMLKit +// +// Created by Iska on 25/02/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLComment.h" + +@implementation HTMLComment + +- (instancetype)initWithData:(NSString *)data +{ + self = [super initWithName:@"#comment" type:HTMLNodeComment]; + if (self) { + self.data = data ?: @""; + } + return self; +} + +- (NSString *)textContent +{ + return self.data; +} + +- (void)setTextContent:(NSString *)textContent +{ + self.data = textContent ?: @""; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone +{ + HTMLComment *copy = [super copyWithZone:zone]; + copy.data = self.data; + return copy; +} + +#pragma mark - Serialization + +- (NSString *)outerHTML +{ + return [NSString stringWithFormat:@"", self.data]; +} + +#pragma mark - Description + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: %p >", self.class, self, self.data]; +} + +@end diff --git a/HTMLKit/HTMLCommentToken.h b/HTMLKit/HTMLCommentToken.h new file mode 100644 index 0000000..044fc7f --- /dev/null +++ b/HTMLKit/HTMLCommentToken.h @@ -0,0 +1,20 @@ +// +// HTMLCommentToken.h +// HTMLKit +// +// Created by Iska on 23/10/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import +#import "HTMLToken.h" + +@interface HTMLCommentToken : HTMLToken + +@property (nonatomic, copy) NSString *data; + +- (instancetype)initWithData:(NSString *)data; + +- (void)appendStringToData:(NSString *)string; + +@end diff --git a/HTMLKit/HTMLCommentToken.m b/HTMLKit/HTMLCommentToken.m new file mode 100644 index 0000000..4d71b33 --- /dev/null +++ b/HTMLKit/HTMLCommentToken.m @@ -0,0 +1,59 @@ +// +// HTMLCommentToken.m +// HTMLKit +// +// Created by Iska on 23/10/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import "HTMLCommentToken.h" + +@interface HTMLCommentToken () +{ + NSMutableString *_data; +} +@end + +@implementation HTMLCommentToken +@synthesize data = _data; + +- (instancetype)initWithData:(NSString *)data +{ + self = [super init]; + if (self) { + self.type = HTMLTokenTypeComment; + _data = [data mutableCopy]; + } + return self; +} + +- (void)appendStringToData:(NSString *)string +{ + if (_data == nil) { + _data = [NSMutableString new]; + } + [_data appendString:string]; +} + +#pragma mark - NSObject + +- (BOOL)isEqual:(id)other +{ + if ([other isKindOfClass:[self class]]) { + HTMLCommentToken *token = (HTMLCommentToken *)other; + return bothNilOrEqual(self.data, token.data); + } + return NO; +} + +- (NSUInteger)hash +{ + return self.data.hash; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: %p Data='%@'>", self.class, self, _data]; +} + +@end diff --git a/HTMLKit/HTMLDOCTYPEToken.h b/HTMLKit/HTMLDOCTYPEToken.h new file mode 100644 index 0000000..1a4de9b --- /dev/null +++ b/HTMLKit/HTMLDOCTYPEToken.h @@ -0,0 +1,25 @@ +// +// HTMLDOCTYPEToken.h +// HTMLKit +// +// Created by Iska on 23/10/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import +#import "HTMLToken.h" + +@interface HTMLDOCTYPEToken : HTMLToken + +@property (nonatomic, copy) NSString *name; +@property (nonatomic, strong) NSMutableString *publicIdentifier; +@property (nonatomic, strong) NSMutableString *systemIdentifier; +@property (nonatomic, assign) BOOL forceQuirks; + +- (instancetype)initWithName:(NSString *)name; + +- (void)appendStringToName:(NSString *)string; +- (void)appendStringToPublicIdentifier:(NSString *)string; +- (void)appendStringToSystemIdentifier:(NSString *)string; + +@end diff --git a/HTMLKit/HTMLDOCTYPEToken.m b/HTMLKit/HTMLDOCTYPEToken.m new file mode 100644 index 0000000..ba98f79 --- /dev/null +++ b/HTMLKit/HTMLDOCTYPEToken.m @@ -0,0 +1,84 @@ +// +// HTMLDOCTYPEToken.m +// HTMLKit +// +// Created by Iska on 23/10/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import "HTMLDOCTYPEToken.h" + +@interface HTMLDOCTYPEToken () +{ + NSMutableString *_name; +} + +@end + +@implementation HTMLDOCTYPEToken +@synthesize name = _name; + +- (instancetype)init +{ + return [self initWithName:nil]; +} + +- (instancetype)initWithName:(NSString *)name +{ + self = [super init]; + if (self) { + self.type = HTMLTokenTypeDoctype; + _name = [name mutableCopy]; + } + return self; +} + +- (void)appendStringToName:(NSString *)string +{ + if (_name == nil) { + _name = [NSMutableString new]; + } + [_name appendString:string]; +} + +- (void)appendStringToPublicIdentifier:(NSString *)string +{ + if (_publicIdentifier == nil) { + _publicIdentifier = [NSMutableString new]; + } + [_publicIdentifier appendString:string]; +} + +- (void)appendStringToSystemIdentifier:(NSString *)string +{ + if (_systemIdentifier == nil) { + _systemIdentifier = [NSMutableString new]; + } + [_systemIdentifier appendString:string]; +} + +#pragma mark - NSObject + +- (BOOL)isEqual:(id)other +{ + if ([other isKindOfClass:[self class]]) { + HTMLDOCTYPEToken *token = (HTMLDOCTYPEToken *)other; + return (bothNilOrEqual(self.name, token.name) && + bothNilOrEqual(self.publicIdentifier, token.publicIdentifier) && + bothNilOrEqual(self.systemIdentifier, token.systemIdentifier) && + self.forceQuirks == token.forceQuirks); + } + return NO; +} + +- (NSUInteger)hash +{ + return self.name.hash + self.publicIdentifier.hash + self.systemIdentifier.hash; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: %p Name='%@' Public='%@' System='%@' ForceQuirks='%@'>", self.class, self, _name, _publicIdentifier, _systemIdentifier, @(_forceQuirks)]; +} + +@end diff --git a/HTMLKit/HTMLDocument.h b/HTMLKit/HTMLDocument.h new file mode 100644 index 0000000..ddcec2b --- /dev/null +++ b/HTMLKit/HTMLDocument.h @@ -0,0 +1,34 @@ +// +// HTMLDocument.h +// HTMLKit +// +// Created by Iska on 25/02/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLNode.h" +#import "HTMLDocumentType.h" +#import "HTMLQuirksMode.h" + +typedef NS_ENUM(short, HTMLDocumentReadyState) +{ + HTMLDocumentLoading, + HTMLDocumentInteractive, // Not used + HTMLDocumentComplete +}; + +@interface HTMLDocument : HTMLNode + +@property (nonatomic, strong) HTMLDocumentType *documentType; + +@property (nonatomic, assign) HTMLQuirksMode quirksMode; + +@property (nonatomic, copy, readonly) NSString *compatMode; + +@property (nonatomic, assign, readonly) HTMLDocumentReadyState readyState; + +- (HTMLNode *)adoptNode:(HTMLNode *)node; + +- (HTMLDocument *)associatedInertTemplateDocument; + +@end diff --git a/HTMLKit/HTMLDocument.m b/HTMLKit/HTMLDocument.m new file mode 100644 index 0000000..11e2a42 --- /dev/null +++ b/HTMLKit/HTMLDocument.m @@ -0,0 +1,97 @@ +// +// HTMLDocument.m +// HTMLKit +// +// Created by Iska on 25/02/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLDocument.h" +#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]; + if (self) { + _readyState = HTMLDocumentLoading; + } + return self; +} + +#pragma mark - Accessors + +- (void)setOwnerDocument:(HTMLDocument *)ownerDocument +{ + [self doesNotRecognizeSelector:_cmd]; +} + +- (void)setDocumentType:(HTMLDocumentType *)documentType +{ + if (documentType == nil) { + if (self.documentType != nil) { + [self removeChildNode:self.documentType]; + } + return; + } + + if (self.documentType != nil) { + [self replaceChildNode:self.documentType withNode:documentType]; + } else { + [self appendNode:documentType]; + } +} + +#pragma mark - Mutation Algorithms + +- (HTMLNode *)adoptNode:(HTMLNode *)node +{ + if (node == nil) { + return nil; + } + + if (node.type == HTMLNodeDocument) { + [NSException raise:HTMLKitNotSupportedError + format:@"%@: Not Fount Error, adopting a document node. The operation is not supported.", NSStringFromSelector(_cmd)]; + } + + [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 +{ + return [[NSAttributedString alloc] initWithString:self.innerHTML]; +} + +@end 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/HTMLDocumentType.h b/HTMLKit/HTMLDocumentType.h new file mode 100644 index 0000000..d872730 --- /dev/null +++ b/HTMLKit/HTMLDocumentType.h @@ -0,0 +1,25 @@ +// +// HTMLDocumentType.h +// HTMLKit +// +// Created by Iska on 25/02/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLNode.h" +#import "HTMLQuirksMode.h" + +@interface HTMLDocumentType : HTMLNode + +@property (nonatomic, copy, readonly) NSString *publicIdentifier; + +@property (nonatomic, copy, readonly) NSString *systemIdentifier; + +- (instancetype)initWithName:(NSString *)name + publicIdentifier:(NSString *)publicIdentifier + systemIdentifier:(NSString *)systemIdentifier; + +- (BOOL)isValid; +- (HTMLQuirksMode)quirksMode; + +@end diff --git a/HTMLKit/HTMLDocumentType.m b/HTMLKit/HTMLDocumentType.m new file mode 100644 index 0000000..ce6687f --- /dev/null +++ b/HTMLKit/HTMLDocumentType.m @@ -0,0 +1,151 @@ +// +// HTMLDocumentType.m +// HTMLKit +// +// Created by Iska on 25/02/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLDocumentType.h" +#import "NSString+HTMLKit.h" + +NS_INLINE BOOL nilOrEqual(id first, id second) { + return (first == nil) || ([first isEqual:second]); +} + +@interface HTMLDocumentType () +{ + NSString *_publicIdentifier; + NSString *_systemIdentifier; +} +@end + +@implementation HTMLDocumentType + +- (instancetype)initWithName:(NSString *)name + publicIdentifier:(NSString *)publicIdentifier + systemIdentifier:(NSString *)systemIdentifier +{ + self = [super initWithName:name type:HTMLNodeDocumentType]; + if (self) { + _publicIdentifier = [publicIdentifier copy]; + _systemIdentifier = [systemIdentifier copy]; + } + return self; +} + +- (NSString *)publicIdentifier +{ + return _publicIdentifier ?: @""; +} + +- (NSString *)systemIdentifier +{ + return _systemIdentifier ?: @""; +} + +- (BOOL)isValid +{ + if (![self.name isEqualToString:@"html"]) { + return NO; + } + + if ([_publicIdentifier isEqualToString:@"-//W3C//DTD HTML 4.0//EN"] && + nilOrEqual(_systemIdentifier, @"http://www.w3.org/TR/REC-html40/strict.dtd")) { + return YES; + } + + if ([_publicIdentifier isEqualToString:@"-//W3C//DTD HTML 4.01//EN"] && + nilOrEqual(_systemIdentifier, @"http://www.w3.org/TR/html4/strict.dtd")) { + return YES; + } + + if ([_publicIdentifier isEqualToString:@"-//W3C//DTD XHTML 1.0 Strict//EN"] && + nilOrEqual(_systemIdentifier, @"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd")) { + return YES; + } + + if ([_publicIdentifier isEqualToString:@"-//W3C//DTD XHTML 1.1//EN"] && + nilOrEqual(_systemIdentifier, @"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd")) { + return YES; + } + + if (_publicIdentifier != nil) { + return NO; + } + + if (_systemIdentifier && ![_systemIdentifier isEqualToString:@"about:legacy-compat"]) { + return NO; + } + + return YES; +} + +- (HTMLQuirksMode)quirksMode +{ + if (![self.name isEqualToString:@"html"]) { + return HTMLQuirksModeQuirks; + } + + if ([_publicIdentifier isEqualToStringIgnoringCase:@"-//W3O//DTD W3 HTML Strict 3.0//EN//"] || + [_publicIdentifier isEqualToStringIgnoringCase:@"-/W3C/DTD HTML 4.0 Transitional/EN"] || + [_publicIdentifier isEqualToStringIgnoringCase:@"HTML"]) { + return HTMLQuirksModeQuirks; + } + + if ([_publicIdentifier isEqualToStringIgnoringCase:@"http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd"]) { + return HTMLQuirksModeQuirks; + } + + if (QuirksModePrefixMatch(_publicIdentifier)) { + return HTMLQuirksModeQuirks; + } + + if (_systemIdentifier == nil) { + if ([_publicIdentifier hasPrefixIgnoringCase:@"-//W3C//DTD HTML 4.01 Frameset//"] || + [_publicIdentifier hasPrefixIgnoringCase:@"-//W3C//DTD HTML 4.01 Transitional//"]) { + return HTMLQuirksModeQuirks; + } + } + + if ([_publicIdentifier hasPrefixIgnoringCase:@"-//W3C//DTD XHTML 1.0 Frameset//"] || + [_publicIdentifier hasPrefixIgnoringCase:@"-//W3C//DTD XHTML 1.0 Transitional//"]) { + return HTMLQuirksModeLimitedQuirks; + } + + if (_systemIdentifier != nil) { + if ([_publicIdentifier hasPrefixIgnoringCase:@"-//W3C//DTD HTML 4.01 Frameset//"] || + [_publicIdentifier hasPrefixIgnoringCase:@"-//W3C//DTD HTML 4.01 Transitional//"]) { + return HTMLQuirksModeLimitedQuirks; + } + } + + return HTMLQuirksModeNoQuirks; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone +{ + HTMLDocumentType *copy = [super copyWithZone:zone]; + copy->_publicIdentifier = self.publicIdentifier; + copy->_systemIdentifier = self.systemIdentifier; + return copy; +} + +#pragma mark - Serialization + +- (NSString *)outerHTML +{ + return [NSString stringWithFormat:@"", self.name]; +} + +#pragma mark - Description + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: %p >", + self.class, self, self.name, self.publicIdentifier, self.systemIdentifier]; +} + +@end diff --git a/HTMLKit/HTMLEOFToken.h b/HTMLKit/HTMLEOFToken.h new file mode 100644 index 0000000..2ebd9c2 --- /dev/null +++ b/HTMLKit/HTMLEOFToken.h @@ -0,0 +1,15 @@ +// +// HTMLEOFToken.h +// HTMLKit +// +// Created by Iska on 15/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLToken.h" + +@interface HTMLEOFToken : HTMLToken + ++ (instancetype)token; + +@end diff --git a/HTMLKit/HTMLEOFToken.m b/HTMLKit/HTMLEOFToken.m new file mode 100644 index 0000000..ecb8132 --- /dev/null +++ b/HTMLKit/HTMLEOFToken.m @@ -0,0 +1,37 @@ +// +// HTMLEOFToken.m +// HTMLKit +// +// Created by Iska on 15/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLEOFToken.h" + +@implementation HTMLEOFToken + ++ (instancetype)token +{ + static dispatch_once_t onceToken; + static HTMLEOFToken *singleton = nil; + dispatch_once(&onceToken, ^{ + singleton = [[self alloc] init]; + }); + return singleton; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.type = HTMLTokenTypeEOF; + } + return self; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: %p EOF>", self.class, self]; +} + +@end diff --git a/HTMLKit/HTMLElement.h b/HTMLKit/HTMLElement.h new file mode 100644 index 0000000..1085162 --- /dev/null +++ b/HTMLKit/HTMLElement.h @@ -0,0 +1,34 @@ +// +// HTMLElement.h +// HTMLKit +// +// Created by Iska on 05/10/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import +#import "HTMLNamespaces.h" +#import "HTMLNode.h" + +@interface HTMLElement : HTMLNode + +@property (nonatomic, assign, readonly) HTMLNamespace htmlNamespace; + +@property (nonatomic, copy, readonly) NSString *tagName; + +@property (nonatomic, strong) NSMutableDictionary *attributes; + +@property (nonatomic, copy) NSString *elementId; + +@property (nonatomic, copy) NSString *className; + +- (instancetype)initWithTagName:(NSString *)tagName; +- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes; +- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes namespace:(HTMLNamespace)htmlNamespace; + +- (BOOL)hasAttribute:(NSString *)name; +- (NSString *)objectForKeyedSubscript:(NSString *)name; +- (void)setObject:(NSString *)value forKeyedSubscript:(NSString *)attribute; +- (void)removeAttribute:(NSString *)name; + +@end diff --git a/HTMLKit/HTMLElement.m b/HTMLKit/HTMLElement.m new file mode 100644 index 0000000..e12b5f7 --- /dev/null +++ b/HTMLKit/HTMLElement.m @@ -0,0 +1,174 @@ +// +// HTMLElement.m +// HTMLKit +// +// Created by Iska on 05/10/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import "HTMLElement.h" +#import "HTMLDocument.h" +#import "HTMLText.h" + +#import "HTMLOrderedDictionary.h" +#import "NSString+HTMLKit.h" + +@interface HTMLElement () +{ + HTMLOrderedDictionary *_attributes; +} +@end + +@implementation HTMLElement + +#pragma mark - Init + +- (instancetype)init +{ + return [self initWithTagName:nil]; +} + +- (instancetype)initWithTagName:(NSString *)tagName +{ + return [self initWithTagName:tagName attributes:nil]; +} + +- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes +{ + return [self initWithTagName:tagName attributes:attributes namespace:HTMLNamespaceHTML]; +} + +- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes namespace:(HTMLNamespace)htmlNamespace +{ + self = [super initWithName:tagName type:HTMLNodeElement]; + if (self) { + _tagName = [tagName copy]; + _attributes = [HTMLOrderedDictionary new]; + if (attributes != nil) { + [_attributes addEntriesFromDictionary:attributes]; + } + _htmlNamespace = htmlNamespace; + } + return self; +} + +#pragma mark - Attributes + +- (NSString *)elementId +{ + return _attributes[@"id"] ?: @""; +} + +- (NSString *)className +{ + return _attributes[@"class"]; +} + +- (BOOL)hasAttribute:(NSString *)name +{ + return _attributes[name] != nil; +} + +- (NSString *)objectForKeyedSubscript:(NSString *)name; +{ + return _attributes[name]; +} + +- (void)setObject:(NSString *)value forKeyedSubscript:(NSString *)attribute +{ + _attributes[attribute] = value; +} + +- (void)removeAttribute:(NSString *)name +{ + [_attributes removeObjectForKey:name]; +} + +- (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]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone +{ + HTMLElement *copy = [super copyWithZone:zone]; + copy->_tagName = [_tagName copy]; + copy->_attributes = [_attributes copy]; + copy->_htmlNamespace = _htmlNamespace; + return copy; +} + +#pragma mark - Serialization + +- (NSString *)outerHTML +{ + NSMutableString *result = [NSMutableString string]; + + [result appendFormat:@"<%@", self.tagName]; + [self.attributes enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL *stop) { + NSRange range = NSMakeRange(0, value.length); + NSMutableString *escaped = [value mutableCopy]; + [escaped replaceOccurrencesOfString:@"&" withString:@"&" options:0 range:range]; + [escaped replaceOccurrencesOfString:@"\00A0" withString:@" " options:0 range:range]; + [escaped replaceOccurrencesOfString:@"\"" withString:@""" options:0 range:range]; + + [result appendFormat:@" %@=\"%@\"", key, escaped]; + }]; + + [result appendString:@">"]; + + if ([self.tagName isEqualToAny:@"area", @"base", @"basefont", @"bgsound", @"br", @"col", @"embed", + @"frame", @"hr", @"img", @"input", @"keygen", @"link", @"menuitem", @"meta", @"param", @"source", + @"track", @"wbr", nil]) { + return result; + } + + if ([self.tagName isEqualToAny:@"pre", @"textarea", @"listing", nil] && self.firstChiledNode.type == HTMLNodeText) { + HTMLText *textNode = (HTMLText *)self.firstChiledNode; + if ([textNode.data hasPrefix:@"\n"]) { + [result appendString:@"\n"]; + } + } + [result appendString:self.innerHTML]; + [result appendFormat:@"", self.tagName]; + + return result; +} + +#pragma mark - Description + +- (NSString *)description +{ + NSMutableString *description = [NSMutableString stringWithFormat:@"<%@: %p <", self.class, self]; + + if (self.htmlNamespace == HTMLNamespaceMathML) { + [description appendString:@"math "]; + } else if (self.htmlNamespace == HTMLNamespaceSVG) { + [description appendString:@"svg "]; + } + + [description appendString:self.tagName]; + [self.attributes enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + [description appendFormat:@" %@=\"%@\"", key, obj]; + }]; + + [description appendString:@">>"]; + + return description; +} + +@end diff --git a/HTMLKit/HTMLElementAdjustment.h b/HTMLKit/HTMLElementAdjustment.h new file mode 100644 index 0000000..e6f2e24 --- /dev/null +++ b/HTMLKit/HTMLElementAdjustment.h @@ -0,0 +1,166 @@ +// +// HTMLElementAdjustment.h +// HTMLKit +// +// Created by Iska on 14/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLElement.h" +#import "HTMLTokens.h" +#import "HTMLNamespaces.h" +#import "NSString+HTMLKit.h" + +NS_INLINE void AdjustMathMLAttributes(HTMLTagToken *token) +{ + NSString *lowercase = token.attributes[@"definitionurl"]; + if (lowercase != nil) { + [token.attributes replaceKey:@"definitionurl" withKey:@"definitionURL"]; + } +} + +NS_INLINE void AdjustSVGAttributes(HTMLTagToken *token) +{ + if (token.attributes == nil) { + return; + } + + NSDictionary *replacements = @{@"attributename": @"attributeName", + @"attributetype": @"attributeType", + @"basefrequency": @"baseFrequency", + @"baseprofile": @"baseProfile", + @"calcmode": @"calcMode", + @"clippathunits": @"clipPathUnits", + @"diffuseconstant": @"diffuseConstant", + @"edgemode": @"edgeMode", + @"filterunits": @"filterUnits", + @"glyphref": @"glyphRef", + @"gradienttransform": @"gradientTransform", + @"gradientunits": @"gradientUnits", + @"kernelmatrix": @"kernelMatrix", + @"kernelunitlength": @"kernelUnitLength", + @"keypoints": @"keyPoints", + @"keysplines": @"keySplines", + @"keytimes": @"keyTimes", + @"lengthadjust": @"lengthAdjust", + @"limitingconeangle": @"limitingConeAngle", + @"markerheight": @"markerHeight", + @"markerunits": @"markerUnits", + @"markerwidth": @"markerWidth", + @"maskcontentunits": @"maskContentUnits", + @"maskunits": @"maskUnits", + @"numoctaves": @"numOctaves", + @"pathlength": @"pathLength", + @"patterncontentunits": @"patternContentUnits", + @"patterntransform": @"patternTransform", + @"patternunits": @"patternUnits", + @"pointsatx": @"pointsAtX", + @"pointsaty": @"pointsAtY", + @"pointsatz": @"pointsAtZ", + @"preservealpha": @"preserveAlpha", + @"preserveaspectratio": @"preserveAspectRatio", + @"primitiveunits": @"primitiveUnits", + @"refx": @"refX", + @"refy": @"refY", + @"repeatcount": @"repeatCount", + @"repeatdur": @"repeatDur", + @"requiredextensions": @"requiredExtensions", + @"requiredfeatures": @"requiredFeatures", + @"specularconstant": @"specularConstant", + @"specularexponent": @"specularExponent", + @"spreadmethod": @"spreadMethod", + @"startoffset": @"startOffset", + @"stddeviation": @"stdDeviation", + @"stitchtiles": @"stitchTiles", + @"surfacescale": @"surfaceScale", + @"systemlanguage": @"systemLanguage", + @"tablevalues": @"tableValues", + @"targetx": @"targetX", + @"targety": @"targetY", + @"textlength": @"textLength", + @"viewbox": @"viewBox", + @"viewtarget": @"viewTarget", + @"xchannelselector": @"xChannelSelector", + @"ychannelselector": @"yChannelSelector", + @"zoomandpan": @"zoomAndPan"}; + + HTMLOrderedDictionary *adjusted = [HTMLOrderedDictionary new]; + for (id key in token.attributes) { + NSString *replacement = replacements[key] ?: key; + adjusted[replacement] = token.attributes[key]; + } + token.attributes = adjusted; +} + +NS_INLINE void AdjustSVGNameCase(HTMLTagToken *token) +{ + NSDictionary *replacements = @{ + @"altglyph": @"altGlyph", + @"altglyphdef": @"altGlyphDef", + @"altglyphitem": @"altGlyphItem", + @"animatecolor": @"animateColor", + @"animatemotion": @"animateMotion", + @"animatetransform": @"animateTransform", + @"clippath": @"clipPath", + @"feblend": @"feBlend", + @"fecolormatrix": @"feColorMatrix", + @"fecomponenttransfer": @"feComponentTransfer", + @"fecomposite": @"feComposite", + @"feconvolvematrix": @"feConvolveMatrix", + @"fediffuselighting": @"feDiffuseLighting", + @"fedisplacementmap": @"feDisplacementMap", + @"fedistantlight": @"feDistantLight", + @"fedropshadow": @"feDropShadow", + @"feflood": @"feFlood", + @"fefunca": @"feFuncA", + @"fefuncb": @"feFuncB", + @"fefuncg": @"feFuncG", + @"fefuncr": @"feFuncR", + @"fegaussianblur": @"feGaussianBlur", + @"feimage": @"feImage", + @"femerge": @"feMerge", + @"femergenode": @"feMergeNode", + @"femorphology": @"feMorphology", + @"feoffset": @"feOffset", + @"fepointlight": @"fePointLight", + @"fespecularlighting": @"feSpecularLighting", + @"fespotlight": @"feSpotLight", + @"fetile": @"feTile", + @"feturbulence": @"feTurbulence", + @"foreignobject": @"foreignObject", + @"glyphref": @"glyphRef", + @"lineargradient": @"linearGradient", + @"radialgradient": @"radialGradient", + @"textpath": @"textPath"}; + + NSString *replacement = replacements[token.tagName] ?: token.tagName; + token.tagName = replacement; +} + +NS_INLINE void AdjustForeignAttributes(HTMLTagToken *token) +{ + if (token.attributes == nil) { + return; + } + + NSDictionary *replacements = @{ @"xlink:actuate": @"xlink actuate", + @"xlink:arcrole": @"xlink arcrole", + @"xlink:href": @"xlink href", + @"xlink:role": @"xlink role", + @"xlink:show": @"xlink show", + @"xlink:title": @"xlink title", + @"xlink:type": @"xlink type", + @"xml:base": @"xml base", + @"xml:lang": @"xml lang", + @"xml:space": @"xml space", + @"xmlns": @"xmlns", + @"xmlns:xlink": @"xmlns xlink"}; + + HTMLOrderedDictionary *adjusted = [HTMLOrderedDictionary new]; + for (id key in token.attributes) { + NSString *replacement = replacements[key] ?: key; + adjusted[replacement] = token.attributes[key]; + } + token.attributes = adjusted; +} + diff --git a/HTMLKit/HTMLElementTypes.h b/HTMLKit/HTMLElementTypes.h new file mode 100644 index 0000000..44be082 --- /dev/null +++ b/HTMLKit/HTMLElementTypes.h @@ -0,0 +1,50 @@ +// +// HTMLElementTypes.h +// HTMLKit +// +// Created by Iska on 19/02/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLElement.h" +#import "HTMLNamespaces.h" +#import "NSString+HTMLKit.h" + +NS_INLINE BOOL IsNodeMathMLTextIntegrationPoint(HTMLElement *node) +{ + return (node.htmlNamespace == HTMLNamespaceMathML && [node.tagName isEqualToAny:@"mi", @"mo", @"mn", @"ms", @"mtext", nil]); +} + +NS_INLINE BOOL IsNodeHTMLIntegrationPoint(HTMLElement *node) +{ + if (node.htmlNamespace == HTMLNamespaceMathML && [node.tagName isEqualToString:@"annotation-xml"]) { + NSString *encoding = node.attributes[@"encoding"]; + return [encoding isEqualToStringIgnoringCase:@"text/html"] || [encoding isEqualToStringIgnoringCase:@"application/xhtml+xml"]; + } else if (node.htmlNamespace == HTMLNamespaceSVG) { + return [node.tagName isEqualToAny:@"foreignObject", @"desc", @"title", nil]; + } + return NO; +} + +NS_INLINE BOOL IsSpecialElement(HTMLElement *element) +{ + if (element.htmlNamespace == HTMLNamespaceHTML) { + return [element.tagName isEqualToAny:@"address", @"applet", @"area", @"article", + @"aside", @"base", @"basefont", @"bgsound", @"blockquote", @"body", @"br", + @"button", @"caption", @"center", @"col", @"colgroup", @"dd", @"details", + @"dir", @"div", @"dl", @"dt", @"embed", @"fieldset", @"figcaption", + @"figure", @"footer", @"form", @"frame", @"frameset", @"h1", @"h2", @"h3", + @"h4", @"h5", @"h6", @"head", @"header", @"hgroup", @"hr", @"html", @"iframe", + @"img", @"input", @"isindex", @"li", @"link", @"listing", @"main", @"marquee", + @"menu", @"menuitem", @"meta", @"nav", @"noembed", @"noframes", @"noscript", + @"object", @"ol", @"p", @"param", @"plaintext", @"pre", @"script", @"section", + @"select", @"source", @"style", @"summary", @"table", @"tbody", @"td", + @"template", @"textarea", @"tfoot", @"th", @"thead", @"title", @"tr", + @"track", @"ul", @"wbr", @"xmp", nil]; + } else if (element.htmlNamespace == HTMLNamespaceMathML) { + return [element.tagName isEqualToAny:@"mi", @"mo", @"mn", @"ms", @"mtext", @"annotation-xml", nil]; + } else if (element.htmlNamespace == HTMLNamespaceSVG) { + return [element.tagName isEqualToAny:@"foreignObject", @"desc", @"title", nil]; + } + return NO; +} diff --git a/HTMLKit/HTMLInputStreamReader.h b/HTMLKit/HTMLInputStreamReader.h new file mode 100644 index 0000000..1ffdce8 --- /dev/null +++ b/HTMLKit/HTMLInputStreamReader.h @@ -0,0 +1,44 @@ +// +// HTMLInputStreamReader.h +// HTMLKit +// +// Created by Iska on 15/09/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import + +typedef void (^ HTMLStreamReaderErrorCallback)(NSString *reason); + +/** + * HTML Input Stream Reader processor conforming to the HTML standard + * http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#preprocessing-the-input-stream + */ +@interface HTMLInputStreamReader : NSObject + +@property (nonatomic, readonly) NSString *string; +@property (nonatomic, readonly) NSUInteger currentLocation; +@property (nonatomic, copy) HTMLStreamReaderErrorCallback errorCallback; + +- (id)initWithString:(NSString *)string; + +- (UTF32Char)currentInputCharacter; +- (UTF32Char)nextInputCharacter; + +- (UTF32Char)consumeNextInputCharacter; +- (void)reconsumeCurrentInputCharacter; +- (void)unconsumeCurrentInputCharacter; + +- (BOOL)consumeCharacter:(UTF32Char)character; +- (BOOL)consumeNumber:(unsigned long long *)result; +- (BOOL)consumeHexNumber:(unsigned long long *)result; +- (BOOL)consumeString:(NSString *)string caseSensitive:(BOOL)caseSensitive; +- (NSString *)consumeCharactersUpToCharactersInString:(NSString *)characters; +- (NSString *)consumeCharactersUpToString:(NSString *)string; +- (NSString *)consumeAlphanumericCharacters; + +- (void)markCurrentLocation; +- (void)rewindToMarkedLocation; +- (void)reset; + +@end diff --git a/HTMLKit/HTMLInputStreamReader.m b/HTMLKit/HTMLInputStreamReader.m new file mode 100644 index 0000000..b2fcb88 --- /dev/null +++ b/HTMLKit/HTMLInputStreamReader.m @@ -0,0 +1,238 @@ +// +// HTMLInputStreamReader.m +// HTMLKit +// +// Created by Iska on 15/09/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import "HTMLInputStreamReader.h" +#import "HTMLTokenizerCharacters.h" + +#pragma mark - HTMLInputStreamReader + +@interface HTMLInputStreamReader () +{ + NSString *_string; + NSScanner *_scanner; + CFStringInlineBuffer _buffer; + NSUInteger _location; + NSUInteger _mark; + UTF32Char _currentInputCharacter; + NSUInteger _consume; + HTMLStreamReaderErrorCallback _errorCallback; + + BOOL _reconsume; +} +@end + +@implementation HTMLInputStreamReader +@synthesize string = _string; +@synthesize currentLocation = _location; +@synthesize errorCallback = _errorCallback; + +#pragma mark - Lifecycle + +- (id)initWithString:(NSString *)string +{ + self = [super init]; + if (self) { + _string = [string copy]; + _scanner = [[NSScanner alloc] initWithString:string]; + CFStringInitInlineBuffer((CFStringRef)_string, &_buffer, CFRangeMake(0, _string.length)); + } + return self; +} + +#pragma mark - Errors + +- (void)emitParseError:(NSString *)reason +{ + if (self.errorCallback) { + self.errorCallback(reason); + } +} + +#pragma mark - Stream Processing + +- (UTF32Char)currentInputCharacter +{ + return _currentInputCharacter; +} + +- (UTF32Char)nextInputCharacter +{ + if (_reconsume) { + _reconsume = NO; + return _currentInputCharacter; + } + + _consume = 0; + UTF32Char nextInputCharacter = CFStringGetCharacterFromInlineBuffer(&_buffer, _location); + + if (nextInputCharacter == 0 && _location >= _string.length) return EOF; + + _consume = 1; + if (nextInputCharacter == CARRIAGE_RETURN) { + UniChar next = CFStringGetCharacterFromInlineBuffer(&_buffer, _location + 1); + if (next == LINE_FEED) { + _consume = 2; + } + return LINE_FEED; + } + if (CFStringIsSurrogateLowCharacter(nextInputCharacter)) { + NSString *reason = [NSString stringWithFormat:@"Non-Unicode character found (an isolated low surrogate: 0x%X)", nextInputCharacter]; + [self emitParseError:reason]; + return nextInputCharacter; + } + + if (CFStringIsSurrogateHighCharacter(nextInputCharacter)) { + UniChar surrogateLow = CFStringGetCharacterFromInlineBuffer(&_buffer, _location + 1); + if (CFStringIsSurrogateLowCharacter(surrogateLow) == NO) { + NSString *reason = [NSString stringWithFormat:@"Non-Unicode character found (an isolated high surrogate: 0x%X)", nextInputCharacter]; + [self emitParseError:reason]; + return nextInputCharacter; + } + + _consume = 2; + nextInputCharacter = CFStringGetLongCharacterForSurrogatePair(nextInputCharacter, surrogateLow); + } + + if (isControlOrUndefinedCharacter(nextInputCharacter)) { + NSString *reason = [NSString stringWithFormat:@"A control/undefined character found: (0x%X)", nextInputCharacter]; + [self emitParseError:reason]; + } + + return nextInputCharacter; +} + +- (UTF32Char)consumeNextInputCharacter +{ + if (_reconsume) { + _reconsume = NO; + return _currentInputCharacter; + } + + UTF32Char nextInputCharacter = [self nextInputCharacter]; + _location += _consume; + _scanner.scanLocation = _location; + _currentInputCharacter = nextInputCharacter; + return nextInputCharacter; +} + +- (BOOL)consumeCharacter:(UTF32Char)character +{ + UTF32Char nextInputCharacter = [self nextInputCharacter]; + if (nextInputCharacter == character) { + _location += _consume; + _scanner.scanLocation = _location; + _currentInputCharacter = nextInputCharacter; + return YES; + } + return NO; +} + +- (BOOL)consumeNumber:(unsigned long long *)result +{ + unsigned long long scanned; + BOOL success = [_scanner scanUnsignedLongLong:&scanned]; + if (success == NO) return NO; + + *result = scanned; + _location = _scanner.scanLocation; + return success; +} + +- (BOOL)consumeHexNumber:(unsigned long long *)result +{ + NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEFabcdef"]; + + NSString *string = nil; + BOOL success = [_scanner scanCharactersFromSet:set intoString:&string]; + if (success == NO) return NO; + + unsigned long long scanned = strtoull(string.UTF8String, NULL, 16); + *result = scanned; + _location = _scanner.scanLocation; + return success; +} + +- (BOOL)consumeString:(NSString *)string caseSensitive:(BOOL)caseSensitive +{ + _scanner.caseSensitive = caseSensitive; + BOOL success = [_scanner scanString:string intoString:nil]; + _location = _scanner.scanLocation; + return success; +} + +- (NSString *)consumeCharactersUpToCharactersInString:(NSString *)characters +{ + NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:characters]; + + NSMutableString *consumed = [NSMutableString string]; + + while (YES) { + UTF32Char nextCharacter = [self consumeNextInputCharacter]; + if ([set longCharacterIsMember:nextCharacter] || nextCharacter == EOF) { + break; + } + [consumed appendString:StringFromUTF32Char(nextCharacter)]; + } + [self unconsumeCurrentInputCharacter]; + + return consumed.length > 0 ? consumed : nil; +} + +- (NSString *)consumeCharactersUpToString:(NSString *)string +{ + NSString *consumed; + [_scanner scanUpToString:string intoString:&consumed]; + _location = _scanner.scanLocation; + consumed = [consumed stringByReplacingOccurrencesOfString:@"\r\n" withString:@"\r"]; + consumed = [consumed stringByReplacingOccurrencesOfString:@"\r" withString:@"\n"]; + return consumed; +} + +- (NSString *)consumeAlphanumericCharacters +{ + NSCharacterSet *set = [NSCharacterSet alphanumericCharacterSet]; + NSString *consumed = nil; + + [_scanner scanCharactersFromSet:set intoString:&consumed]; + _location = _scanner.scanLocation; + return consumed; +} + +- (void)reconsumeCurrentInputCharacter +{ + _reconsume = YES; +} + +- (void)unconsumeCurrentInputCharacter +{ + _location -= _consume; + _scanner.scanLocation = _location; + _consume = 0; +} + +- (void)markCurrentLocation +{ + _mark = _location; +} + +- (void)rewindToMarkedLocation +{ + _location = _mark; + _scanner.scanLocation = _mark; + _consume = 0; +} + +- (void)reset +{ + _mark = 0; + _location = 0; + _scanner.scanLocation = 0; + _consume = 0; +} + +@end diff --git a/HTMLKit/HTMLKit-Info.plist b/HTMLKit/HTMLKit-Info.plist new file mode 100644 index 0000000..5853e0c --- /dev/null +++ b/HTMLKit/HTMLKit-Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.braincookie.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + NSHumanReadableCopyright + Copyright © 2014 BrainCookie. All rights reserved. + NSPrincipalClass + + + diff --git a/HTMLKit/HTMLKit-Prefix.pch b/HTMLKit/HTMLKit-Prefix.pch new file mode 100644 index 0000000..37fdbe6 --- /dev/null +++ b/HTMLKit/HTMLKit-Prefix.pch @@ -0,0 +1,9 @@ +// +// Prefix header +// +// The contents of this file are implicitly included at the beginning of every source file. +// + +#ifdef __OBJC__ + #import +#endif diff --git a/HTMLKit/HTMLKit.h b/HTMLKit/HTMLKit.h new file mode 100644 index 0000000..3b5c810 --- /dev/null +++ b/HTMLKit/HTMLKit.h @@ -0,0 +1,13 @@ +// +// HTMLKit.h +// HTMLKit +// +// Created by Iska on 15/09/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import + +@interface HTMLKit : NSObject + +@end diff --git a/HTMLKit/HTMLKit.m b/HTMLKit/HTMLKit.m new file mode 100644 index 0000000..da149aa --- /dev/null +++ b/HTMLKit/HTMLKit.m @@ -0,0 +1,13 @@ +// +// HTMLKit.m +// HTMLKit +// +// Created by Iska on 15/09/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import "HTMLKit.h" + +@implementation HTMLKit + +@end diff --git a/HTMLKit/HTMLKitExceptions.h b/HTMLKit/HTMLKitExceptions.h new file mode 100644 index 0000000..4520eb0 --- /dev/null +++ b/HTMLKit/HTMLKitExceptions.h @@ -0,0 +1,11 @@ +// +// HTMLKitExceptions.h +// HTMLKit +// +// Created by Iska on 17/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +FOUNDATION_EXPORT NSString * const HTMLKitHierarchyRequestError; +FOUNDATION_EXPORT NSString * const HTMLKitNotFoundError; +FOUNDATION_EXPORT NSString * const HTMLKitNotSupportedError; diff --git a/HTMLKit/HTMLKitExceptions.m b/HTMLKit/HTMLKitExceptions.m new file mode 100644 index 0000000..8234e00 --- /dev/null +++ b/HTMLKit/HTMLKitExceptions.m @@ -0,0 +1,13 @@ +// +// HTMLKitExceptions.m +// HTMLKit +// +// Created by Iska on 17/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLKitExceptions.h" + +NSString * const HTMLKitHierarchyRequestError = @"HierarchyRequestError"; +NSString * const HTMLKitNotFoundError = @"NotFoundError"; +NSString * const HTMLKitNotSupportedError = @"NotSupportedError"; diff --git a/HTMLKit/HTMLListOfActiveFormattingElements.h b/HTMLKit/HTMLListOfActiveFormattingElements.h new file mode 100644 index 0000000..9b7d7b4 --- /dev/null +++ b/HTMLKit/HTMLListOfActiveFormattingElements.h @@ -0,0 +1,38 @@ +// +// HTMLListOfActiveFormattingElements.h +// HTMLKit +// +// Created by Iska on 22/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import +#import "HTMLElement.h" + +@interface HTMLListOfActiveFormattingElements : NSObject + +- (id)objectAtIndexedSubscript:(NSUInteger)index; +- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx; +- (NSUInteger)indexOfElement:(id)node; + +- (void)addElement:(HTMLElement *)element; +- (void)removeElement:(id)element; +- (BOOL)containsElement:(id)element; + +- (void)insertElement:(HTMLElement *)element atIndex:(NSUInteger)index; +- (void)replaceElementAtIndex:(NSUInteger)index withElement:(HTMLElement *)element; + +- (id)lastEntry; + +- (void)addMarker; +- (void)clearUptoLastMarker; + +- (HTMLElement *)formattingElementWithTagName:(NSString *)tagName; + +- (NSUInteger)count; +- (BOOL)isEmpty; + +- (NSEnumerator *)enumerator; +- (NSEnumerator *)reverseObjectEnumerator; + +@end diff --git a/HTMLKit/HTMLListOfActiveFormattingElements.m b/HTMLKit/HTMLListOfActiveFormattingElements.m new file mode 100644 index 0000000..97092c7 --- /dev/null +++ b/HTMLKit/HTMLListOfActiveFormattingElements.m @@ -0,0 +1,158 @@ +// +// HTMLListOfActiveFormattingElements.m +// HTMLKit +// +// Created by Iska on 22/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLListOfActiveFormattingElements.h" +#import "HTMLMarker.h" + +@interface HTMLListOfActiveFormattingElements () +{ + NSMutableArray *_list; +} +@end + +@implementation HTMLListOfActiveFormattingElements + +- (instancetype)init +{ + self = [super init]; + if (self) { + _list = [NSMutableArray new]; + } + return self; +} + +#pragma mark - Access + +- (id)objectAtIndexedSubscript:(NSUInteger)index; +{ + return [_list objectAtIndex:index]; +} + +- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx +{ + [_list setObject:obj atIndexedSubscript:idx]; +} + +- (NSUInteger)indexOfElement:(id)node +{ + return [_list indexOfObject:node]; +} + +- (void)addElement:(HTMLElement *)element +{ + NSUInteger existing = 0; + for (HTMLElement *node in _list.reverseObjectEnumerator.allObjects) { + if ([node isEqual:[HTMLMarker marker]]) { + break; + } + if (node.htmlNamespace == element.htmlNamespace && + [node.tagName isEqualToString:element.tagName] && + [node.attributes isEqualTo:element.attributes]) { + existing++; + } + if (existing == 3) { + [_list removeObject:node]; + break; + } + } + [_list addObject:element]; +} + +- (void)removeElement:(id)element +{ + [_list removeObject:element]; +} + +- (BOOL)containsElement:(id)element +{ + return [_list containsObject:element]; +} + +- (void)insertElement:(HTMLElement *)element atIndex:(NSUInteger)index +{ + if (index > _list.count) { + index = _list.count; + } + [_list insertObject:element atIndex:index]; +} + +- (void)replaceElementAtIndex:(NSUInteger)index withElement:(HTMLElement *)element +{ + [_list replaceObjectAtIndex:index withObject:element]; +} + +- (id)lastEntry +{ + return _list.lastObject; +} + +#pragma mark - Acrions + +- (void)addMarker +{ + [_list addObject:[HTMLMarker marker]]; +} + +- (void)clearUptoLastMarker +{ + while (_list.lastObject && _list.lastObject != [HTMLMarker marker]) { + [_list removeLastObject]; + } + [_list removeLastObject]; +} + +- (HTMLElement *)formattingElementWithTagName:(NSString *)tagName +{ + for (HTMLElement *element in _list.reverseObjectEnumerator) { + if ([element isEqual:[HTMLMarker marker]]) return nil; + if ([element.tagName isEqualToString:tagName]) { + return element; + } + } + return nil; +} + +#pragma mark - Count + +- (NSUInteger)count +{ + return _list.count; +} + +- (BOOL)isEmpty +{ + return _list.count == 0; +} + +#pragma mark - Enumeraiton + +- (NSEnumerator *)enumerator +{ + return _list.objectEnumerator; +} + +- (NSEnumerator *)reverseObjectEnumerator +{ + return _list.reverseObjectEnumerator; +} + +#pragma mark - NSFastEnumeration + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len +{ + return [_list countByEnumeratingWithState:state objects:buffer count:len]; +} + +#pragma mark - Description + +- (NSString *)description +{ + return _list.description; +} + +@end 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/HTMLNamespaces.h b/HTMLKit/HTMLNamespaces.h new file mode 100644 index 0000000..1c37454 --- /dev/null +++ b/HTMLKit/HTMLNamespaces.h @@ -0,0 +1,17 @@ +// +// HTMLNamespaces.h +// HTMLKit +// +// Created by Iska on 03/11/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +typedef NS_ENUM(NSInteger, HTMLNamespace) +{ + HTMLNamespaceHTML, + HTMLNamespaceMathML, + HTMLNamespaceSVG, + HTMLNamespaceXLink, + HTMLNamespaceXML, + HTMLNamespaceXMLNS, +}; diff --git a/HTMLKit/HTMLNode.h b/HTMLKit/HTMLNode.h new file mode 100644 index 0000000..00d1f08 --- /dev/null +++ b/HTMLKit/HTMLNode.h @@ -0,0 +1,98 @@ +// +// HTMLNode.h +// HTMLKit +// +// Created by Iska on 24/02/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import + +typedef NS_ENUM(short, HTMLNodeType) +{ + HTMLNodeElement = 1, + HTMLNodeAttribute = 2, // historical + HTMLNodeText = 3, + HTMLNodeCDATASection = 4, // historical + HTMLNodeEntityReference = 5, // historical + HTMLNodeEntity = 6, // historical + HTMLNodeProcessingInstruction = 7, + HTMLNodeComment = 8, + HTMLNodeDocument = 9, + HTMLNodeDocumentType = 10, + HTMLNodeDocumentFragment = 11, + HTMLNodeNotation = 12 // historical +}; + +@class HTMLDocument; +@class HTMLElement; + +@interface HTMLNode : NSObject + +@property (nonatomic, assign, readonly) HTMLNodeType type; + +@property (nonatomic, strong, readonly) NSString *name; + +@property (nonatomic, weak, readonly) HTMLDocument *ownerDocument; + +@property (nonatomic, strong, readonly) NSString *baseURI; + +@property (nonatomic, weak, readonly) HTMLNode *parentNode; + +@property (nonatomic, weak, readonly) HTMLElement *parentElement; + +@property (nonatomic, strong, readonly) NSOrderedSet *childNodes; + +@property (nonatomic, strong, readonly) HTMLNode *firstChiledNode; + +@property (nonatomic, strong, readonly) HTMLNode *lastChildNode; + +@property (nonatomic, strong, readonly) HTMLNode *previousSibling; + +@property (nonatomic, strong, readonly) HTMLNode *nextSibling; + +@property (nonatomic, copy) NSString *textContent; + +- (instancetype)initWithName:(NSString *)name type:(HTMLNodeType)type; + +- (BOOL)hasChildNodes; + +- (BOOL)hasChildNodeOfType:(HTMLNodeType)type; + +- (NSUInteger)childNodesCount; + +- (HTMLNode *)childNodeAtIndex:(NSUInteger)index; + +- (NSUInteger)indexOfChildNode:(HTMLNode *)node; + +- (HTMLNode *)insertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child; + +- (HTMLNode *)appendNode:(HTMLNode *)node; + +- (void)appendNodes:(NSArray *)nodes; + +- (HTMLNode *)replaceChildNode:(HTMLNode *)node withNode:(HTMLNode *)replacement; + +- (void)replaceAllChildNodesWithNode:(HTMLNode *)node; + +- (HTMLNode *)removeChildNode:(HTMLNode *)node; + +- (HTMLNode *)removeChildNodeAtIndex:(NSUInteger)index; + +- (void)reparentChildNodesIntoNode:(HTMLNode *)node; + +- (void)removeAllChildNodes; + +- (void)enumerateChildNodesUsingBlock:(void (^)(HTMLNode *node, NSUInteger idx, BOOL *stop))block; + +- (NSEnumerator *)treeEnumerator; + +- (NSEnumerator *)reverseTreeEnumerator; + +- (NSString *)outerHTML; + +- (NSString *)innerHTML; + +- (NSString *)treeDescription; + +@end diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m new file mode 100644 index 0000000..368a196 --- /dev/null +++ b/HTMLKit/HTMLNode.m @@ -0,0 +1,482 @@ +// +// HTMLNode.m +// HTMLKit +// +// Created by Iska on 24/02/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLNode.h" +#import "HTMLDocument.h" +#import "HTMLDocumentType.h" +#import "HTMLElement.h" +#import "HTMLText.h" +#import "HTMLComment.h" +#import "HTMLKitExceptions.h" +#import "HTMLNodeTreeEnumerator.h" + +@interface HTMLNode () +{ + NSMutableOrderedSet *_childNodes; +} +@property (nonatomic, weak) HTMLDocument *ownerDocument; +@end + +@implementation HTMLNode +@synthesize ownerDocument = _ownerDocument; + +#pragma mark - Init + +- (instancetype)initWithName:(NSString *)name type:(HTMLNodeType)type +{ + self = [super init]; + if (self) { + _name = name; + _type = type; + _childNodes = [NSMutableOrderedSet new]; + } + return self; +} + +#pragma mark - Properties + +- (HTMLDocument *)ownerDocument +{ + if (_type == HTMLNodeDocument) { + return (HTMLDocument *)self; + } else { + return _ownerDocument; + } +} + +- (void)setOwnerDocument:(HTMLDocument *)ownerDocument +{ + _ownerDocument = ownerDocument; + [self.childNodes.array makeObjectsPerformSelector:@selector(setOwnerDocument:) withObject:ownerDocument]; +} + +- (void)setBaseURI:(NSString *)baseURI +{ + _baseURI = [baseURI copy]; + [self.childNodes.array makeObjectsPerformSelector:@selector(setBaseURI:) withObject:baseURI]; +} + +- (void)setParentNode:(HTMLNode *)parentNode +{ + _parentNode = parentNode; +} + +- (HTMLElement *)parentElement +{ + return _parentNode.type == HTMLNodeElement ? (HTMLElement *)_parentNode : nil; +} + +- (HTMLNode *)firstChiledNode +{ + return self.childNodes.firstObject; +} + +- (HTMLNode *)lastChildNode +{ + return self.childNodes.lastObject; +} + +- (HTMLNode *)previousSibling +{ + NSUInteger index = [_parentNode indexOfChildNode:self]; + if (index <= 0) { + return nil; + } + return [_parentNode childNodeAtIndex:index - 1]; +} + +- (HTMLNode *)nextSibling +{ + NSUInteger index = [_parentNode indexOfChildNode:self]; + if (index >= _parentNode.childNodesCount - 1) { + return nil; + } + return [_parentNode childNodeAtIndex:index + 1]; +} + +- (NSString *)textContent +{ + return nil; +} + +#pragma mark - Child Nodes + +- (BOOL)hasChildNodes +{ + return self.childNodes.count > 0; +} + +- (BOOL)hasChildNodeOfType:(HTMLNodeType)type +{ + NSUInteger index = [self.childNodes indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { + if ([(HTMLNode *)obj type] == type) { + *stop = YES; + return YES; + } + return NO; + }]; + + return index != NSNotFound; +} + +- (NSUInteger)childNodesCount +{ + return self.childNodes.count; +} + +- (HTMLNode *)childNodeAtIndex:(NSUInteger)index +{ + return [self.childNodes objectAtIndex:index]; +} + +- (NSUInteger)indexOfChildNode:(HTMLNode *)node +{ + return [self.childNodes indexOfObject:node]; +} + +- (HTMLNode *)insertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child +{ + node = [self preInsertNode:node beforeChildNode:child]; + node.parentNode = self; + return node; +} + +- (HTMLNode *)appendNode:(HTMLNode *)node +{ + node = [self preInsertNode:node beforeChildNode:nil]; + node.parentNode = self; + return node; +} + +- (void)appendNodes:(NSArray *)nodes +{ + for (id node in nodes) { + [self appendNode:node]; + } +} + +- (HTMLNode *)replaceChildNode:(HTMLNode *)child withNode:(HTMLNode *)node +{ + [self ensureReplacementValidityOfChildNode:child withNode:node]; + + [self.ownerDocument adoptNode:node]; + NSUInteger index = [self indexOfChildNode:child]; + node.parentNode = self; + [(NSMutableOrderedSet *)self.childNodes replaceObjectAtIndex:index withObject:node]; + return child; +} + +- (void)replaceAllChildNodesWithNode:(HTMLNode *)node +{ + [self removeAllChildNodes]; + + if (node != nil) { + [self.ownerDocument adoptNode:node]; + [self insertNode:node beforeChildNode:nil]; + } +} + +- (HTMLNode *)removeChildNode:(HTMLNode *)child +{ + if (child.parentNode != self) { + [NSException raise:HTMLKitNotFoundError + format:@"%@: Not Fount Error, removing non-child node %@. The object can not be found here.", + NSStringFromSelector(_cmd), child]; + } + + [(NSMutableOrderedSet *)self.childNodes removeObject:child]; + child.parentNode = nil; + return child; +} + +- (HTMLNode *)removeChildNodeAtIndex:(NSUInteger)index +{ + HTMLNode *node = [self childNodeAtIndex:index]; + return [self removeChildNode:node]; +} + +- (void)reparentChildNodesIntoNode:(HTMLNode *)node +{ + for (HTMLNode *child in self.childNodes.array) { + [node appendNode:child]; + } + [self removeAllChildNodes]; +} + +- (void)removeAllChildNodes +{ + [(NSMutableOrderedSet *)self.childNodes removeAllObjects]; +} + +#pragma mark - Enumeration + +- (void)enumerateChildNodesUsingBlock:(void (^)(HTMLNode *node, NSUInteger idx, BOOL *stop))block +{ + if (block == nil) { + return; + } + + [self.childNodes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + block(obj, idx, stop); + }]; +} + +- (NSEnumerator *)treeEnumerator +{ + return [[HTMLNodeTreeEnumerator alloc] initWithNode:self reverse:NO]; +} + +- (NSEnumerator *)reverseTreeEnumerator +{ + return [[HTMLNodeTreeEnumerator alloc] initWithNode:self reverse:YES]; +} + +#pragma mark - Mutation Algorithms + +- (HTMLNode *)preInsertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child +{ + [self ensurePreInsertionValidityOfNode:node beforeChildNode:child]; + [self.ownerDocument adoptNode:node]; + NSUInteger index = [self indexOfChildNode:child]; + if (index != NSNotFound) { + [(NSMutableOrderedSet *)self.childNodes insertObject:node atIndex:index]; + } else { + [(NSMutableOrderedSet *)self.childNodes addObject:node]; + } + + return node; +} + +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.", + cmd, node.name]; + } +} + +NS_INLINE void CheckChildsParent(HTMLNode *parent, HTMLNode *child, NSString *cmd) +{ + if (child != nil && + child.parentNode != parent) { + [NSException raise:HTMLKitNotFoundError + format:@"%@: Not Fount Error, insering before non-child node %@. The object can not be found here.", + cmd, child]; + } +} + +NS_INLINE void CheckInsertedNodeValid(HTMLNode *node, NSString *cmd) +{ + if (node.type != HTMLNodeDocumentFragment && + node.type != HTMLNodeDocumentType && + node.type != HTMLNodeElement && + node.type != HTMLNodeText && + node.type != HTMLNodeComment) { + [NSException raise:HTMLKitHierarchyRequestError + format:@"%@: Hierarchy Request Error, inserting a %@ is not allowed. The operation would yield an incorrect node tree.", + cmd, node.name]; + } +} + +NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSString *cmd) +{ + if (node.type == HTMLNodeText && parent.type == HTMLNodeDocument) { + [NSException raise:HTMLKitHierarchyRequestError + format:@"%@: Hierarchy Request Error, inserting a text node %@ into docuement is not allowed. The operation would yield an incorrect node tree.", + cmd, parent.name]; + } + + if (node.type == HTMLNodeDocumentType && parent.type != HTMLNodeDocument) { + [NSException raise:HTMLKitHierarchyRequestError + format:@"%@: Hierarchy Request Error, inserting a doctype %@ into a non-document node is not allowed. The operation would yield an incorrect node tree.", + cmd, parent.name]; + } +} + +- (void)ensurePreInsertionValidityOfNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child +{ + CheckParentValid(self, NSStringFromSelector(_cmd)); + + CheckChildsParent(self, child, NSStringFromSelector(_cmd)); + + CheckInsertedNodeValid(node, NSStringFromSelector(_cmd)); + + CheckInvalidCombination(self, node, NSStringFromSelector(_cmd)); + + void (^ hierarchyError)() = ^{ + [NSException raise:HTMLKitHierarchyRequestError + format:@"%@: Hierarchy Request Error. The operation would yield an incorrect node tree.", + NSStringFromSelector(_cmd)]; + }; + + 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 || + (child != nil && child.nextSibling.type == HTMLNodeDocumentType)) { + hierarchyError(); + } + break; + case HTMLNodeDocumentType: + if ([self hasChildNodeOfType:HTMLNodeDocumentType] || + child.previousSibling != nil || + (child == nil && [self hasChildNodeOfType:HTMLNodeElement])) { + hierarchyError(); + } + break; + default: + break; + } + } +} + +- (void)ensureReplacementValidityOfChildNode:(HTMLNode *)child withNode:(HTMLNode *)node +{ + CheckParentValid(self, NSStringFromSelector(_cmd)); + + CheckChildsParent(self, child, NSStringFromSelector(_cmd)); + + CheckInsertedNodeValid(node, NSStringFromSelector(_cmd)); + + CheckInvalidCombination(self, node, NSStringFromSelector(_cmd)); + + void (^ hierarchyError)() = ^{ + [NSException raise:HTMLKitHierarchyRequestError + format:@"%@: Hierarchy Request Error. The operation would yield an incorrect node tree.", + NSStringFromSelector(_cmd)]; + }; + + 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) { + hierarchyError(); + } + [self enumerateChildNodesUsingBlock:^(HTMLNode *node, NSUInteger idx, BOOL *stop) { + if (node.type == HTMLNodeElement && node != child) { + *stop = YES; + hierarchyError(); + } + }]; + break; + } + case HTMLNodeDocumentType: + { + if (child.previousSibling.type == HTMLNodeElement) { + hierarchyError(); + } + [self enumerateChildNodesUsingBlock:^(HTMLNode *node, NSUInteger idx, BOOL *stop) { + if (node.type == HTMLNodeDocument && node != child) { + *stop = YES; + hierarchyError(); + } + }]; + break; + } + default: + break; + } + } +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone +{ + HTMLNode *copy = [[self.class alloc] initWithName:self.name type:self.type]; + return copy; +} + +#pragma mark - Serialization + +- (NSString *)outerHTML +{ + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (NSString *)innerHTML +{ + return [[self.childNodes.array valueForKey:@"outerHTML"] componentsJoinedByString:@""]; +} + +#pragma mark - Description + +- (NSString *)treeDescription +{ + NSMutableString *string = [NSMutableString string]; + + __weak __block void (^ weakAccumulator) (HTMLNode *, NSUInteger); + void (^ accumulator) (HTMLNode *, NSUInteger); + static NSString *prefix = @"| "; + + weakAccumulator = accumulator = ^ (HTMLNode *node, NSUInteger level) { + + NSString *indent = [prefix stringByPaddingToLength:level * 2 + prefix.length + withString:@" " + startingAtIndex:0]; + if (level > 0) { + [string appendString:@"\n"]; + } + + [string appendString:indent]; + [string appendString:node.description]; + + for (HTMLNode *child in node.childNodes) { + weakAccumulator(child, level + 1); + } + }; + accumulator(self, 0); + + return string; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: %p %@>", self.class, self, self.name]; +} + +- (NSString *)debugDescription +{ + return self.treeDescription; +} + +- (id)debugQuickLookObject +{ + return self.outerHTML; +} + +@end diff --git a/HTMLKit/HTMLNodeTreeEnumerator.h b/HTMLKit/HTMLNodeTreeEnumerator.h new file mode 100644 index 0000000..baf7243 --- /dev/null +++ b/HTMLKit/HTMLNodeTreeEnumerator.h @@ -0,0 +1,17 @@ +// +// HTMLNodeTreeEnumerator.h +// HTMLKit +// +// Created by Iska on 28/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import + +@class HTMLNode; + +@interface HTMLNodeTreeEnumerator : NSEnumerator + +- (instancetype)initWithNode:(HTMLNode *)node reverse:(BOOL)reverse; + +@end diff --git a/HTMLKit/HTMLNodeTreeEnumerator.m b/HTMLKit/HTMLNodeTreeEnumerator.m new file mode 100644 index 0000000..8f82972 --- /dev/null +++ b/HTMLKit/HTMLNodeTreeEnumerator.m @@ -0,0 +1,54 @@ +// +// HTMLNodeTreeEnumerator.m +// HTMLKit +// +// Created by Iska on 28/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLNodeTreeEnumerator.h" +#import "HTMLNode.h" + +@interface HTMLNodeTreeEnumerator () +{ + BOOL _reverse; + NSMutableArray *_stack; +} +@end + +@implementation HTMLNodeTreeEnumerator + +- (instancetype)initWithNode:(HTMLNode *)node reverse:(BOOL)reverse +{ + self = [super init]; + if (self) { + _reverse = reverse; + _stack = [[NSMutableArray alloc] initWithObjects:node, nil]; + } + return self; +} + +- (id)nextObject +{ + if (_stack.count == 0) { + return nil; + } + + HTMLNode *node = _stack.lastObject; + [_stack removeLastObject]; + + NSArray *childNodes = node.childNodes.array; + if (childNodes != nil && childNodes.count > 0) { + if (childNodes.count > 1) { + NSRange range = NSMakeRange(_reverse ? 0 : 1, childNodes.count - 1); + NSArray *rest = [childNodes subarrayWithRange:range]; + + [_stack addObjectsFromArray:_reverse ? rest : rest.reverseObjectEnumerator.allObjects]; + } + [_stack addObject:_reverse ? childNodes.lastObject : childNodes.firstObject]; + } + + return node; +} + +@end diff --git a/HTMLKit/HTMLNodes.h b/HTMLKit/HTMLNodes.h new file mode 100644 index 0000000..f0ffa0e --- /dev/null +++ b/HTMLKit/HTMLNodes.h @@ -0,0 +1,16 @@ +// +// HTMLNodes.h +// HTMLKit +// +// Created by Iska on 27/02/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLNode.h" +#import "HTMLDocument.h" +#import "HTMLDocumentType.h" +#import "HTMLElement.h" +#import "HTMLComment.h" +#import "HTMLText.h" +#import "HTMLTemplate.h" +#import "HTMLDocumentFragment.h" diff --git a/HTMLKit/HTMLOrderedDictionary.h b/HTMLKit/HTMLOrderedDictionary.h new file mode 100644 index 0000000..aad6baf --- /dev/null +++ b/HTMLKit/HTMLOrderedDictionary.h @@ -0,0 +1,25 @@ +// +// HTMLOrderedDictionary.h +// HTMLKit +// +// Created by Iska on 14/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import + +@interface HTMLOrderedDictionary : NSMutableDictionary + +- (id)objectAtIndex:(NSUInteger)index; +- (void)setObject:(id)anObject forKey:(id)aKey atIndex:(NSUInteger)index; +- (void)removeObjectAtIndex:(NSUInteger)index; +- (void)replaceKeyValueAtIndex:(NSUInteger)index withObject:(id)anObject andKey:(id)aKey; +- (void)replaceKey:(id)aKey withKey:(id)newKey; +- (NSUInteger)indexOfKey:(id)aKey; + +- (id)objectAtIndexedSubscript:(NSUInteger)index; +- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)index; + +- (NSEnumerator *)reverseKeyEnumerator; + +@end diff --git a/HTMLKit/HTMLOrderedDictionary.m b/HTMLKit/HTMLOrderedDictionary.m new file mode 100644 index 0000000..8cc03e5 --- /dev/null +++ b/HTMLKit/HTMLOrderedDictionary.m @@ -0,0 +1,157 @@ +// +// HTMLOrderedDictionary.m +// HTMLKit +// +// Created by Iska on 14/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLOrderedDictionary.h" + +@interface HTMLOrderedDictionary () +{ + NSMutableDictionary *_dictionary; + NSMutableArray *_keys; +} +@end + +@implementation HTMLOrderedDictionary + +#pragma mark - Init + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-designated-initializers" +- (instancetype)init +{ + return [self initWithCapacity:0]; +} +#pragma clang diagnostic pop + +- (instancetype)initWithCapacity:(NSUInteger)capacity +{ + self = [super init]; + if (self) { + _dictionary = [[NSMutableDictionary alloc] initWithCapacity:capacity]; + _keys = [[NSMutableArray alloc] initWithCapacity:capacity]; + } + return self; +} + +- (instancetype)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys +{ + self = [self initWithCapacity:objects.count]; + if (self) { + [objects enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + _dictionary[keys[idx]] = obj; + }]; + } + return self; +} + +#pragma mark - Access + +- (id)objectForKey:(id)aKey +{ + return _dictionary[aKey]; +} + +- (void)setObject:(id)anObject forKey:(id)aKey +{ + if (_dictionary[aKey] == nil) { + [_keys addObject:aKey]; + } + _dictionary[aKey] = anObject; +} + +- (void)removeObjectForKey:(id)aKey +{ + [_keys removeObject:aKey]; + [_dictionary removeObjectForKey:aKey]; +} + +- (NSUInteger)count +{ + return _keys.count; +} + +#pragma mark - Indexed Access + +- (id)objectAtIndex:(NSUInteger)index +{ + return _dictionary[_keys[index]]; +} + +- (void)setObject:(id)anObject forKey:(id)aKey atIndex:(NSUInteger)index +{ + if (_dictionary[aKey]) { + [_keys removeObject:aKey]; + } + [_keys insertObject:aKey atIndex:index]; + _dictionary[aKey] = anObject; +} + +- (void)removeObjectAtIndex:(NSUInteger)index +{ + if (_dictionary[_keys[index]]){ + [_dictionary removeObjectForKey:_keys[index]]; + [_keys removeObjectAtIndex:index]; + } +} + +- (void)replaceKeyValueAtIndex:(NSUInteger)index withObject:(id)anObject andKey:(id)aKey +{ + [_keys replaceObjectAtIndex:index withObject:aKey]; + _dictionary[aKey] = anObject; +} + +- (void)replaceKey:(id)aKey withKey:(id)newKey +{ + id value = _dictionary[aKey]; + if (value != nil) { + NSUInteger index = [_keys indexOfObject:aKey]; + [_keys replaceObjectAtIndex:index withObject:newKey]; + [_dictionary removeObjectForKey:aKey]; + _dictionary[newKey] = value; + } +} + +- (NSUInteger)indexOfKey:(id)aKey +{ + return [_keys indexOfObject:aKey]; +} + +#pragma mark - Subscript + +- (id)objectAtIndexedSubscript:(NSUInteger)index +{ + return _dictionary[_keys[index]]; +} + +- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)index +{ + _dictionary[_keys[index]] = obj; +} + +- (void)setObject:(id)obj forKeyedSubscript:(id)key +{ + [self setObject:obj forKey:key]; +} + +#pragma mark - Enumeration + +- (NSEnumerator *)keyEnumerator +{ + return _keys.objectEnumerator; +} + +- (NSEnumerator *)reverseKeyEnumerator +{ + return _keys.reverseObjectEnumerator; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id [])buffer count:(NSUInteger)len +{ + return [_keys countByEnumeratingWithState:state objects:buffer count:len]; +} + +@end diff --git a/HTMLKit/HTMLParseErrorToken.h b/HTMLKit/HTMLParseErrorToken.h new file mode 100644 index 0000000..0f27a57 --- /dev/null +++ b/HTMLKit/HTMLParseErrorToken.h @@ -0,0 +1,19 @@ +// +// HTMLParseErrorToken.h +// HTMLKit +// +// Created by Iska on 23/10/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import +#import "HTMLToken.h" + +@interface HTMLParseErrorToken : HTMLToken + +@property (nonatomic, copy) NSString *reason; +@property (nonatomic, assign) NSUInteger location; + +- (instancetype)initWithReasonMessage:(NSString *)reason andStreamLocation:(NSUInteger)location; + +@end diff --git a/HTMLKit/HTMLParseErrorToken.m b/HTMLKit/HTMLParseErrorToken.m new file mode 100644 index 0000000..4e2bcdd --- /dev/null +++ b/HTMLKit/HTMLParseErrorToken.m @@ -0,0 +1,38 @@ +// +// HTMLParseErrorToken.m +// HTMLKit +// +// Created by Iska on 23/10/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import "HTMLParseErrorToken.h" + +@interface HTMLParseErrorToken () +{ + NSString *_reason; + NSUInteger _location; +} +@end + +@implementation HTMLParseErrorToken +@synthesize reason = _reason; +@synthesize location = _location; + +- (instancetype)initWithReasonMessage:(NSString *)reason andStreamLocation:(NSUInteger)location +{ + self = [super init]; + if (self) { + self.type = HTMLTokenTypeParseError; + _reason = [reason copy]; + _location = location; + } + return self; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: %p Reason='%@' Location='%lu'>", self.class, self, _reason, _location]; +} + +@end diff --git a/HTMLKit/HTMLParser.h b/HTMLKit/HTMLParser.h new file mode 100644 index 0000000..5f29bfa --- /dev/null +++ b/HTMLKit/HTMLParser.h @@ -0,0 +1,22 @@ +// +// HTMLParser.h +// HTMLKit +// +// Created by Iska on 04/10/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import +#import "HTMLElement.h" + +@interface HTMLParser : NSObject + +@property (nonatomic, strong, readonly) NSArray *parseErrors; +@property (nonatomic, strong, readonly) HTMLDocument *document; + +- (instancetype)initWithString:(NSString *)string; + +- (HTMLDocument *)parseDocument; +- (NSArray *)parseFragmentWithContextElement:(HTMLElement *)contextElement; + +@end diff --git a/HTMLKit/HTMLParser.m b/HTMLKit/HTMLParser.m new file mode 100644 index 0000000..5a6e759 --- /dev/null +++ b/HTMLKit/HTMLParser.m @@ -0,0 +1,2640 @@ +// +// HTMLParser.m +// HTMLKit +// +// Created by Iska on 04/10/14. +// Copyright (c) 2014 BrainCookie. All rights reserved. +// + +#import "HTMLParser.h" +#import "HTMLTokenizer.h" +#import "HTMLTokens.h" +#import "HTMLStackOfOpenElements.h" +#import "HTMLListOfActiveFormattingElements.h" +#import "HTMLParserInsertionModes.h" +#import "HTMLNodes.h" +#import "HTMLElementTypes.h" +#import "HTMLElementAdjustment.h" +#import "HTMLMarker.h" +#import "NSString+HTMLKit.h" + +@interface HTMLTokenizer (Private) +@property (nonatomic, weak) HTMLParser *parser; +@end + +@interface HTMLDocument (Private) +@property (nonatomic, assign) HTMLDocumentReadyState readyState; +@end + +@interface HTMLParser () +{ + HTMLTokenizer *_tokenizer; + + NSMutableArray *_errors; + + HTMLInsertionMode _insertionMode; + HTMLInsertionMode _originalInsertionMode; + NSMutableArray *_stackOfTemplateInsertionModes; + + HTMLStackOfOpenElements *_stackOfOpenElements; + HTMLListOfActiveFormattingElements *_listOfActiveFormattingElements; + + HTMLDocument *_document; + + HTMLElement *_contextElement; + HTMLElement *_currentElement; + + HTMLElement *_headElementPointer; + HTMLElement *_formElementPointer; + + HTMLCharacterToken *_pendingTableCharacterTokens; + + BOOL _framesetOkFlag; + BOOL _fragmentParsingAlgorithm; + BOOL _fosterParenting; + BOOL _ignoreNextLineFeedCharacterToken; +} +@end + +@implementation HTMLParser + +#pragma mark - Lifecycle + +- (instancetype)initWithString:(NSString *)string +{ + self = [super init]; + if (self) { + _framesetOkFlag = YES; + _fragmentParsingAlgorithm = NO; + _fosterParenting = NO; + _ignoreNextLineFeedCharacterToken = NO; + + _errors = [NSMutableArray new]; + + _insertionMode = HTMLInsertionModeInitial; + _stackOfTemplateInsertionModes = [NSMutableArray new]; + + _stackOfOpenElements = [HTMLStackOfOpenElements new]; + _listOfActiveFormattingElements = [HTMLListOfActiveFormattingElements new]; + + _tokenizer = [[HTMLTokenizer alloc] initWithString:string ?: @""]; + _tokenizer.parser = self; + + _pendingTableCharacterTokens = [[HTMLCharacterToken alloc] initWithString:@""]; + + _headElementPointer = nil; + _formElementPointer = nil; + } + return self; +} + +#pragma mark - Properties + +- (NSArray *)parseErrors +{ + return _errors; +} + +- (HTMLDocument *)document +{ + return _document ?: [self parseDocument]; +} + +#pragma mark - Parse + +- (void)initializeDocument +{ + if (_document == nil) { + _document = [HTMLDocument new]; + } + _document.quirksMode = HTMLQuirksModeNoQuirks; + _document.documentType = nil; + [_document removeAllChildNodes]; + + _fragmentParsingAlgorithm = NO; +} + +- (HTMLDocument *)parseDocument +{ + [self initializeDocument]; + [self runParser]; + return _document; +} + +- (NSArray *)parseFragmentWithContextElement:(HTMLElement *)contextElement +{ + if (contextElement == nil) { + return nil; + } + + if ([_contextElement isEqualTo:contextElement]) { + return [_document childNodeAtIndex:0].childNodes.array; + } + + [self initializeDocument]; + _tokenizer = [[HTMLTokenizer alloc] initWithString:_tokenizer.string]; + _tokenizer.parser = self; + + _contextElement = contextElement; + _fragmentParsingAlgorithm = YES; + + _document.quirksMode = _contextElement.ownerDocument ? _contextElement.ownerDocument.quirksMode : HTMLQuirksModeNoQuirks; + + if (_contextElement.htmlNamespace == HTMLNamespaceHTML) { + 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"]) { + [_stackOfTemplateInsertionModes addObject:@(HTMLInsertionModeInTemplate)]; + } + + [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; + } + [self processToken:token]; + } +} + +- (void)stopParsing +{ + [_stackOfOpenElements popAll]; + _document.readyState = HTMLDocumentComplete; +} + +#pragma mark - Processing + +- (void)processToken:(HTMLToken *)token +{ + BOOL (^ treeConstructionDispatcher)(HTMLElement *node) = ^BOOL(HTMLElement *node){ + + if (node == nil) { + return YES; + } + if (node.htmlNamespace == HTMLNamespaceHTML) { + return YES; + } + if (IsNodeMathMLTextIntegrationPoint(node)) { + if (token.type == HTMLTokenTypeStartTag) { + return ![token.asStartTagToken.tagName isEqualToAny:@"mglyph", @"malignmark", nil]; + } + if (token.type == HTMLTokenTypeCharacter) { + return YES; + } + } + if (node.htmlNamespace == HTMLNamespaceMathML && [node.tagName isEqualToString:@"annotation-xml"]) { + if (token.type == HTMLTokenTypeStartTag && [token.asTagToken.tagName isEqualToString:@"svg"]) { + return YES; + } + } + + if (IsNodeHTMLIntegrationPoint(node)) { + if (token.type == HTMLTokenTypeStartTag || token.type == HTMLTokenTypeCharacter) { + return YES; + } + } + if (token.type == HTMLTokenTypeEOF) { + return YES; + } + + return NO; + }; + + if (token.isParseError) { + [self emitParseError:@"Tokenizer Parser Error: %@", token.asParseError]; + return; + } + + if (_ignoreNextLineFeedCharacterToken) { + _ignoreNextLineFeedCharacterToken = NO; + if (token.isCharacterToken) { + NSString *characters = token.asCharacterToken.characters; + if ([characters characterAtIndex:0] == 0x000A) { + if (characters.length <= 1) { + return; + } + [token.asCharacterToken trimFormIndex:1]; + } + } + } + + if (treeConstructionDispatcher(self.adjustedCurrentNode)) { + [self processToken:token byApplyingRulesForInsertionMode:_insertionMode]; + } else { + [self processTokenByApplyingRulesForParsingTokensInForeignContent:token]; + } +} + +- (void)reprocessToken:(HTMLToken *)token +{ + [self processToken:token]; +} + +- (void)processToken:(HTMLToken *)token byApplyingRulesForInsertionMode:(HTMLInsertionMode)insertionMode +{ + switch (_insertionMode) { + case HTMLInsertionModeInitial: + return [self HTMLInsertionModeInitial:token]; + case HTMLInsertionModeBeforeHTML: + return [self HTMLInsertionModeBeforeHTML:token]; + case HTMLInsertionModeBeforeHead: + return [self HTMLInsertionModeBeforeHead:token]; + case HTMLInsertionModeInHead: + return [self HTMLInsertionModeInHead:token]; + case HTMLInsertionModeInHeadNoscript: + return [self HTMLInsertionModeInHeadNoscript:token]; + case HTMLInsertionModeAfterHead: + return [self HTMLInsertionModeAfterHead:token]; + case HTMLInsertionModeInBody: + return [self HTMLInsertionModeInBody:token]; + case HTMLInsertionModeText: + return [self HTMLInsertionModeText:token]; + case HTMLInsertionModeInTable: + return [self HTMLInsertionModeInTable:token]; + case HTMLInsertionModeInTableText: + return [self HTMLInsertionModeInTableText:token]; + case HTMLInsertionModeInCaption: + return [self HTMLInsertionModeInCaption:token]; + case HTMLInsertionModeInColumnGroup: + return [self HTMLInsertionModeInColumnGroup:token]; + case HTMLInsertionModeInTableBody: + return [self HTMLInsertionModeInTableBody:token]; + case HTMLInsertionModeInRow: + return [self HTMLInsertionModeInRow:token]; + case HTMLInsertionModeInCell: + return [self HTMLInsertionModeInCell:token]; + case HTMLInsertionModeInSelect: + return [self HTMLInsertionModeInSelect:token]; + case HTMLInsertionModeInSelectInTable: + return [self HTMLInsertionModeInSelectInTable:token]; + case HTMLInsertionModeInTemplate: + return [self HTMLInsertionModeInTemplate:token]; + case HTMLInsertionModeAfterBody: + return [self HTMLInsertionModeAfterBody:token]; + case HTMLInsertionModeInFrameset: + return [self HTMLInsertionModeInFrameset:token]; + case HTMLInsertionModeAfterFrameset: + return [self HTMLInsertionModeAfterFrameset:token]; + case HTMLInsertionModeAfterAfterBody: + return [self HTMLInsertionModeAfterAfterBody:token]; + case HTMLInsertionModeAfterAfterFrameset: + return [self HTMLInsertionModeAfterAfterFrameset:token]; + } +} + +#pragma mark - Nodes + +- (HTMLElement *)currentNode +{ + return _stackOfOpenElements.currentNode; +} + +- (HTMLElement *)adjustedCurrentNode +{ + if (_stackOfOpenElements.count == 1 && _fragmentParsingAlgorithm) { + return _contextElement; + } + return [self currentNode]; +} + +- (HTMLInsertionMode)currentTemplateInsertionMode +{ + if (_stackOfTemplateInsertionModes.count == 0) { + return _insertionMode; + } + return [_stackOfTemplateInsertionModes.firstObject unsignedIntegerValue]; +} + +#pragma mark - Emits + +- (void)emitParseError:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2) +{ + va_list args; + va_start(args, format); + NSString *message = [[NSString alloc] initWithFormat:format arguments:args]; + [_errors addObject:message]; + va_end(args); +} + +#pragma mark - Insertions & Manipulations + +- (HTMLNode *)appropriatePlaceForInsertingANodeWithOverrideTarget:(HTMLElement *)overrideTarget + beforeChildNode:(out HTMLElement * __autoreleasing *)child +{ + HTMLNode *target = self.currentNode; + if (overrideTarget != nil) { + target = overrideTarget; + } + + while (_fosterParenting && [[(HTMLElement *)target tagName] isEqualToAny:@"table", @"tbody", @"tfoot", @"thead", @"tr", nil]) { + HTMLElement *lastTemplate = nil; + HTMLElement *lastTable = nil; + + for (HTMLElement *element in _stackOfOpenElements.reverseObjectEnumerator) { + if ([element.tagName isEqualToString:@"template"]) { + lastTemplate = element; + break; + } + if ([element.tagName isEqualToString:@"table"]) { + lastTable = element; + break; + } + } + + if (lastTemplate != nil) { + HTMLTemplate *template = (HTMLTemplate *)lastTemplate; + target = template; + break; + } + + if (lastTable == nil) { + HTMLElement *htmlElement = _stackOfOpenElements.firstNode; + target = htmlElement; + break; + } + + if (lastTable.parentNode != nil) { + *child = lastTable; + target = lastTable.parentNode; + break; + } + + NSUInteger lastTableIndex = [_stackOfOpenElements indexOfElement:lastTable]; + HTMLElement *previousNode = _stackOfOpenElements[lastTableIndex]; + target = previousNode; + break; + } + + if ([target isKindOfClass:[HTMLTemplate class]]) { + target = [(HTMLTemplate *)target content]; + } + return target; +} + +- (void)insertComment:(HTMLCommentToken *)token +{ + [self insertComment:token asChildOfNode:nil]; +} + +- (void)insertComment:(HTMLCommentToken *)token asChildOfNode:(HTMLNode *)node +{ + HTMLNode *parent = node; + HTMLElement *child = nil; + if (parent == nil) { + parent = [self appropriatePlaceForInsertingANodeWithOverrideTarget:nil beforeChildNode:&child]; + } + + HTMLComment *comment = [[HTMLComment alloc] initWithData:token.data]; + [parent insertNode:comment beforeChildNode:child]; +} + +- (HTMLElement *)createElementForToken:(HTMLTagToken *)token inNamespace:(HTMLNamespace)htmlNamespace +{ + HTMLElement *element = [[HTMLElement alloc] initWithTagName:token.tagName + attributes:token.attributes + namespace:htmlNamespace]; + return element; +} + +- (HTMLElement *)insertElementForToken:(HTMLTagToken *)token +{ + return [self insertForeignElementForToken:token inNamespace:HTMLNamespaceHTML]; +} + +- (HTMLElement *)insertForeignElementForToken:(HTMLTagToken *)token inNamespace:(HTMLNamespace)namespace +{ + HTMLElement *element = [self createElementForToken:token inNamespace:namespace]; + return [self insertElement:element]; +} + +- (HTMLElement *)insertElement:(HTMLElement *)element +{ + HTMLElement *child = nil; + HTMLNode *adjustedInsertionLocation = [self appropriatePlaceForInsertingANodeWithOverrideTarget:nil + beforeChildNode:&child]; + [adjustedInsertionLocation insertNode:element beforeChildNode:child]; + [_stackOfOpenElements pushElement:element]; + return element; +} + +- (void)insertCharacters:(NSString *)data +{ + HTMLElement *child = nil; + HTMLNode *adjustedInsertionLocation = [self appropriatePlaceForInsertingANodeWithOverrideTarget:nil + beforeChildNode:&child]; + if (adjustedInsertionLocation.type != HTMLNodeDocument) { + if (child != nil && child.previousSibling.type == HTMLNodeText) { + [(HTMLText *)child.previousSibling appendString:data]; + } else if (adjustedInsertionLocation.lastChildNode.type == HTMLNodeText) { + [(HTMLText *)adjustedInsertionLocation.lastChildNode appendString:data]; + } else { + HTMLText *text = [[HTMLText alloc] initWithData:data]; + [adjustedInsertionLocation insertNode:text beforeChildNode:child]; + } + } +} + +- (void)applyGenericParsingAlgorithmForToken:(HTMLStartTagToken *)token withTokenizerState:(HTMLTokenizerState)state +{ + [self insertElementForToken:token]; + _tokenizer.state = state; + _originalInsertionMode = _insertionMode; + [self switchInsertionMode:HTMLInsertionModeText]; +} + +- (void)reconstructActiveFormattingElements +{ + if (_listOfActiveFormattingElements.isEmpty) { + return; + } + + id last = _listOfActiveFormattingElements.lastEntry; + if (last == [HTMLMarker marker] || [_stackOfOpenElements containsElement:last]) { + return; + } + + NSInteger index = _listOfActiveFormattingElements.count - 1; + HTMLElement *entry = _listOfActiveFormattingElements[index]; + + // Reconstruct the active formatting elements + // https://html.spec.whatwg.org/multipage/syntax.html#reconstruct-the-active-formatting-elements + + // Rewind phase + while (![entry isEqual:[HTMLMarker marker]] && ![_stackOfOpenElements containsElement:entry]) { + if (index == 0) { + index--; + break; + } + entry = _listOfActiveFormattingElements[--index]; + } + + while (YES) { + // Advance phase + entry = _listOfActiveFormattingElements[++index]; + + // Create phase + HTMLStartTagToken *token = [[HTMLStartTagToken alloc] initWithTagName:entry.tagName + attributes:entry.attributes]; + HTMLElement *element = [self insertElementForToken:token]; + [_listOfActiveFormattingElements replaceElementAtIndex:index withElement:element]; + + if (element == _listOfActiveFormattingElements.lastEntry) { + break; + } + } +} + +- (void)generateImpliedEndTagsExceptForElement:(NSString *)tagName +{ + while ([self.currentNode.tagName isEqualToAny:@"dd", @"dt", @"li", @"option", @"optgroup", @"p", @"rp", @"rt", nil] && + ![self.currentNode.tagName isEqualToString:tagName]) { + [_stackOfOpenElements popCurrentNode]; + } +} + +- (void)generateAllImpliedEndTagsThoroughly +{ + while ([self.currentNode.tagName isEqualToAny:@"caption", @"colgroup", @"dd", @"dt", @"li", @"option", @"optgroup", @"p", + @"rp", @"rt", @"tbody", @"td", @"tfoot", @"th", @"thead", @"tr", nil]) { + [_stackOfOpenElements popCurrentNode]; + } +} + +- (void)closePElement +{ + [self generateImpliedEndTagsExceptForElement:@"p"]; + if (![self.currentNode.tagName isEqualToString:@"p"]) { + [self emitParseError:@"Current node being closed is not a

element"]; + } + [_stackOfOpenElements popElementsUntilElementPoppedWithTagName:@"p"]; +} + +- (BOOL)runAdoptionAgencyAlgorithmForTagName:(NSString *)tagName +{ + if ([self.currentNode.tagName isEqualToString:tagName] && + ![_listOfActiveFormattingElements containsElement:self.currentNode]) { + [_stackOfOpenElements popCurrentNode]; + return NO; + } + + for (int outerLoopCounter = 0; outerLoopCounter < 8; outerLoopCounter++) { + + HTMLElement *formattingElement = [_listOfActiveFormattingElements formattingElementWithTagName:tagName]; + if (formattingElement == nil) { + return YES; + } + + if (![_stackOfOpenElements containsElement:formattingElement]) { + [self emitParseError:@"Formatting element is not in the Stack of Open Elements"]; + [_listOfActiveFormattingElements removeElement:formattingElement]; + return NO; + } + + if (![_stackOfOpenElements hasElementInScopeWithTagName:formattingElement.tagName]) { + [self emitParseError:@"Formatting element is not in scope"]; + return NO; + } + + if (![formattingElement isEqual:self.currentNode]) { + [self emitParseError:@"Formatting element is not the current node"]; + } + + NSUInteger formattingElementIndex = [_stackOfOpenElements indexOfElement:formattingElement]; + HTMLElement *furthestBlock = [_stackOfOpenElements furthestBlockAfterIndex:formattingElementIndex]; + + if (furthestBlock == nil) { + [_stackOfOpenElements popElementsUntilElementPopped:formattingElement]; + [_listOfActiveFormattingElements removeElement:formattingElement]; + return NO; + } + + HTMLElement *commonAncestor = _stackOfOpenElements[formattingElementIndex - 1]; + NSUInteger bookmark = [_listOfActiveFormattingElements indexOfElement:formattingElement]; + + HTMLElement *node = furthestBlock; + HTMLElement *lastNode = furthestBlock; + + NSUInteger index = [_stackOfOpenElements indexOfElement:node]; + for (int innerLoopCounter = 0; innerLoopCounter < 3; innerLoopCounter ++) { + + index--; + node = _stackOfOpenElements[index]; + + if (![_listOfActiveFormattingElements containsElement:node]) { + [_stackOfOpenElements removeElement:node]; + continue; + } + + if ([node isEqual:formattingElement]) { + break; + } + + HTMLElement *newElement = [node copy]; + [_listOfActiveFormattingElements replaceElementAtIndex:[_listOfActiveFormattingElements indexOfElement:node] + withElement:newElement]; + [_stackOfOpenElements replaceElementAtIndex:[_stackOfOpenElements indexOfElement:node] + withElement:newElement]; + node = newElement; + + if ([lastNode isEqual:furthestBlock]) { + bookmark = [_listOfActiveFormattingElements indexOfElement:node] + 1; + } + + [lastNode.parentNode removeChildNode:lastNode]; + [node appendNode:lastNode]; + lastNode = node; + } + + HTMLElement *child = nil; + HTMLNode *parent = [self appropriatePlaceForInsertingANodeWithOverrideTarget:commonAncestor + beforeChildNode:&child]; + [parent insertNode:lastNode beforeChildNode:child]; + + HTMLElement *newElement = [formattingElement copy]; + [furthestBlock reparentChildNodesIntoNode:newElement]; + [furthestBlock appendNode:newElement]; + + [_listOfActiveFormattingElements removeElement:formattingElement]; + [_listOfActiveFormattingElements insertElement:newElement atIndex:bookmark]; + + [_stackOfOpenElements removeElement:formattingElement]; + NSUInteger furthestBlockIndex = [_stackOfOpenElements indexOfElement:furthestBlock]; + [_stackOfOpenElements insertElement:newElement atIndex:furthestBlockIndex + 1]; + } + return NO; +} + +- (void)closeTheCell +{ + [self generateImpliedEndTagsExceptForElement:nil]; + if (![self.currentNode.tagName isEqualToAny:@"td", @"th", nil]) { + [self emitParseError:@"Misnested Cell"]; + } + [_stackOfOpenElements popElementsUntilAnElementPoppedWithAnyOfTagNames:@[@"td", @"th"]]; + [_listOfActiveFormattingElements clearUptoLastMarker]; + [self switchInsertionMode:HTMLInsertionModeInRow]; +} + +#pragma mark - State Machine + +- (void)switchInsertionMode:(HTMLInsertionMode)mode +{ + if (mode == HTMLInsertionModeText || mode == HTMLInsertionModeInTableText) { + _originalInsertionMode = _insertionMode; + } + _insertionMode = mode; +} + +- (void)resetInsertionModeAppropriately +{ + BOOL last = NO; + HTMLElement *node = _stackOfOpenElements.lastNode; + NSUInteger nodeIndex = _stackOfOpenElements.count - 1; + + while (YES) { + + if (node == _stackOfOpenElements.firstNode) { + last = YES; + if (_fragmentParsingAlgorithm) { + node = _contextElement; + } + } + + if ([node.tagName isEqualToString:@"select"]) { + if (last == NO) { + HTMLElement *ancestor = node; + NSUInteger ancestorIndex = nodeIndex; + + while (YES) { + if (ancestor == _stackOfOpenElements.firstNode) { + break; + } + + ancestorIndex--; + ancestor = _stackOfOpenElements[ancestorIndex]; + + if ([ancestor.tagName isEqualToString:@"template"]) { + break; + } + + if ([ancestor.tagName isEqualToString:@"table"]) { + [self switchInsertionMode:HTMLInsertionModeInTable]; + return; + } + } + } + [self switchInsertionMode:HTMLInsertionModeInSelect]; + return; + } + + if (last == NO) { + if ([node.tagName isEqualToAny:@"td", @"th", nil]) { + [self switchInsertionMode:HTMLInsertionModeInCell]; + return; + } + } + + if ([node.tagName isEqualToString:@"tr"]) { + [self switchInsertionMode:HTMLInsertionModeInRow]; + return; + } + + if ([node.tagName isEqualToAny:@"tbody", @"thead", @"tfoot", nil]) { + [self switchInsertionMode:HTMLInsertionModeInTableBody]; + return; + } + + if ([node.tagName isEqualToString:@"caption"]) { + [self switchInsertionMode:HTMLInsertionModeInCaption]; + return; + } + + if ([node.tagName isEqualToString:@"colgroup"]) { + [self switchInsertionMode:HTMLInsertionModeInColumnGroup]; + return; + } + + if ([node.tagName isEqualToString:@"table"]) { + [self switchInsertionMode:HTMLInsertionModeInTable]; + return; + } + + if ([node.tagName isEqualToString:@"template"]) { + [self switchInsertionMode:self.currentTemplateInsertionMode]; + return; + } + + if (last == NO) { + if ([node.tagName isEqualToString:@"head"]) { + [self switchInsertionMode:HTMLInsertionModeInHead]; + return; + } + } + + if ([node.tagName isEqualToString:@"body"]) { + [self switchInsertionMode:HTMLInsertionModeInBody]; + return; + } + + if ([node.tagName isEqualToString:@"frameset"]) { + [self switchInsertionMode:HTMLInsertionModeInFrameset]; + return; + } + + if ([node.tagName isEqualToString:@"html"]) { + if (_headElementPointer == nil) { + [self switchInsertionMode:HTMLInsertionModeBeforeHead]; + } else { + [self switchInsertionMode:HTMLInsertionModeAfterHead]; + } + return; + } + + if (last) { + [self switchInsertionMode:HTMLInsertionModeInBody]; + return; + } + + nodeIndex--; + node = _stackOfOpenElements[nodeIndex]; + } +} + +#pragma mark - Insertion Modes + +- (void)HTMLInsertionModeInitial:(HTMLToken *)token +{ + switch (token.type) { + case HTMLTokenTypeCharacter: + { + [token.asCharacterToken trimLeadingWhitespace]; + + if (token.asCharacterToken.isEmpty) { + return; + } + break; + } + case HTMLTokenTypeComment: + [self insertComment:token.asCommentToken asChildOfNode:_document]; + return; + case HTMLTokenTypeDoctype: + { + HTMLDOCTYPEToken *doctypeToken = token.asDoctypeToken; + + HTMLDocumentType *doctype = [[HTMLDocumentType alloc] initWithName:doctypeToken.name + publicIdentifier:doctypeToken.publicIdentifier + systemIdentifier:doctypeToken.systemIdentifier]; + + if (!doctype.isValid) { + [self emitParseError:@"Invalid DOCTYPE"]; + } + + _document.documentType = doctype; + _document.quirksMode = doctype.quirksMode; + + if (doctypeToken.forceQuirks) { + _document.quirksMode = HTMLQuirksModeQuirks; + } + [self switchInsertionMode:HTMLInsertionModeBeforeHTML]; + return; + } + default: + break; + } + + [self emitParseError:@"Expected a DOCTYPE but got %@", token]; + _document.quirksMode = HTMLQuirksModeQuirks; + [self switchInsertionMode:HTMLInsertionModeBeforeHTML]; + [self reprocessToken:token]; +} + +- (void)HTMLInsertionModeBeforeHTML:(HTMLToken *)token +{ + switch (token.type) { + case HTMLTokenTypeDoctype: + [self emitParseError:@"Unexpected DOCTYPE Token before "]; + return; + case HTMLTokenTypeComment: + [self insertComment:token.asCommentToken asChildOfNode:_document]; + return; + case HTMLTokenTypeCharacter: + { + [token.asCharacterToken trimLeadingWhitespace]; + + if (token.asCharacterToken.isEmpty) { + return; + } + break; + } + case HTMLTokenTypeStartTag: + if ([token.asStartTagToken.tagName isEqualToString:@"html"]) { + HTMLElement *html = [self createElementForToken:token.asTagToken inNamespace:HTMLNamespaceHTML]; + [_document appendNode:html]; + [_stackOfOpenElements pushElement:html]; + [self switchInsertionMode:HTMLInsertionModeBeforeHead]; + return; + } + break; + case HTMLTokenTypeEndTag: + if (![token.asEndTagToken.tagName isEqualToAny:@"head", @"body", @"html", @"br", nil]) { + [self emitParseError:@"Unexpected End Tag Token (%@) before ", token.asEndTagToken.tagName]; + return; + } + break; + default: + break; + } + + HTMLElement *html = [[HTMLElement alloc] initWithTagName:@"html"]; + [_document appendNode:html]; + [_stackOfOpenElements pushElement:html]; + [self switchInsertionMode:HTMLInsertionModeBeforeHead]; + [self reprocessToken:token]; +} + +- (void)HTMLInsertionModeBeforeHead:(HTMLToken *)token +{ + switch (token.type) { + case HTMLTokenTypeCharacter: + { + [token.asCharacterToken trimLeadingWhitespace]; + + if (token.asCharacterToken.isEmpty) { + return; + } + break; + } + case HTMLTokenTypeComment: + [self insertComment:token.asCommentToken]; + return; + case HTMLTokenTypeDoctype: + [self emitParseError:@"Unexpected DOCTYPE Token before "]; + return; + case HTMLTokenTypeStartTag: + if ([token.asStartTagToken.tagName isEqualToString:@"html"]) { + [self HTMLInsertionModeInBody:token]; + } else if ([token.asStartTagToken.tagName isEqualToString:@"head"]) { + HTMLElement *head = [self insertElementForToken:token.asTagToken]; + _headElementPointer = head; + [self switchInsertionMode:HTMLInsertionModeInHead]; + } else { + break; + } + return; + case HTMLTokenTypeEndTag: + if (![token.asEndTagToken.tagName isEqualToAny:@"head", @"body", @"html", @"br", nil]) { + [self emitParseError:@"Unexpected End Tag Token (%@) before ", token.asEndTagToken.tagName]; + return; + } + break; + default: + break; + } + + HTMLStartTagToken *headToken = [[HTMLStartTagToken alloc] initWithTagName:@"head"]; + HTMLElement *head = [self insertElementForToken:headToken]; + _headElementPointer = head; + [self switchInsertionMode:HTMLInsertionModeInHead]; + [self reprocessToken:token]; +} + +- (void)HTMLInsertionModeInHead:(HTMLToken *)token +{ + switch (token.type) { + case HTMLTokenTypeCharacter: + { + HTMLCharacterToken *leadingWhiteSpace = [token.asCharacterToken tokenBySplitingLeadingWhiteSpace]; + if (leadingWhiteSpace) { + [self insertCharacters:leadingWhiteSpace.characters]; + } + + if (token.asCharacterToken.isEmpty) { + return; + } + break; + } + case HTMLTokenTypeComment: + [self insertComment:token.asCommentToken]; + return; + case HTMLTokenTypeDoctype: + [self emitParseError:@"Unexpected DOCTYPE Token in "]; + return; + case HTMLTokenTypeStartTag: + if ([token.asStartTagToken.tagName isEqualToString:@"html"]) { + [self HTMLInsertionModeInBody:token]; + } else if ([token.asStartTagToken.tagName isEqualToAny:@"base", @"basefont", @"bgsound", @"link", nil]) { + [self insertElementForToken:token.asStartTagToken]; + [_stackOfOpenElements popCurrentNode]; + } else if ([token.asStartTagToken.tagName isEqualToString:@"meta"]) { + [self insertElementForToken:token.asStartTagToken]; + [_stackOfOpenElements popCurrentNode]; + } else if ([token.asStartTagToken.tagName isEqualToString:@"title"]) { + [self applyGenericParsingAlgorithmForToken:token.asStartTagToken withTokenizerState:HTMLTokenizerStateRCDATA]; + } else if ([token.asStartTagToken.tagName isEqualToAny:@"noscript", @"noframes", @"style", nil]) { + [self applyGenericParsingAlgorithmForToken:token.asStartTagToken withTokenizerState:HTMLTokenizerStateRAWTEXT]; + } else if ([token.asStartTagToken.tagName isEqualToString:@"script"]) { + HTMLElement *child = nil; + HTMLNode *adjustedInsertionLocation = [self appropriatePlaceForInsertingANodeWithOverrideTarget:nil + beforeChildNode:&child]; + HTMLElement *script = [self createElementForToken:token.asStartTagToken inNamespace:HTMLNamespaceHTML]; + [adjustedInsertionLocation insertNode:script beforeChildNode:child]; + [_stackOfOpenElements pushElement:script]; + _tokenizer.state = HTMLTokenizerStateScriptData; + _originalInsertionMode = _insertionMode; + [self switchInsertionMode:HTMLInsertionModeText]; + } else if ([token.asStartTagToken.tagName isEqualToString:@"head"]) { + [self emitParseError:@"Unexpected Start Tag Token (head) in "]; + } else if ([token.asStartTagToken.tagName isEqualToString:@"template"]) { + HTMLTemplate *template = [HTMLTemplate new]; + [self insertElement:template]; + [_listOfActiveFormattingElements addMarker]; + _framesetOkFlag = NO; + [self switchInsertionMode:HTMLInsertionModeInTemplate]; + [_stackOfTemplateInsertionModes addObject:@(HTMLInsertionModeInTemplate)]; + } else { + break; + } + return; + case HTMLTokenTypeEndTag: + if ([token.asEndTagToken.tagName isEqualToString:@"head"]) { + [_stackOfOpenElements popCurrentNode]; + [self switchInsertionMode:HTMLInsertionModeAfterHead]; + } else if ([token.asEndTagToken.tagName isEqualToAny:@"body", @"html", @"br", nil]) { + break; + } else if ([token.asEndTagToken.tagName isEqualToString:@"template"]) { + if (![_stackOfOpenElements containsElementWithTagName:@"template"]) { + [self emitParseError:@"Unexpected End Tag Token (template) in "]; + return; + } + [self generateAllImpliedEndTagsThoroughly]; + if (![self.currentNode.tagName isEqualToString:@"template"]) { + [self emitParseError:@"Unexpected End Tag Token in "]; + } + [_stackOfOpenElements popElementsUntilTemplateElementPopped]; + [_listOfActiveFormattingElements clearUptoLastMarker]; + [_stackOfTemplateInsertionModes removeLastObject]; + [self resetInsertionModeAppropriately]; + } else { + [self emitParseError:@"Unexpected End Tag Token (%@) in ", token.asEndTagToken.tagName]; + return; + } + return; + default: + break; + } + + [_stackOfOpenElements popCurrentNode]; + [self switchInsertionMode:HTMLInsertionModeAfterHead]; + [self reprocessToken:token]; +} + +- (void)HTMLInsertionModeInHeadNoscript:(HTMLToken *)token +{ + switch (token.type) { + case HTMLTokenTypeDoctype: + [self emitParseError:@"Unexpected DOCTYPE Token in