diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index 6c20966..54c22ab 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -53,14 +53,11 @@ 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 */; }; + 6238C9851AB8D6330006512E /* HTMLKitDOMExceptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 6238C9831AB8D6330006512E /* HTMLKitDOMExceptions.h */; }; + 6238C9861AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238C9841AB8D6330006512E /* HTMLKitDOMExceptions.m */; }; + 6238C9871AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238C9841AB8D6330006512E /* HTMLKitDOMExceptions.m */; }; + 623916C71AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */; }; + 623916C81AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.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 */; }; @@ -86,6 +83,20 @@ 624AB31F1B05373600F3830D /* CSSSimpleSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AB31D1B05373600F3830D /* CSSSimpleSequence.m */; }; 624AB3201B05373600F3830D /* CSSSimpleSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AB31D1B05373600F3830D /* CSSSimpleSequence.m */; }; 624AB3221B053A5800F3830D /* CSSSimpleSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 624AB3211B053A5800F3830D /* CSSSimpleSelector.h */; }; + 624717181B22333200C11912 /* HTMLNodeTraversal.h in Headers */ = {isa = PBXBuildFile; fileRef = 624717161B22333200C11912 /* HTMLNodeTraversal.h */; }; + 624717191B22333200C11912 /* HTMLNodeTraversal.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717171B22333200C11912 /* HTMLNodeTraversal.m */; }; + 6247171A1B22333200C11912 /* HTMLNodeTraversal.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717171B22333200C11912 /* HTMLNodeTraversal.m */; }; + 6247171C1B2240B800C11912 /* HTMLTreeWalkerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247171B1B2240B800C11912 /* HTMLTreeWalkerTests.m */; }; + 6247171D1B2240B800C11912 /* HTMLTreeWalkerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247171B1B2240B800C11912 /* HTMLTreeWalkerTests.m */; }; + 624717B81B21FE5400B38302 /* HTMLNodeFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */; }; + 624717B91B21FE5400B38302 /* HTMLNodeFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */; }; + 624717BC1B22009200B38302 /* HTMLTreeWalker.h in Headers */ = {isa = PBXBuildFile; fileRef = 624717BA1B22009200B38302 /* HTMLTreeWalker.h */; }; + 624717BD1B22009200B38302 /* HTMLTreeWalker.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717BB1B22009200B38302 /* HTMLTreeWalker.m */; }; + 624717BE1B22009200B38302 /* HTMLTreeWalker.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717BB1B22009200B38302 /* HTMLTreeWalker.m */; }; + 6247A9431B152F4F00CCF25C /* HTMLNodeIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */; }; + 6247A9441B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */; }; + 6247A9451B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */; }; + 6247A9471B152F8C00CCF25C /* HTMLNodeFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */; }; 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 */; }; @@ -145,7 +156,7 @@ 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 */; }; - 62D89DB01AE7080300B6243D /* HTMLNodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 62362A3F1A9FDE8A00301989 /* HTMLNodes.h */; }; + 62D89DB01AE7080300B6243D /* HTMLDOM.h in Headers */ = {isa = PBXBuildFile; fileRef = 62362A3F1A9FDE8A00301989 /* HTMLDOM.h */; }; 62D89DB11AE7081600B6243D /* HTMLTokenizerStates.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493A919CCE84A00BCDDF4 /* HTMLTokenizerStates.h */; }; 62D89DB21AE7081600B6243D /* HTMLTokenizerCharacters.h in Headers */ = {isa = PBXBuildFile; fileRef = 62E7CAAE19CDFFB500465A83 /* HTMLTokenizerCharacters.h */; }; 62D89DB31AE7081600B6243D /* HTMLNamespaces.h in Headers */ = {isa = PBXBuildFile; fileRef = 628B7CE61A080E1000602C87 /* HTMLNamespaces.h */; }; @@ -214,7 +225,7 @@ 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 = ""; }; + 62362A3F1A9FDE8A00301989 /* HTMLDOM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLDOM.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 = ""; }; @@ -227,11 +238,9 @@ 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 = ""; }; + 6238C9831AB8D6330006512E /* HTMLKitDOMExceptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLKitDOMExceptions.h; sourceTree = ""; }; + 6238C9841AB8D6330006512E /* HTMLKitDOMExceptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitDOMExceptions.m; sourceTree = ""; }; + 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitNodeIteratorTests.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 = ""; }; @@ -251,6 +260,15 @@ 624AB31C1B05373600F3830D /* CSSSimpleSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSSimpleSequence.h; sourceTree = ""; }; 624AB31D1B05373600F3830D /* CSSSimpleSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSSimpleSequence.m; sourceTree = ""; }; 624AB3211B053A5800F3830D /* CSSSimpleSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSSimpleSelector.h; sourceTree = ""; }; + 624717161B22333200C11912 /* HTMLNodeTraversal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeTraversal.h; sourceTree = ""; }; + 624717171B22333200C11912 /* HTMLNodeTraversal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeTraversal.m; sourceTree = ""; }; + 6247171B1B2240B800C11912 /* HTMLTreeWalkerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLTreeWalkerTests.m; sourceTree = ""; }; + 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeFilter.m; sourceTree = ""; }; + 624717BA1B22009200B38302 /* HTMLTreeWalker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLTreeWalker.h; sourceTree = ""; }; + 624717BB1B22009200B38302 /* HTMLTreeWalker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLTreeWalker.m; sourceTree = ""; }; + 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeIterator.h; sourceTree = ""; }; + 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeIterator.m; sourceTree = ""; }; + 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeFilter.h; 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 = ""; }; @@ -380,32 +398,40 @@ name = Tokenizer; sourceTree = ""; }; - 623719441AA1472B002E03C8 /* Nodes */ = { + 623719441AA1472B002E03C8 /* DOM */ = { isa = PBXGroup; children = ( - 624B28BB1B03DAB70048D328 /* HTMLNodeFilter.h */, - 623719431AA12EE8002E03C8 /* HTMLQuirksMode.h */, - 62362A3F1A9FDE8A00301989 /* HTMLNodes.h */, + 62362A3F1A9FDE8A00301989 /* HTMLDOM.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 */, + 623406E41ADB05AD004677A3 /* HTMLDocumentFragment.h */, + 623406E51ADB05AD004677A3 /* HTMLDocumentFragment.m */, + 6279F87219E1808D00F12EE5 /* HTMLElement.h */, + 6279F87319E1808D00F12EE5 /* HTMLElement.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 */, + 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */, + 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */, + 624717BA1B22009200B38302 /* HTMLTreeWalker.h */, + 624717BB1B22009200B38302 /* HTMLTreeWalker.m */, + 624717161B22333200C11912 /* HTMLNodeTraversal.h */, + 624717171B22333200C11912 /* HTMLNodeTraversal.m */, + 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */, + 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */, + 6238C9831AB8D6330006512E /* HTMLKitDOMExceptions.h */, + 6238C9841AB8D6330006512E /* HTMLKitDOMExceptions.m */, + 628B7CE61A080E1000602C87 /* HTMLNamespaces.h */, + 623719431AA12EE8002E03C8 /* HTMLQuirksMode.h */, ); - name = Nodes; + name = DOM; sourceTree = ""; }; 623975581AC362A5007E26F1 /* Tree Construction */ = { @@ -419,15 +445,15 @@ name = "Tree Construction"; sourceTree = ""; }; - 624B9FB71AE072CB00646C4C /* Nodes */ = { + 624B9FB71AE072CB00646C4C /* DOM */ = { isa = PBXGroup; children = ( - 623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */, - 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */, + 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */, + 6247171B1B2240B800C11912 /* HTMLTreeWalkerTests.m */, 624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */, 62EC7AE51AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m */, ); - name = Nodes; + name = DOM; sourceTree = ""; }; 624B9FB81AE072D500646C4C /* Categories */ = { @@ -438,6 +464,14 @@ name = Categories; sourceTree = ""; }; + 624E1A2D1B1D1C8A00E66AAC /* Structures */ = { + isa = PBXGroup; + children = ( + 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */, + ); + name = Structures; + sourceTree = ""; + }; 625A14A219C7829400AD0C32 = { isa = PBXGroup; children = ( @@ -485,11 +519,9 @@ children = ( 625A14BC19C7829400AD0C32 /* HTMLKit.h */, 625A14BD19C7829400AD0C32 /* HTMLKit.m */, - 6238C9831AB8D6330006512E /* HTMLKitExceptions.h */, - 6238C9841AB8D6330006512E /* HTMLKitExceptions.m */, 62AE593219F97CCA0043F069 /* Tokenizing */, 628E16EC1ADAE71700B15A06 /* Parsing */, - 623719441AA1472B002E03C8 /* Nodes */, + 623719441AA1472B002E03C8 /* DOM */, 628E16ED1ADAE73700B15A06 /* Categories */, 628E16EE1ADAE75300B15A06 /* Structures */, 62079BE61AF5656600D3B402 /* Selectors */, @@ -514,8 +546,9 @@ children = ( 6236738C1AC0CD2400FF89B3 /* Tokenizer */, 623975581AC362A5007E26F1 /* Tree Construction */, - 624B9FB71AE072CB00646C4C /* Nodes */, + 624B9FB71AE072CB00646C4C /* DOM */, 624B9FB81AE072D500646C4C /* Categories */, + 624E1A2D1B1D1C8A00E66AAC /* Structures */, 625A14CB19C7829400AD0C32 /* Supporting Files */, 624AB3161B0508AE00F3830D /* CSSTypeSelectorTests.m */, 624AB3191B050A4D00F3830D /* CSSAttributeSelectorTests.m */, @@ -544,7 +577,6 @@ 6234BEED1AABBF1400DEB15F /* HTMLStackOfOpenElements.m */, 62363C3B1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h */, 62363C3C1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m */, - 628B7CE61A080E1000602C87 /* HTMLNamespaces.h */, 6223211D1A969B9300BACED5 /* HTMLElementTypes.h */, 6234C3361AB3BF710046F527 /* HTMLElementAdjustment.h */, 6279F87119E17DC700F12EE5 /* HTMLParserInsertionModes.h */, @@ -611,6 +643,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 624717181B22333200C11912 /* HTMLNodeTraversal.h in Headers */, 6235CE9B1AA509430026937B /* NSString+HTMLKit.h in Headers */, 62D89DB11AE7081600B6243D /* HTMLTokenizerStates.h in Headers */, 62079BE91AF56F1E00D3B402 /* CSSSelector.h in Headers */, @@ -622,10 +655,9 @@ 62D89DB51AE7081600B6243D /* HTMLElementAdjustment.h in Headers */, 62D89DB61AE7081600B6243D /* HTMLParserInsertionModes.h in Headers */, 62D89DB71AE7081600B6243D /* HTMLQuirksMode.h in Headers */, - 62D89DB01AE7080300B6243D /* HTMLNodes.h in Headers */, + 62D89DB01AE7080300B6243D /* HTMLDOM.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 */, @@ -634,8 +666,8 @@ 623406E11ADB04F9004677A3 /* HTMLTemplate.h in Headers */, 624B28B81B03DA820048D328 /* CSSTypeSelector.h in Headers */, 6234BEEE1AABBF1400DEB15F /* HTMLStackOfOpenElements.h in Headers */, - 6238C9851AB8D6330006512E /* HTMLKitExceptions.h in Headers */, 624AB3221B053A5800F3830D /* CSSSimpleSelector.h in Headers */, + 6238C9851AB8D6330006512E /* HTMLKitDOMExceptions.h in Headers */, 6238039F1AB63A8C008A53D0 /* HTMLEOFToken.h in Headers */, 6279F87419E1808D00F12EE5 /* HTMLElement.h in Headers */, 62AE593F19F9907C0043F069 /* HTMLTagToken.h in Headers */, @@ -643,7 +675,9 @@ 624B28C41B04190D0048D328 /* NSCharacterSet+HTMLKit.h in Headers */, 624493A619CCC54100BCDDF4 /* HTMLTokenizer.h in Headers */, 62AE593A19F97E1C0043F069 /* HTMLDOCTYPEToken.h in Headers */, + 6247A9471B152F8C00CCF25C /* HTMLNodeFilter.h in Headers */, 623424881AB467B200726190 /* HTMLOrderedDictionary.h in Headers */, + 6247A9431B152F4F00CCF25C /* HTMLNodeIterator.h in Headers */, 625A150419C783EB00AD0C32 /* HTMLKit.h in Headers */, 625A150819C78ABA00AD0C32 /* HTMLInputStreamReader.h in Headers */, 6238579B1A9E8934003A45D9 /* HTMLComment.h in Headers */, @@ -654,6 +688,7 @@ 623857961A9E8606003A45D9 /* HTMLDocumentType.h in Headers */, 623406E61ADB05AD004677A3 /* HTMLDocumentFragment.h in Headers */, 62363C3D1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h in Headers */, + 624717BC1B22009200B38302 /* HTMLTreeWalker.h in Headers */, 62362A3C1A9FA70400301989 /* HTMLText.h in Headers */, 6234584E1A9D2FA4009BD491 /* HTMLNode.h in Headers */, ); @@ -807,18 +842,20 @@ files = ( 62362A3E1A9FA70400301989 /* HTMLText.m in Sources */, 6235CE9D1AA509430026937B /* NSString+HTMLKit.m in Sources */, + 6247A9451B152F4F00CCF25C /* HTMLNodeIterator.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 */, + 624717BE1B22009200B38302 /* HTMLTreeWalker.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 */, + 6238C9871AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */, + 6247171A1B22333200C11912 /* HTMLNodeTraversal.m in Sources */, 62F873ED19E088C90062683C /* HTMLParser.m in Sources */, 623803A11AB63A8C008A53D0 /* HTMLEOFToken.m in Sources */, 624B28C61B04190D0048D328 /* NSCharacterSet+HTMLKit.m in Sources */, @@ -826,6 +863,7 @@ 625A150A19C78ABA00AD0C32 /* HTMLInputStreamReader.m in Sources */, 623857931A9E772B003A45D9 /* HTMLDocument.m in Sources */, 623406E81ADB05AD004677A3 /* HTMLDocumentFragment.m in Sources */, + 624717B91B21FE5400B38302 /* HTMLNodeFilter.m in Sources */, 624493A819CCC54100BCDDF4 /* HTMLTokenizer.m in Sources */, 624AB3201B05373600F3830D /* CSSSimpleSequence.m in Sources */, 623F13AC1AFC11D900C90BF4 /* CSSTokenizer.m in Sources */, @@ -848,6 +886,7 @@ files = ( 6239755B1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */, 623CAF9E1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */, + 6247171D1B2240B800C11912 /* HTMLTreeWalkerTests.m in Sources */, 62EC7AE71AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m in Sources */, 624AB31B1B050A4D00F3830D /* CSSAttributeSelectorTests.m in Sources */, 624B9FB61AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */, @@ -855,7 +894,7 @@ 6236738F1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */, 624AC90219FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */, 624B9FB31AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */, - 623916C81AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */, + 623916C81AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m in Sources */, 62D8345A19FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */, 6239755F1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */, 624FC37B1AE591D80015DDF9 /* HTMLKitNodesTests.m in Sources */, @@ -868,18 +907,20 @@ files = ( 62362A3D1A9FA70400301989 /* HTMLText.m in Sources */, 6235CE9C1AA509430026937B /* NSString+HTMLKit.m in Sources */, + 6247A9441B152F4F00CCF25C /* HTMLNodeIterator.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 */, + 624717BD1B22009200B38302 /* HTMLTreeWalker.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 */, + 6238C9861AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */, + 624717191B22333200C11912 /* HTMLNodeTraversal.m in Sources */, 62F873EC19E088C90062683C /* HTMLParser.m in Sources */, 623803A01AB63A8C008A53D0 /* HTMLEOFToken.m in Sources */, 624B28C51B04190D0048D328 /* NSCharacterSet+HTMLKit.m in Sources */, @@ -887,6 +928,7 @@ 625A150919C78ABA00AD0C32 /* HTMLInputStreamReader.m in Sources */, 623857921A9E772B003A45D9 /* HTMLDocument.m in Sources */, 623406E71ADB05AD004677A3 /* HTMLDocumentFragment.m in Sources */, + 624717B81B21FE5400B38302 /* HTMLNodeFilter.m in Sources */, 624493A719CCC54100BCDDF4 /* HTMLTokenizer.m in Sources */, 624AB31F1B05373600F3830D /* CSSSimpleSequence.m in Sources */, 623F13AB1AFC11D900C90BF4 /* CSSTokenizer.m in Sources */, @@ -909,6 +951,7 @@ files = ( 6239755A1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */, 623CAF9D1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */, + 6247171C1B2240B800C11912 /* HTMLTreeWalkerTests.m in Sources */, 62EC7AE61AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m in Sources */, 624AB31A1B050A4D00F3830D /* CSSAttributeSelectorTests.m in Sources */, 624B9FB51AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */, @@ -916,7 +959,7 @@ 6236738E1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */, 624AC90119FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */, 624B9FB21AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */, - 623916C71AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */, + 623916C71AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m in Sources */, 62D8345919FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */, 6239755E1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */, 624FC37A1AE591D80015DDF9 /* HTMLKitNodesTests.m in Sources */, @@ -1066,7 +1109,6 @@ FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", - "$(SDKROOT)/Developer/Library/Frameworks", ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch"; @@ -1089,7 +1131,6 @@ FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", - "$(SDKROOT)/Developer/Library/Frameworks", ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch"; @@ -1138,7 +1179,6 @@ buildSettings = { BUNDLE_LOADER = ""; FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", "$(DEVELOPER_FRAMEWORKS_DIR)", ); @@ -1162,7 +1202,6 @@ buildSettings = { BUNDLE_LOADER = ""; FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", "$(DEVELOPER_FRAMEWORKS_DIR)", ); diff --git a/HTMLKit/HTMLNodes.h b/HTMLKit/HTMLDOM.h similarity index 79% rename from HTMLKit/HTMLNodes.h rename to HTMLKit/HTMLDOM.h index f0ffa0e..c570ae0 100644 --- a/HTMLKit/HTMLNodes.h +++ b/HTMLKit/HTMLDOM.h @@ -9,8 +9,11 @@ #import "HTMLNode.h" #import "HTMLDocument.h" #import "HTMLDocumentType.h" +#import "HTMLDocumentFragment.h" #import "HTMLElement.h" #import "HTMLComment.h" #import "HTMLText.h" #import "HTMLTemplate.h" -#import "HTMLDocumentFragment.h" +#import "HTMLNodeIterator.h" +#import "HTMLTreeWalker.h" +#import "HTMLNodeFilter.h" diff --git a/HTMLKit/HTMLDocument.h b/HTMLKit/HTMLDocument.h index ddcec2b..f8a0485 100644 --- a/HTMLKit/HTMLDocument.h +++ b/HTMLKit/HTMLDocument.h @@ -27,6 +27,16 @@ typedef NS_ENUM(short, HTMLDocumentReadyState) @property (nonatomic, assign, readonly) HTMLDocumentReadyState readyState; +@property (nonatomic, strong) HTMLElement *rootElement; + +@property (nonatomic, strong) HTMLElement *documentElement; + +@property (nonatomic, strong) HTMLElement *head; + +@property (nonatomic, strong) HTMLElement *body; + ++ (instancetype)documentWithString:(NSString *)string; + - (HTMLNode *)adoptNode:(HTMLNode *)node; - (HTMLDocument *)associatedInertTemplateDocument; diff --git a/HTMLKit/HTMLDocument.m b/HTMLKit/HTMLDocument.m index 11e2a42..09baa88 100644 --- a/HTMLKit/HTMLDocument.m +++ b/HTMLKit/HTMLDocument.m @@ -7,16 +7,25 @@ // #import "HTMLDocument.h" -#import "HTMLKitExceptions.h" +#import "HTMLParser.h" +#import "HTMLNodeIterator.h" +#import "HTMLKitDOMExceptions.h" @interface HTMLNode (Private) @property (nonatomic, weak) HTMLDocument *ownerDocument; @property (nonatomic, weak) HTMLNode *parentNode; @end +@interface HTMLNodeIterator (Private) +- (void)runRemovingStepsForNode:(HTMLNode *)oldNode + withOldParent:(HTMLNode *)oldParent + andOldPreviousSibling:(HTMLNode *)oldPreviousSibling; +@end + @interface HTMLDocument () { HTMLDocument *_inertTemplateDocument; + NSMutableArray *_nodeIterators; } @property (nonatomic, assign) HTMLDocumentReadyState readyState; @end @@ -25,11 +34,18 @@ #pragma mark - Init ++ (instancetype)documentWithString:(NSString *)string +{ + HTMLParser *parser = [[HTMLParser alloc] initWithString:string]; + return [parser parseDocument]; +} + - (instancetype)init { self = [super initWithName:@"#document" type:HTMLNodeDocument]; if (self) { _readyState = HTMLDocumentLoading; + _nodeIterators = [NSMutableArray new]; } return self; } @@ -57,6 +73,91 @@ } } +#pragma mark - + +- (HTMLElement *)rootElement +{ + for (HTMLNode *node = self.firstChild; node; node = node.nextSibling) { + if (node.nodeType == HTMLNodeElement) { + return node.asElement; + } + } + return nil; +} + +- (void)setRootElement:(HTMLElement *)rootElement +{ + [self replaceChildNode:self.rootElement withNode:rootElement]; +} + +- (HTMLElement *)documentElement +{ + for (HTMLNode *node in [self nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]) { + if ([node.asElement.tagName isEqualToString:@"html"]) { + return node.asElement; + } + } + return nil; +} + +- (void)setDocumentElement:(HTMLElement *)documentElement +{ + [self replaceChildNode:self.documentElement withNode:documentElement]; +} + +- (HTMLElement *)head +{ + for (HTMLNode *node in [self nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]) { + if ([node.asElement.tagName isEqualToString:@"head"]) { + return node.asElement; + } + } + return nil; +} + +- (void)setHead:(HTMLElement *)head +{ + [self replaceChildNode:self.head withNode:head]; +} + +- (HTMLElement *)body +{ + for (HTMLNode *node in [self nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]) { + if ([node.asElement.tagName isEqualToString:@"body"]) { + return node.asElement; + } + } + return nil; +} + +- (void)setBody:(HTMLElement *)body +{ + [self replaceChildNode:self.body withNode:body]; +} + +#pragma mark - Node Iterators + +- (void)attachNodeIterator:(HTMLNodeIterator *)iterator +{ + [_nodeIterators addObject:iterator]; +} + +- (void)detachNodeIterator:(HTMLNodeIterator *)iterator +{ + [_nodeIterators removeObject:iterator]; +} + +- (void)runRemovingStepsForNode:(HTMLNode *)oldNode + withOldParent:(HTMLNode *)oldParent + andOldPreviousSibling:(HTMLNode *)oldPreviousSibling +{ + for (HTMLNodeIterator *iterator in _nodeIterators) { + [iterator runRemovingStepsForNode:oldNode + withOldParent:oldParent + andOldPreviousSibling:oldPreviousSibling]; + } +} + #pragma mark - Mutation Algorithms - (HTMLNode *)adoptNode:(HTMLNode *)node @@ -65,7 +166,7 @@ return nil; } - if (node.type == HTMLNodeDocument) { + if (node.nodeType == HTMLNodeDocument) { [NSException raise:HTMLKitNotSupportedError format:@"%@: Not Fount Error, adopting a document node. The operation is not supported.", NSStringFromSelector(_cmd)]; } diff --git a/HTMLKit/HTMLDocumentFragment.m b/HTMLKit/HTMLDocumentFragment.m index 6b549a8..adcde5e 100644 --- a/HTMLKit/HTMLDocumentFragment.m +++ b/HTMLKit/HTMLDocumentFragment.m @@ -33,7 +33,7 @@ { NSMutableString *content = [NSMutableString string]; for (HTMLNode *node in self.treeEnumerator) { - if (node.type == HTMLNodeText) { + if (node.nodeType == HTMLNodeText) { [content appendString:[(HTMLText *)node data]]; } } diff --git a/HTMLKit/HTMLElement.h b/HTMLKit/HTMLElement.h index 1085162..6343516 100644 --- a/HTMLKit/HTMLElement.h +++ b/HTMLKit/HTMLElement.h @@ -24,7 +24,7 @@ - (instancetype)initWithTagName:(NSString *)tagName; - (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes; -- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes namespace:(HTMLNamespace)htmlNamespace; +- (instancetype)initWithTagName:(NSString *)tagName namespace:(HTMLNamespace)htmlNamespace attributes:(NSDictionary *)attributes; - (BOOL)hasAttribute:(NSString *)name; - (NSString *)objectForKeyedSubscript:(NSString *)name; diff --git a/HTMLKit/HTMLElement.m b/HTMLKit/HTMLElement.m index e12b5f7..6083537 100644 --- a/HTMLKit/HTMLElement.m +++ b/HTMLKit/HTMLElement.m @@ -7,6 +7,7 @@ // #import "HTMLElement.h" +#import "HTMLParser.h" #import "HTMLDocument.h" #import "HTMLText.h" @@ -35,10 +36,10 @@ - (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes { - return [self initWithTagName:tagName attributes:attributes namespace:HTMLNamespaceHTML]; + return [self initWithTagName:tagName namespace:HTMLNamespaceHTML attributes:attributes]; } -- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes namespace:(HTMLNamespace)htmlNamespace +- (instancetype)initWithTagName:(NSString *)tagName namespace:(HTMLNamespace)htmlNamespace attributes:(NSDictionary *)attributes { self = [super initWithName:tagName type:HTMLNodeElement]; if (self) { @@ -87,8 +88,8 @@ - (NSString *)textContent { NSMutableString *content = [NSMutableString string]; - for (HTMLNode *node in self.treeEnumerator) { - if (node.type == HTMLNodeText) { + for (HTMLNode *node in self.nodeIterator) { + if (node.nodeType == HTMLNodeText) { [content appendString:[(HTMLText *)node data]]; } } @@ -101,6 +102,14 @@ [self replaceAllChildNodesWithNode:node]; } +- (void)setInnerHTML:(NSString *)innerHTML +{ + HTMLParser *parser = [[HTMLParser alloc] initWithString:innerHTML]; + NSArray *fragmentNodes = [parser parseFragmentWithContextElement:self]; + [self removeAllChildNodes]; + [self appendNodes:fragmentNodes]; +} + #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone @@ -137,8 +146,8 @@ return result; } - if ([self.tagName isEqualToAny:@"pre", @"textarea", @"listing", nil] && self.firstChiledNode.type == HTMLNodeText) { - HTMLText *textNode = (HTMLText *)self.firstChiledNode; + if ([self.tagName isEqualToAny:@"pre", @"textarea", @"listing", nil] && self.firstChild.nodeType == HTMLNodeText) { + HTMLText *textNode = (HTMLText *)self.firstChild; if ([textNode.data hasPrefix:@"\n"]) { [result appendString:@"\n"]; } diff --git a/HTMLKit/HTMLKitExceptions.h b/HTMLKit/HTMLKitDOMExceptions.h similarity index 100% rename from HTMLKit/HTMLKitExceptions.h rename to HTMLKit/HTMLKitDOMExceptions.h diff --git a/HTMLKit/HTMLKitExceptions.m b/HTMLKit/HTMLKitDOMExceptions.m similarity index 91% rename from HTMLKit/HTMLKitExceptions.m rename to HTMLKit/HTMLKitDOMExceptions.m index 8234e00..f7fcacc 100644 --- a/HTMLKit/HTMLKitExceptions.m +++ b/HTMLKit/HTMLKitDOMExceptions.m @@ -6,7 +6,7 @@ // Copyright (c) 2015 BrainCookie. All rights reserved. // -#import "HTMLKitExceptions.h" +#import "HTMLKitDOMExceptions.h" NSString * const HTMLKitHierarchyRequestError = @"HierarchyRequestError"; NSString * const HTMLKitNotFoundError = @"NotFoundError"; diff --git a/HTMLKit/HTMLNode.h b/HTMLKit/HTMLNode.h index 332fb4f..eb07dbc 100644 --- a/HTMLKit/HTMLNode.h +++ b/HTMLKit/HTMLNode.h @@ -7,6 +7,7 @@ // #import +#import "HTMLNodeIterator.h" typedef NS_ENUM(short, HTMLNodeType) { @@ -24,28 +25,37 @@ typedef NS_ENUM(short, HTMLNodeType) HTMLNodeNotation = 12 // historical }; +typedef NS_ENUM(unsigned short, HTMLDocumentPosition) +{ + HTMLDocumentPositionEquivalent = 0x0, + HTMLDocumentPositionDisconnected = 0x01, + HTMLDocumentPositionPreceding = 0x02, + HTMLDocumentPositionFollowing = 0x04, + HTMLDocumentPositionContains = 0x08, + HTMLDocumentPositionContainedBy = 0x10, + HTMLDocumentPositionImplementationSpecific = 0x20 +}; + @class HTMLDocument; @class HTMLElement; @interface HTMLNode : NSObject -@property (nonatomic, assign, readonly) HTMLNodeType type; +@property (nonatomic, assign, readonly) HTMLNodeType nodeType; @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 *firstChild; -@property (nonatomic, strong, readonly) HTMLNode *lastChildNode; +@property (nonatomic, strong, readonly) HTMLNode *lastChild; @property (nonatomic, strong, readonly) HTMLNode *previousSibling; @@ -53,10 +63,16 @@ typedef NS_ENUM(short, HTMLNodeType) @property (nonatomic, copy) NSString *textContent; +@property (nonatomic, strong, readonly) NSString *outerHTML; + +@property (nonatomic, copy) NSString *innerHTML; + - (instancetype)init NS_UNAVAILABLE; - (instancetype)initWithName:(NSString *)name type:(HTMLNodeType)type; +- (HTMLElement *)asElement; + - (BOOL)hasChildNodes; - (BOOL)hasChildNodeOfType:(HTMLNodeType)type; @@ -91,15 +107,19 @@ typedef NS_ENUM(short, HTMLNodeType) - (void)removeAllChildNodes; +- (HTMLDocumentPosition)compareDocumentPositionWithNode:(HTMLNode *)node; +- (BOOL)isDescendantOfNode:(HTMLNode *)node; +- (BOOL)containsNode:(HTMLNode *)node; + - (void)enumerateChildNodesUsingBlock:(void (^)(HTMLNode *node, NSUInteger idx, BOOL *stop))block; -- (NSEnumerator *)treeEnumerator; +- (void)enumerateChildElementsUsingBlock:(void (^)(HTMLElement *element, NSUInteger idx, BOOL *stop))block; -- (NSEnumerator *)reverseTreeEnumerator; - -- (NSString *)outerHTML; - -- (NSString *)innerHTML; +- (HTMLNodeIterator *)nodeIterator; +- (HTMLNodeIterator *)nodeIteratorWithShowOptions:(HTMLNodeFilterShowOptions)showOptions + filter:(id)filter; +- (HTMLNodeIterator *)nodeIteratorWithShowOptions:(HTMLNodeFilterShowOptions)showOptions + filterBlock:(HTMLNodeFilterValue (^)(HTMLNode *node))filter; - (NSString *)treeDescription; diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index 470cc15..fdff072 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -12,8 +12,13 @@ #import "HTMLElement.h" #import "HTMLText.h" #import "HTMLComment.h" -#import "HTMLKitExceptions.h" -#import "HTMLNodeTreeEnumerator.h" +#import "HTMLKitDOMExceptions.h" + +@interface HTMLDocument (Private) +- (void)runRemovingStepsForNode:(HTMLNode *)oldNode + withOldParent:(HTMLNode *)oldParent + andOldPreviousSibling:(HTMLNode *)oldPreviousSibling; +@end @interface HTMLNode () { @@ -32,7 +37,7 @@ self = [super init]; if (self) { _name = name; - _type = type; + _nodeType = type; _childNodes = [NSMutableOrderedSet new]; } return self; @@ -42,7 +47,7 @@ - (HTMLDocument *)ownerDocument { - if (_type == HTMLNodeDocument) { + if (_nodeType == HTMLNodeDocument) { return (HTMLDocument *)self; } else { return _ownerDocument; @@ -55,12 +60,6 @@ [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; @@ -68,15 +67,15 @@ - (HTMLElement *)parentElement { - return _parentNode.type == HTMLNodeElement ? (HTMLElement *)_parentNode : nil; + return _parentNode.nodeType == HTMLNodeElement ? (HTMLElement *)_parentNode : nil; } -- (HTMLNode *)firstChiledNode +- (HTMLNode *)firstChild { return self.childNodes.firstObject; } -- (HTMLNode *)lastChildNode +- (HTMLNode *)lastChild { return self.childNodes.lastObject; } @@ -104,6 +103,13 @@ return nil; } +#pragma mark - Cast + +- (HTMLElement *)asElement +{ + return (HTMLElement *)self; +} + #pragma mark - Child Nodes - (BOOL)hasChildNodes @@ -114,7 +120,7 @@ - (BOOL)hasChildNodeOfType:(HTMLNodeType)type { NSUInteger index = [self.childNodes indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { - if ([(HTMLNode *)obj type] == type) { + if ([(HTMLNode *)obj nodeType] == type) { *stop = YES; return YES; } @@ -141,13 +147,13 @@ - (HTMLNode *)prependNode:(HTMLNode *)node { - return [self insertNode:node beforeChildNode:self.firstChiledNode]; + return [self insertNode:node beforeChildNode:self.firstChild]; } - (void)prependNodes:(NSArray *)nodes { for (id node in nodes.reverseObjectEnumerator) { - [self insertNode:node beforeChildNode:self.firstChiledNode]; + [self insertNode:node beforeChildNode:self.firstChild]; } } @@ -171,7 +177,7 @@ [self.ownerDocument adoptNode:node]; - NSArray *nodes = node.type == HTMLNodeDocumentFragment ? [NSArray arrayWithArray:node.childNodes.array] : @[node]; + NSArray *nodes = node.nodeType == HTMLNodeDocumentFragment ? [NSArray arrayWithArray:node.childNodes.array] : @[node]; NSUInteger index = [self indexOfChildNode:child]; if (index != NSNotFound) { @@ -181,7 +187,7 @@ [(NSMutableOrderedSet *)self.childNodes addObjectsFromArray:nodes]; } - if (node.type == HTMLNodeDocumentFragment) { + if (node.nodeType == HTMLNodeDocumentFragment) { [node removeAllChildNodes]; } @@ -224,8 +230,16 @@ NSStringFromSelector(_cmd), child]; } + HTMLNode *oldNode = child; + HTMLNode *oldParent = child.parentNode; + HTMLNode *oldPreviousSibling = child.previousSibling; + [(NSMutableOrderedSet *)self.childNodes removeObject:child]; child.parentNode = nil; + + [self.ownerDocument runRemovingStepsForNode:oldNode + withOldParent:oldParent + andOldPreviousSibling:oldPreviousSibling]; return child; } @@ -249,6 +263,86 @@ [(NSMutableOrderedSet *)self.childNodes removeAllObjects]; } +- (HTMLDocumentPosition)compareDocumentPositionWithNode:(HTMLNode *)otherNode +{ + if (otherNode == nil) { + return HTMLDocumentPositionDisconnected; + } + + if (self == otherNode) { + return HTMLDocumentPositionEquivalent; + } + + NSArray * (^ ancestorNodes) (HTMLNode *) = ^ NSArray * (HTMLNode *node) { + NSMutableArray *ancestors = [NSMutableArray array]; + for (HTMLNode *node = self; node; node = node.parentNode) { + [ancestors addObject:node]; + } + return ancestors; + }; + + NSArray *ancestors1 = ancestorNodes(self); + NSArray *ancestors2 = ancestorNodes(otherNode); + + if (ancestors1.lastObject != ancestors2.lastObject) { + return HTMLDocumentPositionDisconnected | + HTMLDocumentPositionImplementationSpecific | + HTMLDocumentPositionFollowing; + } + + for (NSUInteger i = MIN(ancestors1.count - 1, ancestors2.count - 1); i; --i) { + HTMLNode *child1 = ancestors1[i]; + HTMLNode *child2 = ancestors2[i]; + + if (child1 != child2) { + for (HTMLNode *sibling = child1.nextSibling; sibling; sibling = sibling.nextSibling) { + if (sibling == child2) { + return HTMLDocumentPositionFollowing; + } + } + return HTMLDocumentPositionPreceding; + } + } + + if (ancestors1.count < ancestors2.count) { + return HTMLDocumentPositionContainedBy | HTMLDocumentPositionFollowing; + } else { + return HTMLDocumentPositionContains | HTMLDocumentPositionPreceding; + } +} + +- (BOOL)isDescendantOfNode:(HTMLNode *)otherNode +{ + if (otherNode == nil) { + return NO; + } + + if (self.ownerDocument != otherNode.ownerDocument) { + return NO; + } + + if (!otherNode.hasChildNodes) { + return NO; + } + + if (otherNode.nodeType == HTMLNodeDocument) { + return self.nodeType != HTMLNodeDocument && self.ownerDocument == otherNode; + } + + for (HTMLNode *parentNode = self.parentNode; parentNode; parentNode = parentNode.parentNode) { + if (parentNode == otherNode) { + return YES; + } + } + + return NO; +} + +- (BOOL)containsNode:(HTMLNode *)otherNode +{ + return self == otherNode || [otherNode isDescendantOfNode:self]; +} + #pragma mark - Enumeration - (void)enumerateChildNodesUsingBlock:(void (^)(HTMLNode *node, NSUInteger idx, BOOL *stop))block @@ -262,27 +356,34 @@ }]; } -- (NSEnumerator *)treeEnumerator +- (void)enumerateChildElementsUsingBlock:(void (^)(HTMLElement *element, NSUInteger idx, BOOL *stop))block { - return [[HTMLNodeTreeEnumerator alloc] initWithNode:self reverse:NO]; -} - -- (NSEnumerator *)reverseTreeEnumerator -{ - return [[HTMLNodeTreeEnumerator alloc] initWithNode:self reverse:YES]; -} - -- (void)doInsertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child -{ - NSArray *nodes = node.type == HTMLNodeDocumentFragment ? node.childNodes.array : @[node]; - - NSUInteger index = [self indexOfChildNode:child]; - if (index != NSNotFound) { - NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, nodes.count)]; - [(NSMutableOrderedSet *)self.childNodes insertObjects:nodes atIndexes:indexes]; - } else { - [(NSMutableOrderedSet *)self.childNodes addObjectsFromArray:nodes]; + if (block == nil) { + return; } + + [self.childNodes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + if ([obj isKindOfClass:[HTMLElement class]]) { + block([obj asElement], idx, stop); + } + }]; +} + +- (HTMLNodeIterator *)nodeIterator +{ + return [self nodeIteratorWithShowOptions:HTMLNodeFilterShowAll filter:nil]; +} + +- (HTMLNodeIterator *)nodeIteratorWithShowOptions:(HTMLNodeFilterShowOptions)showOptions + filter:(id)filter +{ + return [[HTMLNodeIterator alloc] initWithNode:self showOptions:showOptions filter:filter]; +} + +- (HTMLNodeIterator *)nodeIteratorWithShowOptions:(HTMLNodeFilterShowOptions)showOptions + filterBlock:(HTMLNodeFilterValue (^)(HTMLNode *node))filter +{ + return [HTMLNodeIterator iteratorWithNode:self showOptions:showOptions filter:filter]; } #ifndef HTMLKIT_NO_DOM_CHECKS @@ -291,9 +392,9 @@ NS_INLINE void CheckParentValid(HTMLNode *parent, NSString *cmd) { - if (parent.type != HTMLNodeDocument && - parent.type != HTMLNodeDocumentFragment && - parent.type != HTMLNodeElement) { + if (parent.nodeType != HTMLNodeDocument && + parent.nodeType != HTMLNodeDocumentFragment && + parent.nodeType != HTMLNodeElement) { [NSException raise:HTMLKitHierarchyRequestError format:@"%@: Hierarchy Request Error, inserting into %@ is not allowed. The operation would yield an incorrect node tree.", cmd, parent.name]; @@ -312,11 +413,11 @@ NS_INLINE void CheckChildsParent(HTMLNode *parent, HTMLNode *child, NSString *cm NS_INLINE void CheckInsertedNodeValid(HTMLNode *node, NSString *cmd) { - if (node.type != HTMLNodeDocumentFragment && - node.type != HTMLNodeDocumentType && - node.type != HTMLNodeElement && - node.type != HTMLNodeText && - node.type != HTMLNodeComment) { + if (node.nodeType != HTMLNodeDocumentFragment && + node.nodeType != HTMLNodeDocumentType && + node.nodeType != HTMLNodeElement && + node.nodeType != HTMLNodeText && + node.nodeType != HTMLNodeComment) { [NSException raise:HTMLKitHierarchyRequestError format:@"%@: Hierarchy Request Error, inserting a %@ is not allowed. The operation would yield an incorrect node tree.", cmd, node.name]; @@ -325,13 +426,13 @@ NS_INLINE void CheckInsertedNodeValid(HTMLNode *node, NSString *cmd) NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSString *cmd) { - if (node.type == HTMLNodeText && parent.type == HTMLNodeDocument) { + if (node.nodeType == HTMLNodeText && parent.nodeType == 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) { + if (node.nodeType == HTMLNodeDocumentType && parent.nodeType != 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]; @@ -354,24 +455,24 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin NSStringFromSelector(_cmd), self, node]; }; - if (self.type == HTMLNodeDocument) { - switch (node.type) { + if (self.nodeType == HTMLNodeDocument) { + switch (node.nodeType) { case HTMLNodeDocumentFragment: if (node.childNodesCount > 1 || [node hasChildNodeOfType:HTMLNodeText]) { hierarchyError(); } else if (node.childNodesCount == 1) { if ([self hasChildNodeOfType:HTMLNodeElement] || - child.type == HTMLNodeDocumentType || - child.nextSibling.type == HTMLNodeDocumentType) { + child.nodeType == HTMLNodeDocumentType || + child.nextSibling.nodeType == HTMLNodeDocumentType) { hierarchyError(); } } break; case HTMLNodeElement: if ([self hasChildNodeOfType:HTMLNodeElement] || - child.type == HTMLNodeDocumentType || - (child != nil && child.nextSibling.type == HTMLNodeDocumentType)) { + child.nodeType == HTMLNodeDocumentType || + (child != nil && child.nextSibling.nodeType == HTMLNodeDocumentType)) { hierarchyError(); } break; @@ -406,21 +507,21 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin void (^ checkParentHasAnotherChildOfType)(HTMLNodeType) = ^ void (HTMLNodeType type) { [self enumerateChildNodesUsingBlock:^(HTMLNode *node, NSUInteger idx, BOOL *stop) { - if (node.type == type && node != child) { + if (node.nodeType == type && node != child) { *stop = YES; hierarchyError(); } }]; }; - if (self.type == HTMLNodeDocument) { - switch (node.type) { + if (self.nodeType == HTMLNodeDocument) { + switch (node.nodeType) { case HTMLNodeDocumentFragment: if (node.childNodesCount > 1 || [node hasChildNodeOfType:HTMLNodeText]) { hierarchyError(); } else if (node.childNodesCount == 1) { - if (child.nextSibling.type == HTMLNodeDocumentType) { + if (child.nextSibling.nodeType == HTMLNodeDocumentType) { hierarchyError(); } checkParentHasAnotherChildOfType(HTMLNodeElement); @@ -428,7 +529,7 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin break; case HTMLNodeElement: { - if (child.nextSibling.type == HTMLNodeDocumentType) { + if (child.nextSibling.nodeType == HTMLNodeDocumentType) { hierarchyError(); } checkParentHasAnotherChildOfType(HTMLNodeElement); @@ -436,7 +537,7 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin } case HTMLNodeDocumentType: { - if (child.previousSibling.type == HTMLNodeElement) { + if (child.previousSibling.nodeType == HTMLNodeElement) { hierarchyError(); } checkParentHasAnotherChildOfType(HTMLNodeDocumentType); @@ -454,7 +555,7 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin - (id)copyWithZone:(NSZone *)zone { - HTMLNode *copy = [[self.class alloc] initWithName:self.name type:self.type]; + HTMLNode *copy = [[self.class alloc] initWithName:self.name type:self.nodeType]; return copy; } @@ -471,6 +572,11 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin return [[self.childNodes.array valueForKey:@"outerHTML"] componentsJoinedByString:@""]; } +- (void)setInnerHTML:(NSString *)outerHTML +{ + [self doesNotRecognizeSelector:_cmd]; +} + #pragma mark - Description - (NSString *)treeDescription diff --git a/HTMLKit/HTMLNodeFilter.h b/HTMLKit/HTMLNodeFilter.h index 2bf2fc2..a32a2ea 100644 --- a/HTMLKit/HTMLNodeFilter.h +++ b/HTMLKit/HTMLNodeFilter.h @@ -2,16 +2,40 @@ // HTMLNodeFilter.h // HTMLKit // -// Created by Iska on 13/05/15. +// Created by Iska on 27/05/15. // Copyright (c) 2015 BrainCookie. All rights reserved. // #import +typedef NS_ENUM(unsigned short, HTMLNodeFilterValue) +{ + HTMLNodeFilterAccept = 1, + HTMLNodeFilterReject = 2, + HTMLNodeFilterSkip = 3 +}; + +typedef NS_OPTIONS(unsigned long, HTMLNodeFilterShowOptions) +{ + HTMLNodeFilterShowAll = 0xFFFFFFFF, + HTMLNodeFilterShowElement = 0x1, + HTMLNodeFilterShowText = 0x4, + HTMLNodeFilterShowComment = 0x80, + HTMLNodeFilterShowDocument = 0x100, + HTMLNodeFilterShowDocumentType = 0x200, + HTMLNodeFilterShowDocumentFragment = 0x400 +}; + @class HTMLNode; @protocol HTMLNodeFilter -- (BOOL)acceptNode:(HTMLNode *)node; +- (HTMLNodeFilterValue)acceptNode:(HTMLNode *)node; + +@end + +@interface HTMLNodeFilterBlock : NSObject + ++ (instancetype)filterWithBlock:(HTMLNodeFilterValue (^)(HTMLNode *node))block; @end diff --git a/HTMLKit/HTMLNodeFilter.m b/HTMLKit/HTMLNodeFilter.m new file mode 100644 index 0000000..96e4843 --- /dev/null +++ b/HTMLKit/HTMLNodeFilter.m @@ -0,0 +1,42 @@ +// +// HTMLNodeFilter.m +// HTMLKit +// +// Created by Iska on 05/06/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLNodeFilter.h" + +@interface HTMLNodeFilterBlock () +{ + BOOL (^ _block)(HTMLNode *); +} +@end + +@implementation HTMLNodeFilterBlock + ++ (instancetype)filterWithBlock:(HTMLNodeFilterValue (^)(HTMLNode *))block +{ + return [[self alloc] initWithBlock:block]; +} + +- (instancetype)initWithBlock:(HTMLNodeFilterValue (^)(HTMLNode *))block +{ + self = [super init]; + if (self) { + _block = [block copy]; + } + return self; +} + +- (HTMLNodeFilterValue)acceptNode:(HTMLNode *)node +{ + if (!_block) { + return HTMLNodeFilterSkip; + } + + return _block(node); +} + +@end diff --git a/HTMLKit/HTMLNodeIterator.h b/HTMLKit/HTMLNodeIterator.h new file mode 100644 index 0000000..6dd269e --- /dev/null +++ b/HTMLKit/HTMLNodeIterator.h @@ -0,0 +1,36 @@ +// +// HTMLNodeIterator.h +// HTMLKit +// +// Created by Iska on 27/05/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import +#import "HTMLNodeFilter.h" + +@class HTMLNode; + +@interface HTMLNodeIterator : NSEnumerator + +@property (nonatomic, strong, readonly) HTMLNode *root; +@property (nonatomic, strong, readonly) HTMLNode *referenceNode; +@property (nonatomic, assign, readonly) BOOL pointerBeforeReferenceNode; +@property (nonatomic, assign, readonly) HTMLNodeFilterShowOptions whatToShow; +@property (nonatomic, strong, readonly) id filter; + ++ (instancetype)iteratorWithNode:(HTMLNode *)node + showOptions:(HTMLNodeFilterShowOptions)showOptions + filter:(HTMLNodeFilterValue (^)(HTMLNode *node))filter; + +- (instancetype)initWithNode:(HTMLNode *)node; +- (instancetype)initWithNode:(HTMLNode *)node + filter:(id)filter; +- (instancetype)initWithNode:(HTMLNode *)node + showOptions:(HTMLNodeFilterShowOptions)showOptions + filter:(id)filter; + +- (HTMLNode *)nextNode; +- (HTMLNode *)previousNode; + +@end diff --git a/HTMLKit/HTMLNodeIterator.m b/HTMLKit/HTMLNodeIterator.m new file mode 100644 index 0000000..6bfb9f4 --- /dev/null +++ b/HTMLKit/HTMLNodeIterator.m @@ -0,0 +1,166 @@ +// +// HTMLNodeIterator.m +// HTMLKit +// +// Created by Iska on 27/05/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLNodeIterator.h" +#import "HTMLDocument.h" +#import "HTMLNode.h" +#import "HTMLNodeFilter.h" +#import "HTMLNodeTraversal.h" + +typedef NS_ENUM(short, TraverseDirection) +{ + TraverseDirectionNext, + TraverseDirectionPrevious +}; + +@interface HTMLDocument (Private) +- (void)attachNodeIterator:(HTMLNodeIterator *)iterator; +- (void)detachNodeIterator:(HTMLNodeIterator *)iterator; +@end + +@interface HTMLNodeIterator () +{ + HTMLNode *_root; +} +@end + +@implementation HTMLNodeIterator + +#pragma mark - Lifecycle + ++ (instancetype)iteratorWithNode:(HTMLNode *)node + showOptions:(HTMLNodeFilterShowOptions)showOptions + filter:(HTMLNodeFilterValue (^)(HTMLNode *))filter +{ + return [[self alloc] initWithNode:node + showOptions:showOptions + filter:[HTMLNodeFilterBlock filterWithBlock:filter]]; +} + +- (instancetype)initWithNode:(HTMLNode *)node +{ + return [self initWithNode:node filter:nil]; +} + +- (instancetype)initWithNode:(HTMLNode *)node + filter:(id)filter +{ + return [self initWithNode:node showOptions:HTMLNodeFilterShowAll filter:filter]; +} + +- (instancetype)initWithNode:(HTMLNode *)node + showOptions:(HTMLNodeFilterShowOptions)showOptions + filter:(id)filter +{ + self = [super init]; + if (self) { + _root = node; + _filter = filter; + _whatToShow = showOptions; + _referenceNode = _root; + _pointerBeforeReferenceNode = YES; + [_root.ownerDocument attachNodeIterator:self]; + } + return self; +} + +- (void)dealloc +{ + [_root.ownerDocument detachNodeIterator:self]; +} + +#pragma mark - Removing Steps + +- (void)runRemovingStepsForNode:(HTMLNode *)oldNode + withOldParent:(HTMLNode *)oldParent + andOldPreviousSibling:(HTMLNode *)oldPreviousSibling +{ + if ([oldNode containsNode:_root]) { + return; + } + + if (![oldNode containsNode:_referenceNode]) { + return; + } + + if (_pointerBeforeReferenceNode) { + HTMLNode *nextSibling = oldPreviousSibling != nil ? oldPreviousSibling.nextSibling : oldParent.firstChild; + if (nextSibling != nil) { + _referenceNode = nextSibling; + return; + } + + HTMLNode *next = FollowingNode(oldParent, _root); + if ([_root containsNode:next]) { + _referenceNode = next; + return; + } + + _pointerBeforeReferenceNode = NO; + } + + HTMLNode * (^ lastInclusiveDescendant) (HTMLNode *) = ^ HTMLNode * (HTMLNode *node) { + while (node.lastChild) { + node = node.lastChild; + } + return node; + }; + + _referenceNode = oldPreviousSibling != nil ? lastInclusiveDescendant(oldPreviousSibling) : oldParent; +} + +#pragma mark - Traversal + +- (HTMLNode *)traverseInDirection:(TraverseDirection)direction +{ + HTMLNode *node = self.referenceNode; + BOOL beforeNode = self.pointerBeforeReferenceNode; + + do { + if (direction == TraverseDirectionNext) { + if (!beforeNode) { + node = FollowingNode(node, self.root); + if (node == nil) { + return nil; + } + } + beforeNode = NO; + } else { + if (beforeNode) { + node = PrecedingNode(node, self.root); + if (node == nil) { + return nil; + } + } + beforeNode = YES; + } + } while (FilterNode(self.filter, self.whatToShow, node) != HTMLNodeFilterAccept); + + _referenceNode = node; + _pointerBeforeReferenceNode = beforeNode; + return node; +} + +- (HTMLNode *)nextNode +{ + return [self traverseInDirection:TraverseDirectionNext]; +} + +- (HTMLNode *)previousNode +{ + return [self traverseInDirection:TraverseDirectionPrevious]; +} + +#pragma mark - NSEnumerator + +- (id)nextObject +{ + return self.nextNode; +} + +@end diff --git a/HTMLKit/HTMLNodeTraversal.h b/HTMLKit/HTMLNodeTraversal.h new file mode 100644 index 0000000..ee2bf7e --- /dev/null +++ b/HTMLKit/HTMLNodeTraversal.h @@ -0,0 +1,17 @@ +// +// HTMLNodeTraversal.h +// HTMLKit +// +// Created by Iska on 05/06/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import +#import "HTMLNodeFilter.h" + +@class HTMLNode; + +extern HTMLNode * PrecedingNode(HTMLNode *node, HTMLNode *root); +extern HTMLNode * FollowingNode(HTMLNode *node, HTMLNode *root); +extern HTMLNode * FollowingNodeSkippingChildren(HTMLNode *node, HTMLNode *root); +extern HTMLNodeFilterValue FilterNode(id filter, HTMLNodeFilterShowOptions whatToShow, HTMLNode *node); diff --git a/HTMLKit/HTMLNodeTraversal.m b/HTMLKit/HTMLNodeTraversal.m new file mode 100644 index 0000000..dd17b2a --- /dev/null +++ b/HTMLKit/HTMLNodeTraversal.m @@ -0,0 +1,76 @@ +// +// HTMLNodeTraversal.m +// HTMLKit +// +// Created by Iska on 05/06/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLNodeTraversal.h" +#import "HTMLNode.h" +#import "HTMLNodeFilter.h" + +HTMLNode * PrecedingNode(HTMLNode *node, HTMLNode *root) +{ + HTMLNode *previous = node.previousSibling; + if (previous != nil) { + while (previous.lastChild != nil) { + previous = previous.lastChild; + } + return previous; + } + + if (node == root) { + return nil; + } + + return node.parentNode; +} + +HTMLNode * FollowingNode(HTMLNode *node, HTMLNode *root) +{ + if (node.firstChild != nil) { + return node.firstChild; + } + + do { + if (node == root) { + return nil; + } + if (node.nextSibling != nil) { + return node.nextSibling; + } + node = node.parentNode; + } while (node != nil); + + return nil; +} + +HTMLNode * FollowingNodeSkippingChildren(HTMLNode *node, HTMLNode *root) +{ + do { + if (node == root) { + return nil; + } + if (node.nextSibling != nil) { + return node.nextSibling; + } + node = node.parentNode; + } while (node != nil); + + return nil; +} + +HTMLNodeFilterValue FilterNode(id filter, HTMLNodeFilterShowOptions whatToShow, HTMLNode *node) +{ + unsigned long nthBit = (1 << (node.nodeType - 1)) & whatToShow; + if (!nthBit) { + return HTMLNodeFilterSkip; + } + + if (filter == nil) { + return HTMLNodeFilterAccept; + } + + return [filter acceptNode:node]; +} diff --git a/HTMLKit/HTMLNodeTreeEnumerator.h b/HTMLKit/HTMLNodeTreeEnumerator.h deleted file mode 100644 index baf7243..0000000 --- a/HTMLKit/HTMLNodeTreeEnumerator.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// 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 deleted file mode 100644 index 8f82972..0000000 --- a/HTMLKit/HTMLNodeTreeEnumerator.m +++ /dev/null @@ -1,54 +0,0 @@ -// -// 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/HTMLParser.m b/HTMLKit/HTMLParser.m index f5f0ed5..6f7bb46 100644 --- a/HTMLKit/HTMLParser.m +++ b/HTMLKit/HTMLParser.m @@ -12,7 +12,7 @@ #import "HTMLStackOfOpenElements.h" #import "HTMLListOfActiveFormattingElements.h" #import "HTMLParserInsertionModes.h" -#import "HTMLNodes.h" +#import "HTMLDOM.h" #import "HTMLElementTypes.h" #import "HTMLElementAdjustment.h" #import "HTMLMarker.h" @@ -421,8 +421,8 @@ - (HTMLElement *)createElementForToken:(HTMLTagToken *)token inNamespace:(HTMLNamespace)htmlNamespace { HTMLElement *element = [[HTMLElement alloc] initWithTagName:token.tagName - attributes:token.attributes - namespace:htmlNamespace]; + namespace:htmlNamespace + attributes:token.attributes]; return element; } @@ -452,11 +452,11 @@ HTMLElement *child = nil; HTMLNode *adjustedInsertionLocation = [self appropriatePlaceForInsertingANodeWithOverrideTarget:nil beforeChildNode:&child]; - if (adjustedInsertionLocation.type != HTMLNodeDocument) { - if (child != nil && child.previousSibling.type == HTMLNodeText) { + if (adjustedInsertionLocation.nodeType != HTMLNodeDocument) { + if (child != nil && child.previousSibling.nodeType == HTMLNodeText) { [(HTMLText *)child.previousSibling appendString:data]; - } else if (adjustedInsertionLocation.lastChildNode.type == HTMLNodeText) { - [(HTMLText *)adjustedInsertionLocation.lastChildNode appendString:data]; + } else if (adjustedInsertionLocation.lastChild.nodeType == HTMLNodeText) { + [(HTMLText *)adjustedInsertionLocation.lastChild appendString:data]; } else { HTMLText *text = [[HTMLText alloc] initWithData:data]; [adjustedInsertionLocation insertNode:text beforeChildNode:child]; diff --git a/HTMLKit/HTMLTreeWalker.h b/HTMLKit/HTMLTreeWalker.h new file mode 100644 index 0000000..fff2328 --- /dev/null +++ b/HTMLKit/HTMLTreeWalker.h @@ -0,0 +1,36 @@ +// +// HTMLTreeWalker.h +// HTMLKit +// +// Created by Iska on 05/06/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import +#import "HTMLNodeFilter.h" + +@class HTMLNode; + +@interface HTMLTreeWalker : NSObject + +@property (nonatomic, strong, readonly) HTMLNode *root; +@property (nonatomic, assign, readonly) HTMLNodeFilterShowOptions whatToShow; +@property (nonatomic, strong, readonly) id filter; +@property (nonatomic, strong) HTMLNode *currentNode; + +- (instancetype)initWithNode:(HTMLNode *)node; +- (instancetype)initWithNode:(HTMLNode *)node + filter:(id)filter; +- (instancetype)initWithNode:(HTMLNode *)node + showOptions:(HTMLNodeFilterShowOptions)showOptions + filter:(id)filter; + +- (HTMLNode *)parentNode; +- (HTMLNode *)firstChild; +- (HTMLNode *)lastChild; +- (HTMLNode *)previousSibling; +- (HTMLNode *)nextSibling; +- (HTMLNode *)previousNode; +- (HTMLNode *)nextNode; + +@end diff --git a/HTMLKit/HTMLTreeWalker.m b/HTMLKit/HTMLTreeWalker.m new file mode 100644 index 0000000..0e21ed0 --- /dev/null +++ b/HTMLKit/HTMLTreeWalker.m @@ -0,0 +1,236 @@ +// +// HTMLTreeWalker.m +// HTMLKit +// +// Created by Iska on 05/06/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLTreeWalker.h" +#import "HTMLNode.h" +#import "HTMLNodeFilter.h" +#import "HTMLNodeTraversal.h" + +typedef NS_ENUM(short, HTMLTreeWalkerChildrenType) +{ + HTMLTreeWalkerChildrenTypeFirst, + HTMLTreeWalkerChildrenTypeLast +}; + +typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType) +{ + HTMLTreeWalkerSiblingsTypeNext, + HTMLTreeWalkerSiblingsTypePrevious +}; + +@implementation HTMLTreeWalker + +#pragma mark - Lifecycle + +- (instancetype)initWithNode:(HTMLNode *)node +{ + return [self initWithNode:node filter:nil]; +} + +- (instancetype)initWithNode:(HTMLNode *)node + filter:(id)filter +{ + return [self initWithNode:node showOptions:HTMLNodeFilterShowAll filter:filter]; +} + +- (instancetype)initWithNode:(HTMLNode *)node + showOptions:(HTMLNodeFilterShowOptions)showOptions + filter:(id)filter +{ + self = [super init]; + if (self) { + _root = node; + _filter = filter; + _whatToShow = showOptions; + _currentNode = _root; + } + return self; +} + +#pragma mark - Traversal + +- (HTMLNode *)parentNode +{ + HTMLNode *node = _currentNode; + + while (node != nil && node != _root) { + node = node.parentNode; + if (node != nil && FilterNode(self.filter, self.whatToShow, node) == HTMLNodeFilterAccept) { + _currentNode = node; + return node; + } + } + return nil; +} + +- (HTMLNode *)traverseChildrenOfType:(HTMLTreeWalkerChildrenType)type +{ + HTMLNode *node = _currentNode; + + node = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.firstChild : node.lastChild; + + while (node != nil) { + HTMLNodeFilterValue result = FilterNode(self.filter, self.whatToShow, node); + if (result == HTMLNodeFilterAccept) { + _currentNode = node; + return node; + } + + if (result == HTMLNodeFilterSkip) { + HTMLNode *child = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.firstChild : node.lastChild; + if (child != nil) { + node = child; + continue; + } + } + + while (node != nil) { + HTMLNode *sibling = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.nextSibling : node.previousSibling; + if (sibling != nil) { + node = sibling; + break; + } + + HTMLNode *parent = node.parentNode; + if (parent == nil || parent == _root || parent == _currentNode) { + return nil; + } + node = parent; + } + } + + return nil; +} + +- (HTMLNode *)firstChild +{ + return [self traverseChildrenOfType:HTMLTreeWalkerChildrenTypeFirst]; +} + +- (HTMLNode *)lastChild +{ + return [self traverseChildrenOfType:HTMLTreeWalkerChildrenTypeLast]; +} + +- (HTMLNode *)traverseSiblingsOfType:(HTMLTreeWalkerSiblingsType)type +{ + HTMLNode *node = _currentNode; + + if (node == _root) { + return nil; + } + + while (YES) { + HTMLNode *sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.nextSibling : node.previousSibling; + while (sibling != nil) { + node = sibling; + HTMLNodeFilterValue result = FilterNode(self.filter, self.whatToShow, node); + if (result == HTMLNodeFilterAccept) { + _currentNode = node; + return node; + } + + sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.firstChild : node.lastChild; + if (result == HTMLNodeFilterReject || sibling == nil) { + sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.nextSibling : node.previousSibling; + } + } + + node = node.parentNode; + if (node == nil || node == _root) { + return nil; + } + + if (FilterNode(self.filter, self.whatToShow, node) == HTMLNodeFilterAccept) { + return nil; + } + } + + return nil; +} + +- (HTMLNode *)previousSibling +{ + return [self traverseSiblingsOfType:HTMLTreeWalkerSiblingsTypePrevious]; +} + +- (HTMLNode *)nextSibling +{ + return [self traverseSiblingsOfType:HTMLTreeWalkerSiblingsTypeNext]; +} + +- (HTMLNode *)previousNode +{ + HTMLNode *node = _currentNode; + + while (node != _root) { + HTMLNode *sibling = node.previousSibling; + + while (sibling != nil) { + node = sibling; + + HTMLNodeFilterValue result = FilterNode(self.filter, self.whatToShow, node); + while (result != HTMLNodeFilterReject && node.hasChildNodes) { + node = node.lastChild; + result = FilterNode(self.filter, self.whatToShow, node); + } + + if (result == HTMLNodeFilterAccept) { + _currentNode = node; + return node; + } + + sibling = node.previousSibling; + } + + if (node == _root || node.parentNode == nil) { + return nil; + } + + node = node.parentNode; + if (FilterNode(self.filter, self.whatToShow, node) == HTMLNodeFilterAccept) { + _currentNode = node; + return node; + } + } + + return nil; +} + +- (HTMLNode *)nextNode +{ + HTMLNode *node = _currentNode; + + HTMLNodeFilterValue result = YES; + + while (YES) { + while (result != HTMLNodeFilterReject && node.hasChildNodes) { + node = node.firstChild; + result = FilterNode(self.filter, self.whatToShow, node); + if (result == HTMLNodeFilterAccept) { + _currentNode = node; + return node; + } + } + + HTMLNode *nextSibling; + while ((nextSibling = FollowingNodeSkippingChildren(node, _root)) != nil) { + node = nextSibling; + result = FilterNode(self.filter, self.whatToShow, node); + if (result == HTMLNodeFilterAccept) { + _currentNode = node; + return node; + } + break; + } + } + + return nil; +} + +@end diff --git a/HTMLKitTests/HTML5LibTreeConstructionTest.m b/HTMLKitTests/HTML5LibTreeConstructionTest.m index 1e8ac4d..9d0a45c 100644 --- a/HTMLKitTests/HTML5LibTreeConstructionTest.m +++ b/HTMLKitTests/HTML5LibTreeConstructionTest.m @@ -13,7 +13,6 @@ #import "HTMLElement.h" #import "HTMLText.h" #import "HTMLComment.h" -#import "HTMLNodeTreeEnumerator.h" static NSString * const HTML5LibTests = @"html5lib-tests"; static NSString * const TreeConstruction = @"tree-construction"; @@ -111,7 +110,7 @@ static NSString * const TreeConstruction = @"tree-construction"; fragment = [fragment substringFromIndex:@"svg ".length]; namespace = HTMLNamespaceSVG; } - test.documentFragment = [[HTMLElement alloc] initWithTagName:fragment attributes:nil namespace:namespace]; + test.documentFragment = [[HTMLElement alloc] initWithTagName:fragment namespace:namespace attributes:nil]; } else if ([match hasPrefix:@"#document\n"]) { NSArray *parts = [[match substringFromIndex:@"#document\n".length] componentsSeparatedByString:@"| "]; NSArray *nodes = [HTML5LibTreeConstructionTest parseDocument:parts]; @@ -245,7 +244,7 @@ NS_INLINE HTMLElement * parseTag(NSString *str) NSString *tagName = parts.count == 2 ? parts[1] : parts[0]; HTMLNamespace namespace = parts.count == 1 ? HTMLNamespaceHTML : ([parts[0] isEqualToString:@"math"] ? HTMLNamespaceMathML : HTMLNamespaceSVG); - HTMLElement *element = [[HTMLElement alloc] initWithTagName:tagName attributes:nil namespace:namespace]; + HTMLElement *element = [[HTMLElement alloc] initWithTagName:tagName namespace:namespace attributes:nil]; return element; } diff --git a/HTMLKitTests/HTMLKitMutationAlgorithmsTests.m b/HTMLKitTests/HTMLKitMutationAlgorithmsTests.m index 990d574..421065b 100644 --- a/HTMLKitTests/HTMLKitMutationAlgorithmsTests.m +++ b/HTMLKitTests/HTMLKitMutationAlgorithmsTests.m @@ -7,7 +7,7 @@ // #import -#import "HTMLNodes.h" +#import "HTMLDOM.h" #import "NSString+HTMLKit.h" extern uint64_t dispatch_benchmark(size_t count, void (^block)(void)); diff --git a/HTMLKitTests/HTMLKitNodeIteratorTests.m b/HTMLKitTests/HTMLKitNodeIteratorTests.m new file mode 100644 index 0000000..7ff6823 --- /dev/null +++ b/HTMLKitTests/HTMLKitNodeIteratorTests.m @@ -0,0 +1,568 @@ +// +// HTMLKitNodeTreeEnumratorTests.m +// HTMLKit +// +// Created by Iska on 28/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import +#import "HTMLDOM.h" + +@interface HTMLKitNodeIteratorTests : XCTestCase + +@end + +@implementation HTMLKitNodeIteratorTests + +#pragma mark - Test DOM + +- (HTMLElement *)div +{ + return [[HTMLElement alloc] initWithTagName:@"div"]; +} + +- (HTMLElement *)simpleTree +{ + /* + | div + | a + | b + | c + */ + HTMLElement *div = self.div; + [div appendNode:[[HTMLElement alloc] initWithTagName:@"a"]]; + [div appendNode:[[HTMLElement alloc] initWithTagName:@"b"]]; + [div appendNode:[[HTMLElement alloc] initWithTagName:@"c"]]; + return div; +} + +- (HTMLElement *)nestedSimpleTree +{ + /* + | div + | div + | a + | b + | c + | div + | a + | b + | c + */ + HTMLElement *div = self.div; + [div appendNode:self.simpleTree]; + [div appendNode:self.simpleTree]; + return div; +} + +- (HTMLElement *)complexTree +{ + /* + | div + | div + | div + | a + | b + | c + | e + | f + | div + | a + | b + | c + */ + HTMLElement *root = self.div; + + HTMLElement *div = self.div; + [div appendNode:self.simpleTree]; + [root appendNode:div]; + + HTMLElement *e = [[HTMLElement alloc] initWithTagName:@"e"]; + [e appendNode:[[HTMLElement alloc] initWithTagName:@"f"]]; + [root appendNode:e]; + [root appendNode:self.simpleTree]; + + return root; +} + +- (HTMLDocument *)mixedTree +{ + + /* + | doctype + | #comment + | #comment + | html + | #text "This is a Text" + | div + */ + HTMLDocument *document = [HTMLDocument new]; + document.documentType = [HTMLDocumentType new];; + + HTMLComment *comment = [[HTMLComment alloc] initWithData:@"This is a Comment"]; + [document appendNode:comment]; + + HTMLComment *secondCommnet = [[HTMLComment alloc] initWithData:@"This is a second Comment"]; + [document appendNode:secondCommnet]; + + HTMLElement *root = [[HTMLElement alloc] initWithTagName:@"html"]; + [document appendNode:root]; + + [root appendNode:[[HTMLText alloc] initWithData:@"This is a Text"]]; + + [root appendNode:[[HTMLElement alloc] initWithTagName:@"div"]]; + + return document; +} + +- (HTMLDocument *)document +{ + NSString *htmlString = + @"" + @"" + @"" + @"Title" + @"" + @"" + @"HelloWorld!HTML Kit" + @"

This is an Important paragraph

" + @"" + @""; + + HTMLDocument *document = [HTMLDocument documentWithString:htmlString]; + return document; +} + +#pragma mark - Test Iterator + +- (void)testNodeIteratorInit +{ + HTMLElement *tree = self.simpleTree; + HTMLNodeIterator *iterator = tree.nodeIterator; + + XCTAssertEqualObjects(iterator.root, tree); + XCTAssertEqualObjects(iterator.referenceNode, tree); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); + XCTAssertEqualObjects(iterator.filter, nil); + XCTAssertEqual(iterator.whatToShow, HTMLNodeFilterShowAll); +} + +- (void)testNewIteratorNextNodeShouldBeRoot +{ + HTMLElement *tree = self.simpleTree; + HTMLNodeIterator *iterator = tree.nodeIterator; + + XCTAssertEqualObjects(iterator.nextNode, tree); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); +} + +- (void)testNewIteratorPreviousNodeShouldBeNil +{ + HTMLElement *tree = self.simpleTree; + HTMLNodeIterator *iterator = tree.nodeIterator; + + XCTAssertEqualObjects(iterator.previousNode, nil); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); +} + +- (void)testNewIteratorPreviousNodeShouldBeNextNode +{ + HTMLElement *tree = self.simpleTree; + HTMLNodeIterator *iterator = tree.nodeIterator; + + HTMLNode *node = iterator.nextNode; + + XCTAssertEqualObjects(iterator.previousNode, node); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); +} + +- (void)testNextPreviousIteration +{ + HTMLElement *tree = self.simpleTree; + HTMLNodeIterator *iterator = tree.nodeIterator; + + XCTAssertEqualObjects(iterator.previousNode, nil); + XCTAssertEqualObjects(iterator.nextNode.name, @"div"); + XCTAssertEqualObjects(iterator.nextNode.name, @"a"); + XCTAssertEqualObjects(iterator.previousNode.name, @"a"); + XCTAssertEqualObjects(iterator.previousNode.name, @"div"); + XCTAssertEqualObjects(iterator.nextNode.name, @"div"); + XCTAssertEqualObjects(iterator.nextNode.name, @"a"); + XCTAssertEqualObjects(iterator.nextNode.name, @"b"); + XCTAssertEqualObjects(iterator.nextNode.name, @"c"); + XCTAssertEqualObjects(iterator.previousNode.name, @"c"); + XCTAssertEqualObjects(iterator.previousNode.name, @"b"); + XCTAssertEqualObjects(iterator.previousNode.name, @"a"); + XCTAssertEqualObjects(iterator.previousNode.name, @"div"); +} + +- (void)testSingleNodeIteration +{ + HTMLElement *div = self.div; + NSArray *result = div.nodeIterator.allObjects; + NSArray *expected = @[@"div"]; + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testSimpleTreeIteration +{ + HTMLElement *tree = self.simpleTree; + NSArray *result = tree.nodeIterator.allObjects; + NSArray *expected = @[@"div", @"a", @"b", @"c"]; + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testNestedSimpleTreeIteration +{ + HTMLElement *tree = self.nestedSimpleTree; + NSArray *result = tree.nodeIterator.allObjects; + NSArray *expected = @[@"div", @"div", @"a", @"b", @"c", @"div", @"a", @"b", @"c"]; + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testComplexTreeIteration +{ + HTMLElement *tree = self.complexTree; + NSArray *result = tree.nodeIterator.allObjects; + NSArray *expected = @[@"div", @"div",@"div", @"a", @"b", @"c", @"e", @"f", @"div", @"a", @"b", @"c"]; + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +#pragma mark - Test Iterator ShowOptions (WhatToShow) + +- (void)testShowDocument +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowDocument filter:nil]; + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"#document"]; + XCTAssertEqual(result.count, 1); + XCTAssertEqual([result.firstObject class], [HTMLDocument class]); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testShowDocumentType +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowDocumentType filter:nil]; + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"html"]; + XCTAssertEqual(result.count, 1); + XCTAssertEqual([result.firstObject class], [HTMLDocumentType class]); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testShowComment +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowComment filter:nil]; + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"#comment", @"#comment"]; + XCTAssertEqual(result.count, 2); + XCTAssertEqual([result[0] class], [HTMLComment class]); + XCTAssertEqual([result[1] class], [HTMLComment class]); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testShowText +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowText filter:nil]; + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"#text"]; + XCTAssertEqual(result.count, 1); + XCTAssertEqual([result.firstObject class], [HTMLText class]); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testShowElement +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]; + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"html", @"div"]; + XCTAssertEqual(result.count, 2); + XCTAssertEqual([result.firstObject class], [HTMLElement class]); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testShowBitmask +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowElement | HTMLNodeFilterShowText + filter:nil]; + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"html", @"#text", @"div"]; + XCTAssertEqual(result.count, 3); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +#pragma mark - Test Iterator Filter + +- (void)testNodeFilter +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowAll + filterBlock:^HTMLNodeFilterValue(HTMLNode *node) { + if (node.nodeType == HTMLNodeComment) { + if ([[(HTMLComment *)node data] rangeOfString:@"second"].location != NSNotFound) { + return HTMLNodeFilterAccept; + } + } + return HTMLNodeFilterSkip; + }]; + + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"#comment"]; + XCTAssertEqual(result.count, 1); + XCTAssertEqualObjects([result[0] data], @"This is a second Comment"); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +#pragma mark - Test Removing Steps + +/* + Test cases for the Removing Steps + https://dom.spec.whatwg.org/#interface-nodeiterator + + Following DOM is used: + +| +| +| +| "Title" +| <body> +| <span> +| "Hello" +| <strong> +| "World!" +| <strong> +| "HTML " +| <!-- This is a Comment! --> +| " Kit" +| <p> +| "This is an " +| <em> +| "Important" +| " paragraph" +*/ + +static void (^ RemoveThenInsertNode)(HTMLNode *) = ^ (HTMLNode *node) { + HTMLNode *parent = node.parentNode; + HTMLNode *nextSibling = node.nextSibling; + [parent removeChildNode:node]; + [parent insertNode:node beforeChildNode:nextSibling]; +}; + +static void (^ IterateUpToNode)(HTMLNodeIterator *, HTMLNode *) = ^ (HTMLNodeIterator *iterator, HTMLNode *target) { + for(HTMLNode *node = iterator.referenceNode; node && (node != target); node = iterator.nextNode); +}; + +static HTMLNode * (^ LastDescendant)(HTMLNode *) = ^ HTMLNode * (HTMLNode *node) { + while (node.lastChild) { + node = node.lastChild; + } + return node; +}; + +- (void)testThatRemovingRootNodeShouldNotAffectIterator +{ + HTMLDocument *document = self.document; + HTMLNode *node = document.body.firstChild; // <span> + + HTMLNodeIterator *iterator = node.nodeIterator; + + [document.body removeChildNode:node]; + + XCTAssertEqualObjects(iterator.root, node); + XCTAssertEqualObjects(iterator.referenceNode, node); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); + XCTAssertEqualObjects(iterator.referenceNode.parentNode, nil); +} + +- (void)testThatRemovingANonInclusiveAnscestorOfReferenceShouldNotAffectIterator +{ + HTMLDocument *document = self.document; + HTMLNode *body = document.body; + + HTMLNodeIterator *iterator = body.nodeIterator; + + [iterator nextNode]; // Reference node: <body> + [iterator nextNode]; // Reference node: <span> + + RemoveThenInsertNode(iterator.root.childNodes[1]); // Remove <p> + + XCTAssertEqualObjects(iterator.root, body); + XCTAssertEqualObjects(iterator.referenceNode, body.firstChild); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); +} + +- (void)testThatRemovingReferenceNodeShouldUpdateIterator_NilOldPreviousSibling +{ + HTMLDocument *document = self.document; + + HTMLNodeIterator *iterator = document.body.nodeIterator; + + [iterator nextNode]; // Reference node: <body> + + HTMLNode *node = iterator.nextNode; // Reference node: <span> + RemoveThenInsertNode(node); // Remove <span> with old previos sibling being nil + + XCTAssertEqualObjects(iterator.referenceNode, iterator.root); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); + + HTMLNode *next = iterator.nextNode; // "Hello" + XCTAssertEqualObjects(next, iterator.root.firstChild); +} + +- (void)testThatRemovingReferenceNodeShouldUpdateIterator_NonNilOldPreviousSibling_NotBeforeReference +{ + HTMLDocument *document = self.document; + HTMLNode *body = document.body; + + HTMLNodeIterator *iterator = body.nodeIterator; + + HTMLNode *node = iterator.root.childNodes[1]; // <p> + IterateUpToNode(iterator, node); // Reference node: <p>, pointer-before-reference: NO + RemoveThenInsertNode(node); // Remove <p> with old previos sibling being <span> + + XCTAssertEqualObjects(iterator.referenceNode, LastDescendant(body.firstChild)); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); +} + +- (void)testThatRemovingReferenceNodeShouldUpdateIterator_NonNilOldPreviousSibling_BeforeReference +{ + HTMLDocument *document = self.document; + HTMLNode *body = document.body; + + HTMLNodeIterator *iterator = body.nodeIterator; + + HTMLNode *node = iterator.root.childNodes[1]; // <p> + IterateUpToNode(iterator, node); // Reference node: <p>, pointer-before-reference: NO + [iterator previousNode]; // pointer-before-reference: YES + RemoveThenInsertNode(node); // Remove <p> with old previos sibling being <span> + + XCTAssertEqualObjects(iterator.referenceNode, body.firstChild); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); +} + +- (void)testThatRemovingThenReinsertingReferenceNodeAfterNextShouldReturnItAgain +{ + HTMLDocument *document = self.document; + HTMLNodeIterator *iterator = document.body.nodeIterator; + + [iterator nextNode]; // Reference node: <body> + + HTMLNode *node = iterator.nextNode; // <span> + RemoveThenInsertNode(node); + + XCTAssertEqualObjects(iterator.referenceNode, iterator.root); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); + + HTMLNode *next = iterator.nextNode; + XCTAssertEqualObjects(next, node); +} + +- (void)testThatRemovingThenReinsertingReferenceNodeAfterPreviousShouldReturnItAgain +{ + HTMLDocument *document = self.document; + HTMLNodeIterator *iterator = document.body.nodeIterator; + + [iterator nextNode]; // Reference node: <body> + [iterator nextNode]; // Reference node: <span> + + HTMLNode *node = iterator.previousNode; // Reference node: <span>, pointer-before-reference: YES + HTMLNode *next = node.nextSibling; // <p> + RemoveThenInsertNode(node); + + XCTAssertEqualObjects(iterator.referenceNode, next); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); + + HTMLNode *previous = iterator.previousNode; + XCTAssertEqualObjects(previous, LastDescendant(node)); +} + +- (void)testThatRemovingParentOfReferenceNodeShouldUpdateIterator_NotBeforeReference +{ + HTMLDocument *document = self.document; + HTMLNode *body = document.body; + + HTMLNodeIterator *iterator = body.nodeIterator; + HTMLNode *parent = body.childNodes[1]; + + IterateUpToNode(iterator, parent); // Reference node: <p>, pointer-before-reference: NO + [iterator nextNode]; // Reference node: "This is an " + RemoveThenInsertNode(parent); + + XCTAssertEqualObjects(iterator.referenceNode, LastDescendant(body.firstChild)); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); +} + +- (void)testThatRemovingParentOfReferenceNodeShouldUpdateIterator_BeforeReference +{ + HTMLDocument *document = self.document; + HTMLNode *body = document.body; + + HTMLNodeIterator *iterator = body.nodeIterator; + HTMLNode *parent = body.childNodes[1]; + + IterateUpToNode(iterator, parent); // Reference node: <p>, pointer-before-reference: NO + [iterator nextNode]; // Reference node: "This is an " + [iterator previousNode]; // pointer-before-reference: YES + RemoveThenInsertNode(parent); + + XCTAssertEqualObjects(iterator.referenceNode, body.firstChild); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); +} + +- (void)testRemoveReferenceNode_NilPreviousSibling_NonNilParentFirstChild +{ + HTMLDocument *document = self.document; + HTMLNode *body = document.body; + + HTMLNodeIterator *iterator = body.nodeIterator; + + [iterator nextNode]; // Reference node: <body> + [iterator nextNode]; // Reference node: <span> + + HTMLNode *node = iterator.previousNode; // Reference node: <span>, pointer-before-reference: YES + XCTAssertNil(node.previousSibling); + XCTAssertNotNil(node.nextSibling); + + HTMLNode *nextSibling = node.nextSibling; // <p> + RemoveThenInsertNode(node); + + XCTAssertEqualObjects(iterator.referenceNode, nextSibling); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); + + HTMLNode *next = iterator.nextNode; // <p> + XCTAssertNotEqualObjects(next, node); + XCTAssertEqualObjects(next, nextSibling); +} + +- (void)testRemoveReferenceNode_NodeAfterOldParentIsOutsideRoot_BeforeReference +{ + HTMLDocument *document = self.document; + HTMLNode *body = document.body; + + body.innerHTML = @"<div><p><a></a></p></div><div></div>"; + + HTMLNodeIterator *iterator = body.firstChild.nodeIterator; + + IterateUpToNode(iterator, LastDescendant(body.firstChild)); // Referecne node: <a> + HTMLNode *node = [iterator previousNode]; // pointer-before-reference: YES + RemoveThenInsertNode(node); + + XCTAssertEqualObjects(iterator.referenceNode, iterator.root.firstChild); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); +} + +@end diff --git a/HTMLKitTests/HTMLKitNodeTreeEnumratorTests.m b/HTMLKitTests/HTMLKitNodeTreeEnumratorTests.m deleted file mode 100644 index 654fbcf..0000000 --- a/HTMLKitTests/HTMLKitNodeTreeEnumratorTests.m +++ /dev/null @@ -1,148 +0,0 @@ -// -// HTMLKitNodeTreeEnumratorTests.m -// HTMLKit -// -// Created by Iska on 28/03/15. -// Copyright (c) 2015 BrainCookie. All rights reserved. -// - -#import <XCTest/XCTest.h> - -#import "HTMLNodes.h" - -@interface HTMLKitNodeTreeEnumratorTests : XCTestCase - -@end - -@implementation HTMLKitNodeTreeEnumratorTests - -#pragma mark - Elements - -- (HTMLElement *)div -{ - return [[HTMLElement alloc] initWithTagName:@"div"]; -} - -- (HTMLElement *)simpleTree -{ - /* - | div - | a - | b - | c - */ - HTMLElement *div = self.div; - [div appendNode:[[HTMLElement alloc] initWithTagName:@"a"]]; - [div appendNode:[[HTMLElement alloc] initWithTagName:@"b"]]; - [div appendNode:[[HTMLElement alloc] initWithTagName:@"c"]]; - return div; -} - -- (HTMLElement *)nestedSimpleTree -{ - /* - | div - | div - | a - | b - | c - | div - | a - | b - | c - */ - HTMLElement *div = self.div; - [div appendNode:self.simpleTree]; - [div appendNode:self.simpleTree]; - return div; -} - -- (HTMLElement *)complexTree -{ - /* - | div - | div - | div - | a - | b - | c - | e - | f - | div - | a - | b - | c - */ - HTMLElement *root = self.div; - - HTMLElement *div = self.div; - [div appendNode:self.simpleTree]; - [root appendNode:div]; - - HTMLElement *e = [[HTMLElement alloc] initWithTagName:@"e"]; - [e appendNode:[[HTMLElement alloc] initWithTagName:@"f"]]; - [root appendNode:e]; - [root appendNode:self.simpleTree]; - - return root; -} - -#pragma mark - Tests - -- (void)testSingle -{ - HTMLElement *div = self.div; - NSArray *result = div.treeEnumerator.allObjects; - NSArray *expected = @[@"div"]; - XCTAssertEqualObjects([result valueForKey:@"name"], expected); -} - -- (void)testSimpleTree -{ - HTMLElement *tree = self.simpleTree; - NSArray *result = tree.treeEnumerator.allObjects; - NSArray *expected = @[@"div", @"a", @"b", @"c"]; - XCTAssertEqualObjects([result valueForKey:@"name"], expected); -} - -- (void)testSimpleTreeReversed -{ - HTMLElement *tree = self.simpleTree; - NSArray *result = tree.reverseTreeEnumerator.allObjects; - NSArray *expected = @[@"div", @"c", @"b", @"a"]; - XCTAssertEqualObjects([result valueForKey:@"name"], expected); -} - -- (void)testNestedSimpleTree -{ - HTMLElement *tree = self.nestedSimpleTree; - NSArray *result = tree.treeEnumerator.allObjects; - NSArray *expected = @[@"div", @"div", @"a", @"b", @"c", @"div", @"a", @"b", @"c"]; - XCTAssertEqualObjects([result valueForKey:@"name"], expected); -} - -- (void)testNestedSimpleTreeReversed -{ - HTMLElement *tree = self.nestedSimpleTree; - NSArray *result = tree.reverseTreeEnumerator.allObjects; - NSArray *expected = @[@"div", @"div", @"c", @"b", @"a", @"div", @"c", @"b", @"a"]; - XCTAssertEqualObjects([result valueForKey:@"name"], expected); -} - -- (void)testComplexSimpleTree -{ - HTMLElement *tree = self.complexTree; - NSArray *result = tree.treeEnumerator.allObjects; - NSArray *expected = @[@"div", @"div",@"div", @"a", @"b", @"c", @"e", @"f", @"div", @"a", @"b", @"c"]; - XCTAssertEqualObjects([result valueForKey:@"name"], expected); -} - -- (void)testComplexSimpleTreeReversed -{ - HTMLElement *tree = self.complexTree; - NSArray *result = tree.reverseTreeEnumerator.allObjects; - NSArray *expected = @[@"div", @"div", @"c", @"b", @"a", @"e", @"f", @"div", @"div", @"c", @"b", @"a"]; - XCTAssertEqualObjects([result valueForKey:@"name"], expected); -} - -@end diff --git a/HTMLKitTests/HTMLKitNodesTests.m b/HTMLKitTests/HTMLKitNodesTests.m index e2b65cc..87f37f9 100644 --- a/HTMLKitTests/HTMLKitNodesTests.m +++ b/HTMLKitTests/HTMLKitNodesTests.m @@ -7,7 +7,7 @@ // #import <XCTest/XCTest.h> -#import "HTMLNodes.h" +#import "HTMLDOM.h" @interface HTMLKitNodesTests : XCTestCase @@ -30,18 +30,17 @@ HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement]; XCTAssertNotNil(node); XCTAssertEqualObjects(node.name, @"name"); - XCTAssertEqual(node.type, HTMLNodeElement); + XCTAssertEqual(node.nodeType, HTMLNodeElement); XCTAssertNotNil(node.childNodes); XCTAssertEqual(node.childNodes.count, 0); XCTAssertNil(node.ownerDocument); - XCTAssertNil(node.baseURI); XCTAssertNil(node.parentNode); XCTAssertNil(node.parentElement); - XCTAssertNil(node.firstChiledNode); - XCTAssertNil(node.lastChildNode); + XCTAssertNil(node.firstChild); + XCTAssertNil(node.lastChild); XCTAssertNil(node.previousSibling); - XCTAssertNil(node.lastChildNode); + XCTAssertNil(node.lastChild); } - (void)testAppendNode @@ -51,7 +50,7 @@ [element appendNode:comment]; XCTAssertEqual(element.childNodesCount, 1); - XCTAssertEqual(element.firstChiledNode, comment); + XCTAssertEqual(element.firstChild, comment); HTMLElement *firstElement = [HTMLElement new]; HTMLElement *secondElement = [HTMLElement new]; @@ -60,8 +59,8 @@ [element appendNodes:nodes]; XCTAssertEqual(element.childNodesCount, 3); - XCTAssertEqual(element.firstChiledNode, comment); - XCTAssertEqual(element.lastChildNode, secondElement); + XCTAssertEqual(element.firstChild, comment); + XCTAssertEqual(element.lastChild, secondElement); } - (void)testPrependNode @@ -74,7 +73,7 @@ [element prependNode:comment]; XCTAssertEqual(element.childNodesCount, 2); - XCTAssertEqual(element.firstChiledNode, comment); + XCTAssertEqual(element.firstChild, comment); HTMLElement *firstElement = [HTMLElement new]; HTMLElement *secondElement = [HTMLElement new]; @@ -83,8 +82,8 @@ [element prependNodes:nodes]; XCTAssertEqual(element.childNodesCount, 4); - XCTAssertEqual(element.firstChiledNode, firstElement); - XCTAssertEqual(element.lastChildNode, text); + XCTAssertEqual(element.firstChild, firstElement); + XCTAssertEqual(element.lastChild, text); } - (void)testAppendDocumentFragment @@ -105,10 +104,10 @@ XCTAssertEqualObjects(firstChild.parentNode, element); XCTAssertEqualObjects(secondChild.parentNode, element); - XCTAssertEqualObjects(element.firstChiledNode, comment); - XCTAssertEqualObjects(element.firstChiledNode.nextSibling, firstChild); - XCTAssertEqualObjects(element.lastChildNode.previousSibling, firstChild); - XCTAssertEqualObjects(element.lastChildNode, secondChild); + XCTAssertEqualObjects(element.firstChild, comment); + XCTAssertEqualObjects(element.firstChild.nextSibling, firstChild); + XCTAssertEqualObjects(element.lastChild.previousSibling, firstChild); + XCTAssertEqualObjects(element.lastChild, secondChild); } - (void)testParentNode @@ -171,20 +170,20 @@ HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement]; [node appendNode:firstChild]; - XCTAssertEqualObjects(node.firstChiledNode, firstChild); - XCTAssertEqualObjects(node.lastChildNode, firstChild); + XCTAssertEqualObjects(node.firstChild, firstChild); + XCTAssertEqualObjects(node.lastChild, firstChild); HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement]; [node appendNode:secondChild]; - XCTAssertEqualObjects(node.firstChiledNode, firstChild); - XCTAssertEqualObjects(node.lastChildNode, secondChild); + XCTAssertEqualObjects(node.firstChild, firstChild); + XCTAssertEqualObjects(node.lastChild, secondChild); HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement]; [node appendNode:thirdChild]; - XCTAssertEqualObjects(node.firstChiledNode, firstChild); - XCTAssertEqualObjects(node.lastChildNode, thirdChild); + XCTAssertEqualObjects(node.firstChild, firstChild); + XCTAssertEqualObjects(node.lastChild, thirdChild); } - (void)testNextAndPreviousSiblingNodes @@ -470,4 +469,17 @@ XCTAssertEqualObjects(thirdChild.parentNode, newParent); } +- (void)testElementSetInnerHTML +{ + HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"]; + [element appendNode:[[HTMLElement alloc] initWithTagName:@"img"]]; + + [element setInnerHTML:@"<p></p><p></p>"]; + + XCTAssertEqual(element.childNodesCount, 2); + + XCTAssertEqualObjects(element.firstChild.asElement.tagName, @"p"); + XCTAssertEqualObjects([element childNodeAtIndex:1].asElement.tagName, @"p"); +} + @end diff --git a/HTMLKitTests/HTMLKitParserPerformance.m b/HTMLKitTests/HTMLKitParserPerformance.m index f4839d0..005282f 100644 --- a/HTMLKitTests/HTMLKitParserPerformance.m +++ b/HTMLKitTests/HTMLKitParserPerformance.m @@ -15,6 +15,8 @@ @implementation HTMLKitParserPerformance +#define HTMLKIT_NO_DOM_CHECKS + - (void)testParserPerformance { NSString *path = [[NSBundle bundleForClass:self.class] resourcePath]; @@ -28,4 +30,6 @@ }]; } +#undef HTMLKIT_NO_DOM_CHECKS + @end diff --git a/HTMLKitTests/HTMLKitTreeConstructionTests.m b/HTMLKitTests/HTMLKitTreeConstructionTests.m index cfb7430..ca1f223 100644 --- a/HTMLKitTests/HTMLKitTreeConstructionTests.m +++ b/HTMLKitTests/HTMLKitTreeConstructionTests.m @@ -9,7 +9,7 @@ #import <XCTest/XCTest.h> #import "HTML5LibTreeConstructionTest.h" -#import "HTMLNodes.h" +#import "HTMLDOM.h" #import "HTMLParser.h" @@ -121,14 +121,14 @@ { XCTAssertEqualObjects(actual.name, expected.name, @"Node name mismatch [%@ should be %@]:\n%@", actual.name, expected.name, message); - XCTAssert(actual.type == expected.type, @"Node type mismatch [%hd should be %hd]:\n%@", - actual.type, expected.type, message); + XCTAssert(actual.nodeType == expected.nodeType, @"Node type mismatch [%hd should be %hd]:\n%@", + actual.nodeType, expected.nodeType, message); - if (actual.type != expected.type) { + if (actual.nodeType != expected.nodeType) { return; } - switch (actual.type) { + switch (actual.nodeType) { case HTMLNodeDocumentType: XCTAssertEqualObjects([(HTMLDocumentType *)actual publicIdentifier], [(HTMLDocumentType *)expected publicIdentifier], @"%@", message); XCTAssertEqualObjects([(HTMLDocumentType *)actual systemIdentifier], [(HTMLDocumentType *)expected systemIdentifier], @"%@", message); diff --git a/HTMLKitTests/HTMLTreeWalkerTests.m b/HTMLKitTests/HTMLTreeWalkerTests.m new file mode 100644 index 0000000..6aeeaf2 --- /dev/null +++ b/HTMLKitTests/HTMLTreeWalkerTests.m @@ -0,0 +1,503 @@ + // + // HTMLTreeWalkerTests.m + // HTMLKit + // + // Created by Iska on 05/06/15. + // Copyright (c) 2015 BrainCookie. All rights reserved. + // + +#import <XCTest/XCTest.h> +#import "HTMLTreeWalker.h" +#import "HTMLDOM.h" + +@interface HTMLTreeWalkerTests : XCTestCase + +@end + +@implementation HTMLTreeWalkerTests + +#pragma mark - Asserts + +#define AssertElementWithId(input, id) \ + do { \ + HTMLNode *node = input;\ + XCTAssertEqual(node.nodeType, HTMLNodeElement);\ + XCTAssertEqualObjects(node.asElement[@"id"], id);\ + } while(0) + +#define AssertTextWithValue(input, value) \ + do { \ + HTMLNode *node = input;\ + XCTAssertEqual(node.nodeType, HTMLNodeText);\ + XCTAssertEqualObjects(node.textContent, value);\ + } while(0) + +#define AssertCommentWithValue(input, value) \ + do { \ + HTMLNode *node = input;\ + XCTAssertEqual(node.nodeType, HTMLNodeComment);\ + XCTAssertEqualObjects(node.textContent, value);\ + } while(0) + +#pragma mark - Tests + +- (void)testTreeWalkerInit +{ + HTMLNode *root = self.basicWalkingDOM; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root]; + + XCTAssertNotNil(walker); + XCTAssertNotNil(walker.root); + XCTAssertNotNil(walker.currentNode); + XCTAssertNil(walker.filter); + XCTAssertEqual(walker.whatToShow, HTMLNodeFilterShowAll); + + XCTAssertEqualObjects(walker.root, root); + XCTAssertEqualObjects(walker.root, walker.currentNode); +} + +#pragma mark - Basic Walking + +- (HTMLNode *)basicWalkingDOM +{ + // Tree structure: + // #a + // | + // +----+----+ + // | | + // "b" #c + // | + // +----+----+ + // | | + // #d <!--j--> + // | + // +----+----+ + // | | | + // "e" #f "i" + // | + // +--+--+ + // | | + // "g" <!--h--> + + HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"a"}]; + + [div appendNode:[[HTMLText alloc] initWithData:@"b"]]; + + HTMLElement *c = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"c"}]; + [div appendNode:c]; + + HTMLElement *d = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"d"}]; + [c appendNode:d]; + [c appendNode:[[HTMLComment alloc] initWithData:@"j"]]; + + [d appendNode:[[HTMLText alloc] initWithData:@"e"]]; + + HTMLElement *f = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"f"}]; + [d appendNode:f]; + [d appendNode:[[HTMLText alloc] initWithData:@"i"]]; + + [f appendNode:[[HTMLText alloc] initWithData:@"g"]]; + [f appendNode:[[HTMLComment alloc] initWithData:@"h"]]; + + return div; +} + +- (void)testBasicWalking +{ + HTMLNode *root = self.basicWalkingDOM; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root]; + + HTMLNode *f = root.lastChild.firstChild.childNodes[1]; + + AssertElementWithId(walker.currentNode, @"a"); + XCTAssertNil(walker.parentNode); + AssertElementWithId(walker.currentNode, @"a"); + + AssertTextWithValue(walker.firstChild, @"b"); + AssertTextWithValue(walker.currentNode, @"b"); + + AssertElementWithId(walker.nextSibling, @"c"); + AssertElementWithId(walker.currentNode, @"c"); + + AssertCommentWithValue(walker.lastChild, @"j"); + AssertCommentWithValue(walker.currentNode, @"j"); + + AssertElementWithId(walker.previousSibling, @"d"); + AssertElementWithId(walker.currentNode, @"d"); + + AssertTextWithValue(walker.nextNode, @"e"); + AssertTextWithValue(walker.currentNode, @"e"); + + AssertElementWithId(walker.parentNode, @"d"); + AssertElementWithId(walker.currentNode, @"d"); + + AssertElementWithId(walker.previousNode, @"c"); + AssertElementWithId(walker.currentNode, @"c"); + + XCTAssertNil(walker.nextSibling); + AssertElementWithId(walker.currentNode, @"c"); + + walker.currentNode = f; + XCTAssertEqualObjects(walker.currentNode, f); +} + +#pragma mark - Current Node + +- (HTMLDocument *)currentNodeDOM +{ + HTMLDocument *document = [HTMLDocument documentWithString: + @"<div id='first'><p><a></a></p></div>" + @"<div id='second'><p><b></b></p</div>"]; + + return document; +} + +- (void)testThatTreeWalkerParentHasNoEffectCurrentNodeWhenParentIsNotUnderRoot +{ + HTMLDocument *document = self.currentNodeDOM; + HTMLNode *first = document.body.firstChild; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:first + showOptions:HTMLNodeFilterShowElement + filter:nil]; + + AssertElementWithId(walker.currentNode, @"first"); + XCTAssertNil(walker.parentNode); + AssertElementWithId(walker.currentNode, @"first"); +} + +- (void)testThatSettingCurrentNodeToNodesNotUnderRootIsHandledCorrectly +{ + HTMLDocument *document = self.currentNodeDOM; + HTMLNode *first = document.body.firstChild; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:first + showOptions:HTMLNodeFilterShowElement|HTMLNodeFilterShowComment + filter:nil]; + walker.currentNode = document.documentElement; + XCTAssertNil(walker.parentNode); + XCTAssertEqualObjects(walker.currentNode, document.documentElement); + + walker.currentNode = document.documentElement; + XCTAssertEqualObjects(walker.nextNode, document.documentElement.firstChild); + XCTAssertEqualObjects(walker.currentNode, document.documentElement.firstChild); + + walker.currentNode = document.documentElement; + XCTAssertNil(walker.previousNode); + XCTAssertEqualObjects(walker.currentNode, document.documentElement); + + walker.currentNode = document.documentElement; + XCTAssertEqualObjects(walker.firstChild, document.documentElement.firstChild); + XCTAssertEqualObjects(walker.currentNode, document.documentElement.firstChild); + + walker.currentNode = document.documentElement; + XCTAssertEqualObjects(walker.lastChild, document.documentElement.lastChild); + XCTAssertEqualObjects(walker.currentNode, document.documentElement.lastChild); + + walker.currentNode = document.documentElement; + XCTAssertNil(walker.nextSibling); + XCTAssertEqualObjects(walker.currentNode, document.documentElement); + + walker.currentNode = document.documentElement; + XCTAssertNil(walker.previousSibling); + XCTAssertEqualObjects(walker.currentNode, document.documentElement); +} + +#pragma mark - Filter + +- (HTMLElement *)filterBasicDOM +{ + HTMLElement *root = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"root"}]; + + HTMLElement *a1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"A1"}]; + HTMLElement *b1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B1"}]; + HTMLElement *b2 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B2"}]; + + [root appendNode:a1]; + [a1 appendNode:b1]; + [a1 appendNode:b2]; + + return root; +} + +- (void)testTreeWalkerNilFilter +{ + HTMLElement *root = self.filterBasicDOM; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:nil]; + + AssertElementWithId(walker.currentNode, @"root"); + AssertElementWithId(walker.firstChild, @"A1"); + AssertElementWithId(walker.currentNode, @"A1"); + AssertElementWithId(walker.nextNode, @"B1"); + AssertElementWithId(walker.currentNode, @"B1"); +} + +- (void)testTreeWalkerWithFilter +{ + HTMLElement *root = self.filterBasicDOM; + + id<HTMLNodeFilter> filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) { + if ([node.asElement[@"id"] isEqualToString:@"B1"]) { + return HTMLNodeFilterSkip; + } + return HTMLNodeFilterAccept; + }]; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + AssertElementWithId(walker.currentNode, @"root"); + AssertElementWithId(walker.firstChild, @"A1"); + AssertElementWithId(walker.currentNode, @"A1"); + AssertElementWithId(walker.nextNode, @"B2"); + AssertElementWithId(walker.currentNode, @"B2"); +} + +#pragma mark - Filter Skip + +- (HTMLElement *)filterDOM +{ + HTMLElement *root = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"root"}]; + + HTMLElement *a1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"A1"}]; + HTMLElement *b1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B1"}]; + HTMLElement *b2 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B2"}]; + HTMLElement *b3 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B3"}]; + HTMLElement *c1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"C1"}]; + + [root appendNode:a1]; + [a1 appendNode:b1]; + [a1 appendNode:b2]; + [a1 appendNode:b3]; + [b1 appendNode:c1]; + + return root; +} + +- (id<HTMLNodeFilter>)skipB1Filter +{ + id<HTMLNodeFilter> filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) { + if ([node.asElement[@"id"] isEqualToString:@"B1"]) { + return HTMLNodeFilterSkip; + } + return HTMLNodeFilterAccept; + }]; + return filter; +} + +- (id<HTMLNodeFilter>)skipB2Filter +{ + id<HTMLNodeFilter> filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) { + if ([node.asElement[@"id"] isEqualToString:@"B2"]) { + return HTMLNodeFilterSkip; + } + return HTMLNodeFilterAccept; + }]; + return filter; +} + +- (id<HTMLNodeFilter>)rejectB1Filter +{ + id<HTMLNodeFilter> filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) { + if ([node.asElement[@"id"] isEqualToString:@"B1"]) { + return HTMLNodeFilterReject; + } + return HTMLNodeFilterAccept; + }]; + return filter; +} + +- (id<HTMLNodeFilter>)rejectB2Filter +{ + id<HTMLNodeFilter> filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) { + if ([node.asElement[@"id"] isEqualToString:@"B2"]) { + return HTMLNodeFilterReject; + } + return HTMLNodeFilterAccept; + }]; + return filter; +} + +static HTMLElement * (^ FindElementById)(HTMLNode *, NSString *) = ^ HTMLElement * (HTMLNode *root, NSString *id) { + for (HTMLNode *node in [root nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]) { + if ([node.asElement[@"id"] isEqualToString:id]) { + return node.asElement; + } + } + return nil; +}; + +- (void)testThatFilterSkipsNextNode +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.skipB1Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + AssertElementWithId(walker.nextNode, @"A1"); + AssertElementWithId(walker.nextNode, @"C1"); + AssertElementWithId(walker.nextNode, @"B2"); + AssertElementWithId(walker.nextNode, @"B3"); +} + +- (void)testThatFilterSkipsFirstChild +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.skipB1Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + AssertElementWithId(walker.firstChild, @"A1"); + AssertElementWithId(walker.firstChild, @"C1"); +} + +- (void)testThatFilterSkipsNextSibling +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.skipB2Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + AssertElementWithId(walker.firstChild, @"A1"); + AssertElementWithId(walker.firstChild, @"B1"); + AssertElementWithId(walker.nextSibling, @"B3"); +} + +- (void)testThatFilterSkipsParentNode +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.skipB1Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + walker.currentNode = FindElementById(root, @"C1"); + AssertElementWithId(walker.parentNode, @"A1"); +} + +- (void)testThatFilterSkipsPreviousSibling +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.skipB2Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + walker.currentNode = FindElementById(root, @"B3"); + AssertElementWithId(walker.previousSibling, @"B1"); +} + +- (void)testThatFilterSkipsPreviousNode +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.skipB1Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + walker.currentNode = FindElementById(root, @"B3"); + AssertElementWithId(walker.previousNode, @"B2"); + AssertElementWithId(walker.previousNode, @"C1"); + AssertElementWithId(walker.previousNode, @"A1"); +} + +#pragma mark - Filter Reject + +- (void)testThatFilterRejectsNextNode +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.rejectB1Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + AssertElementWithId(walker.nextNode, @"A1"); + AssertElementWithId(walker.nextNode, @"B2"); + AssertElementWithId(walker.nextNode, @"B3"); +} + +- (void)testThatFilterRejectsFirstChild +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.rejectB1Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + AssertElementWithId(walker.firstChild, @"A1"); + AssertElementWithId(walker.firstChild, @"B2"); +} + +- (void)testThatFilterRejectsNextSibling +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.rejectB2Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + AssertElementWithId(walker.firstChild, @"A1"); + AssertElementWithId(walker.firstChild, @"B1"); + AssertElementWithId(walker.nextSibling, @"B3"); +} + +- (void)testThatFilterRejectsParentNode +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.rejectB1Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + walker.currentNode = FindElementById(root, @"C1"); + AssertElementWithId(walker.parentNode, @"A1"); +} + +- (void)testThatFilterRejectsPreviousSibling +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.rejectB2Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + walker.currentNode = FindElementById(root, @"B3"); + AssertElementWithId(walker.previousSibling, @"B1"); +} + +- (void)testThatFilterRejectsPreviousNode +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.rejectB1Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + walker.currentNode = FindElementById(root, @"B3"); + AssertElementWithId(walker.previousNode, @"B2"); + AssertElementWithId(walker.previousNode, @"A1"); +} + + +@end