diff --git a/.jazzy.yaml b/.jazzy.yaml index 9fab102..115f74d 100644 --- a/.jazzy.yaml +++ b/.jazzy.yaml @@ -1,5 +1,5 @@ module: HTMLKit -module_version: 1.0.0 +module_version: 1.1.0 author: Iskandar Abudiab author_url: https://twitter.com/iabudiab github_url: https://github.com/iabudiab/HTMLKit @@ -27,10 +27,12 @@ custom_categories: - HTMLDocumentType - HTMLDocumentFragment - HTMLDocumentPosition + - HTMLCharacterData - HTMLText - HTMLComment - HTMLTemplate - HTMLDOMTokenList + - HTMLRange - name: Iteration & Filtering children: diff --git a/.travis.yml b/.travis.yml index 095d84f..313ee3f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode8 +osx_image: xcode8.2 branches: except: @@ -17,21 +17,21 @@ env: - MACOS_FRAMEWORK_SCHEME=HTMLKit-macOS - WATCHOS_FRAMEWORK_SCHEME="HTMLKit-watchOS" - TVOS_FRAMEWORK_SCHEME="HTMLKit-tvOS" - - IOS_SDK=iphonesimulator10.0 + - IOS_SDK=iphonesimulator10.2 - MACOS_SDK=macosx10.12 - - WATCHOS_SDK=watchsimulator3.0 - - TVOS_SDK=appletvsimulator10.0 + - WATCHOS_SDK=watchsimulator3.1 + - TVOS_SDK=appletvsimulator10.1 matrix: - - DESTINATION="arch=x86_64" SIMULATOR="" SCHEME="$MACOS_FRAMEWORK_SCHEME" SDK="$MACOS_SDK" - - DESTINATION="OS=9.0,name=iPhone 6" SIMULATOR="iPhone 6 (9.0)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" - - DESTINATION="OS=9.1,name=iPhone 6 Plus" SIMULATOR="iPhone 6 Plus (9.1)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" - - DESTINATION="OS=9.2,name=iPhone 6S" SIMULATOR="iPhone 6S (9.2)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" - - DESTINATION="OS=9.3,name=iPhone 6S Plus" SIMULATOR="iPhone 6S Plus (9.3)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" - - DESTINATION="OS=10.0,name=iPhone 7 Plus" SIMULATOR="iPhone 7 Plus (10.0)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" - - DESTINATION="OS=2.0,name=Apple Watch - 42mm" SIMULATOR="Apple Watch - 42mm (2.2)" SCHEME="$WATCHOS_FRAMEWORK_SCHEME" SDK="$WATCHOS_SDK" - - DESTINATION="OS=3.0,name=Apple Watch - 42mm" SIMULATOR="Apple Watch - 42mm (3.0)" SCHEME="$WATCHOS_FRAMEWORK_SCHEME" SDK="$WATCHOS_SDK" - - DESTINATION="OS=9.0,name=Apple TV 1080p" SIMULATOR="Apple TV 1080p (9.2)" SCHEME="$TVOS_FRAMEWORK_SCHEME" SDK="$TVOS_SDK" - - DESTINATION="OS=10.0,name=Apple TV 1080p" SIMULATOR="Apple TV 1080p (10.0)" SCHEME="$TVOS_FRAMEWORK_SCHEME" SDK="$TVOS_SDK" + - DESTINATION="arch=x86_64" SIMULATOR="" SCHEME="$MACOS_FRAMEWORK_SCHEME" SDK="$MACOS_SDK" + - DESTINATION="OS=9.0,name=iPhone 6" SIMULATOR="iPhone 6 (9.0)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" + - DESTINATION="OS=9.1,name=iPhone 6 Plus" SIMULATOR="iPhone 6 Plus (9.1)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" + - DESTINATION="OS=9.2,name=iPhone 6S" SIMULATOR="iPhone 6S (9.2)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" + - DESTINATION="OS=9.3,name=iPhone 6S Plus" SIMULATOR="iPhone 6S Plus (9.3)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" + - DESTINATION="OS=10.1,name=iPhone 7 Plus" SIMULATOR="iPhone 7 Plus (10.1)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" + - DESTINATION="OS=2.2,name=Apple Watch - 42mm" SIMULATOR="Apple Watch - 42mm (2.2)" SCHEME="$WATCHOS_FRAMEWORK_SCHEME" SDK="$WATCHOS_SDK" + - DESTINATION="OS=3.1,name=Apple Watch Series 2 - 42mm" SIMULATOR="Apple Watch Series 2 - 42mm (3.1)" SCHEME="$WATCHOS_FRAMEWORK_SCHEME" SDK="$WATCHOS_SDK" + - DESTINATION="OS=9.0,name=Apple TV 1080p" SIMULATOR="Apple TV 1080p (9.2)" SCHEME="$TVOS_FRAMEWORK_SCHEME" SDK="$TVOS_SDK" + - DESTINATION="OS=10.0,name=Apple TV 1080p" SIMULATOR="Apple TV 1080p (10.0)" SCHEME="$TVOS_FRAMEWORK_SCHEME" SDK="$TVOS_SDK" script: - set -o pipefail @@ -39,7 +39,7 @@ script: - xcodebuild -showsdks - SIMULATOR_ID=$(xcrun instruments -s devices | grep -io "$SIMULATOR \[.*\]" | grep -o "\[.*\]" | sed "s/^\[\(.*\)\]$/\1/") - open -b com.apple.iphonesimulator --args -CurrentDeviceUDID $SIMULATOR_ID - - xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO clean build | xcpretty -c + - travis_retry xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO clean build | xcpretty -c - if [ "$SDK" != "$WATCHOS_SDK" ]; then - xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO test | xcpretty -c; + travis_retry xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO test | xcpretty -c; fi diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e8cb29..37ffbba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Change Log +## [1.1.0](https://github.com/iabudiab/HTMLKit/releases/tag/1.1.0) + +Released on 2017.01.14 + +### Added + +- `DOM Ranges` implementation ([spec](https://dom.spec.whatwg.org/#ranges)) +- `HTMLChatacterData` as base class for `HTMLText` & `HTMLComment` + - `HTMLText` and `HTMLComment` no longer extend `HTMLNode` directly +- `splitText` implementation for `HTMLText` nodes +- `index` property for `HTMLNode` +- `cloneNodeDeep` method for `HTMLNode` + +### Deprecated + +- `appendString` method in `HTMLText` in favor of `appendData` from the supperclass `HTMLCharacterData` + + ## [1.0.0](https://github.com/iabudiab/HTMLKit/releases/tag/1.0.0) Released on 2016.09.28 @@ -61,7 +79,6 @@ This release passes all tokenizer and tree construction html5lib-tests as of 201 - `` is completely removed from the spec now, therefore it is dropped from the implementation - `Tokenizer` and `Tree-Construction` tests are now generated dynamically - Test failures are collected by a `XCTestObservation` for better reporting - - `` is completely removed from the spec now, therefore it is dropped from the implementation - `Tokenizer` and `Tree-Construction` tests are now generated dynamically - Test failures are collected by a `XCTestObservation` for better reporting diff --git a/HTMLKit.podspec b/HTMLKit.podspec index 8025949..13c58c1 100644 --- a/HTMLKit.podspec +++ b/HTMLKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "HTMLKit" - s.version = "1.0.0" + s.version = "1.1.0" s.summary = "HTMLKit, an Objective-C framework for your everyday HTML needs." s.license = "MIT" s.homepage = "https://github.com/iabudiab/HTMLKit" @@ -16,7 +16,7 @@ Pod::Spec.new do |s| s.source_files = "Sources", "Sources/**/*.{h,m}" s.private_header_files = [ - 'Sources/**/*{HTMLToken,HTMLTokens,HTMLTagToken,HTMLCharacterToken,HTMLCommentToken,HTMLDOCTYPEToken,HTMLEOFToken,HTMLTokenizer,HTMLTokenizerCharacters,HTMLTokenizerEntities,HTMLTokenizerStates,HTMLElementAdjustment,HTMLElementTypes,HTMLInputStreamReader,HTMLListOfActiveFormattingElements,HTMLNodeTraversal,HTMLParseErrorToken,HTMLParserInsertionModes,HTMLStackOfOpenElements,HTMLNode+Private,HTMLMarker,CSSCodePoints,CSSInputStream}.h' + 'Sources/**/*{HTMLToken,HTMLTokens,HTMLTagToken,HTMLCharacterToken,HTMLCommentToken,HTMLDOCTYPEToken,HTMLEOFToken,HTMLTokenizer,HTMLTokenizerCharacters,HTMLTokenizerEntities,HTMLTokenizerStates,HTMLElementAdjustment,HTMLElementTypes,HTMLInputStreamReader,HTMLListOfActiveFormattingElements,HTMLParseErrorToken,HTMLParserInsertionModes,HTMLStackOfOpenElements,HTMLMarker,HTMLNode+Private,HTMLDocument+Private,HTMLCharacterData+Private,HTMLRange+Private,HTMLParser+Private,HTMLNodeIterator+Private,HTMLNodeTraversal,HTMLDOMUtils,CSSCodePoints,CSSInputStream}.h' ] s.requires_arc = true diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index 000f546..fcd465f 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -7,14 +7,26 @@ objects = { /* Begin PBXBuildFile section */ + 62056C4F1E2AD9FB009A4EE0 /* HTMLNodeIterator+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 62056C4E1E2AD9FB009A4EE0 /* HTMLNodeIterator+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 62056C501E2AD9FB009A4EE0 /* HTMLNodeIterator+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 62056C4E1E2AD9FB009A4EE0 /* HTMLNodeIterator+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 62056C511E2AD9FB009A4EE0 /* HTMLNodeIterator+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 62056C4E1E2AD9FB009A4EE0 /* HTMLNodeIterator+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 62056C521E2AD9FB009A4EE0 /* HTMLNodeIterator+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 62056C4E1E2AD9FB009A4EE0 /* HTMLNodeIterator+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; 62132E5A1C01F83200084175 /* CSSSelectorTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 62132E581C01F83200084175 /* CSSSelectorTest.m */; }; 62132E5D1C021FF200084175 /* css-tests in Resources */ = {isa = PBXBuildFile; fileRef = 62132E5B1C021FF200084175 /* css-tests */; }; 6216ACFD1C28DCC80074CAB4 /* CSSExtensionSelectorsParsingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6216ACFC1C28DCC80074CAB4 /* CSSExtensionSelectorsParsingTests.m */; }; 6216ACFE1C28DCC80074CAB4 /* CSSExtensionSelectorsParsingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6216ACFC1C28DCC80074CAB4 /* CSSExtensionSelectorsParsingTests.m */; }; + 621CC4911DEA704000D1992A /* HTMLDocument+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 621CC48F1DEA704000D1992A /* HTMLDocument+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 621CC4921DEA704000D1992A /* HTMLDocument+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 621CC48F1DEA704000D1992A /* HTMLDocument+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 621CC4931DEA704000D1992A /* HTMLDocument+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 621CC48F1DEA704000D1992A /* HTMLDocument+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 621CC4941DEA704000D1992A /* HTMLDocument+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 621CC48F1DEA704000D1992A /* HTMLDocument+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 621CC4A51DEA721A00D1992A /* HTMLParser+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 621CC4A31DEA721A00D1992A /* HTMLParser+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 621CC4A61DEA721A00D1992A /* HTMLParser+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 621CC4A31DEA721A00D1992A /* HTMLParser+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 621CC4A71DEA721A00D1992A /* HTMLParser+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 621CC4A31DEA721A00D1992A /* HTMLParser+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 621CC4A81DEA721A00D1992A /* HTMLParser+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 621CC4A31DEA721A00D1992A /* HTMLParser+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; 621FBE5B1BDAD68700BC9555 /* CSSSelectorParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 621FBE591BDAD68700BC9555 /* CSSSelectorParserTests.m */; }; 621FBE5E1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 621FBE5C1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m */; }; 6236738F1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 6236738D1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m */; }; - 623916C81AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */; }; + 623916C81AC7209E0066B4FE /* HTMLNodeIteratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLNodeIteratorTests.m */; }; 6239755B1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623975591AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m */; }; 6239755F1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6239755D1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m */; }; 623CAF9E1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 623CAF9C1AD88BEA00E34C32 /* HTMLKitParserPerformance.m */; }; @@ -24,15 +36,23 @@ 624AC90219FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AC90019FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m */; }; 624AC90519FBFE9A00BD3C4A /* html5lib-tests in Resources */ = {isa = PBXBuildFile; fileRef = 624AC90419FBFE8A00BD3C4A /* html5lib-tests */; }; 624AC90F19FC702E00BD3C4A /* HTML Standard.html in Resources */ = {isa = PBXBuildFile; fileRef = 624AC90D19FC702E00BD3C4A /* HTML Standard.html */; }; - 624B9FB31AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB11AE0313300646C4C /* HTMLKitStringCategoryTests.m */; }; - 624B9FB61AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */; }; - 624FC37B1AE591D80015DDF9 /* HTMLKitNodesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */; }; + 624B9FB31AE0313300646C4C /* HTMLStringCategoryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB11AE0313300646C4C /* HTMLStringCategoryTests.m */; }; + 624B9FB61AE0391400646C4C /* HTMLOrderedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB41AE0391400646C4C /* HTMLOrderedDictionaryTests.m */; }; + 624C777A1DE9D05E00DD6DFA /* HTMLCharacterData.h in Headers */ = {isa = PBXBuildFile; fileRef = 624C77781DE9D05E00DD6DFA /* HTMLCharacterData.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 624C777B1DE9D05E00DD6DFA /* HTMLCharacterData.h in Headers */ = {isa = PBXBuildFile; fileRef = 624C77781DE9D05E00DD6DFA /* HTMLCharacterData.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 624C777C1DE9D05E00DD6DFA /* HTMLCharacterData.h in Headers */ = {isa = PBXBuildFile; fileRef = 624C77781DE9D05E00DD6DFA /* HTMLCharacterData.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 624C777D1DE9D05E00DD6DFA /* HTMLCharacterData.h in Headers */ = {isa = PBXBuildFile; fileRef = 624C77781DE9D05E00DD6DFA /* HTMLCharacterData.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 624C777E1DE9D05E00DD6DFA /* HTMLCharacterData.m in Sources */ = {isa = PBXBuildFile; fileRef = 624C77791DE9D05E00DD6DFA /* HTMLCharacterData.m */; }; + 624C777F1DE9D05E00DD6DFA /* HTMLCharacterData.m in Sources */ = {isa = PBXBuildFile; fileRef = 624C77791DE9D05E00DD6DFA /* HTMLCharacterData.m */; }; + 624C77801DE9D05E00DD6DFA /* HTMLCharacterData.m in Sources */ = {isa = PBXBuildFile; fileRef = 624C77791DE9D05E00DD6DFA /* HTMLCharacterData.m */; }; + 624C77811DE9D05E00DD6DFA /* HTMLCharacterData.m in Sources */ = {isa = PBXBuildFile; fileRef = 624C77791DE9D05E00DD6DFA /* HTMLCharacterData.m */; }; + 624FC37B1AE591D80015DDF9 /* HTMLNodesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624FC3791AE591D80015DDF9 /* HTMLNodesTests.m */; }; 62567F501C0CB5750025D458 /* HTMLDOMTokenList.h in Headers */ = {isa = PBXBuildFile; fileRef = 62567F4E1C0CB5750025D458 /* HTMLDOMTokenList.h */; settings = {ATTRIBUTES = (Public, ); }; }; 62567F511C0CB5750025D458 /* HTMLDOMTokenList.h in Headers */ = {isa = PBXBuildFile; fileRef = 62567F4E1C0CB5750025D458 /* HTMLDOMTokenList.h */; settings = {ATTRIBUTES = (Public, ); }; }; 62567F521C0CB5750025D458 /* HTMLDOMTokenList.m in Sources */ = {isa = PBXBuildFile; fileRef = 62567F4F1C0CB5750025D458 /* HTMLDOMTokenList.m */; }; 62567F531C0CB5750025D458 /* HTMLDOMTokenList.m in Sources */ = {isa = PBXBuildFile; fileRef = 62567F4F1C0CB5750025D458 /* HTMLDOMTokenList.m */; }; - 62567F561C0CC3AE0025D458 /* HTMLKitDOMTokenListTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62567F551C0CC3AE0025D458 /* HTMLKitDOMTokenListTests.m */; }; - 62567F571C0CC3AE0025D458 /* HTMLKitDOMTokenListTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62567F551C0CC3AE0025D458 /* HTMLKitDOMTokenListTests.m */; }; + 62567F561C0CC3AE0025D458 /* HTMLDOMTokenListTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62567F551C0CC3AE0025D458 /* HTMLDOMTokenListTests.m */; }; + 62567F571C0CC3AE0025D458 /* HTMLDOMTokenListTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62567F551C0CC3AE0025D458 /* HTMLDOMTokenListTests.m */; }; 625A14B019C7829400AD0C32 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14AF19C7829400AD0C32 /* Cocoa.framework */; }; 625A14C519C7829400AD0C32 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14C419C7829400AD0C32 /* XCTest.framework */; }; 625A14C919C7829400AD0C32 /* HTMLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14AC19C7829400AD0C32 /* HTMLKit.framework */; }; @@ -42,6 +62,18 @@ 625EE4581CBAA41D00F2CC8E /* HTMLKitTestObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 625EE4561CBAA41D00F2CC8E /* HTMLKitTestObserver.m */; }; 625EE45B1CBB171300F2CC8E /* HTMLKitTestUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 625EE45A1CBB171300F2CC8E /* HTMLKitTestUtil.m */; }; 625EE45C1CBB171300F2CC8E /* HTMLKitTestUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 625EE45A1CBB171300F2CC8E /* HTMLKitTestUtil.m */; }; + 626679CF1DEA3DAF004353E7 /* HTMLCharacterData+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 626679CD1DEA3DAF004353E7 /* HTMLCharacterData+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 626679D01DEA3DAF004353E7 /* HTMLCharacterData+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 626679CD1DEA3DAF004353E7 /* HTMLCharacterData+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 626679D11DEA3DAF004353E7 /* HTMLCharacterData+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 626679CD1DEA3DAF004353E7 /* HTMLCharacterData+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 626679D21DEA3DAF004353E7 /* HTMLCharacterData+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 626679CD1DEA3DAF004353E7 /* HTMLCharacterData+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 626BE1E21DF3819500C49514 /* HTMLDOMUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 626BE1E01DF3819500C49514 /* HTMLDOMUtils.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 626BE1E31DF3819500C49514 /* HTMLDOMUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 626BE1E01DF3819500C49514 /* HTMLDOMUtils.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 626BE1E41DF3819500C49514 /* HTMLDOMUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 626BE1E01DF3819500C49514 /* HTMLDOMUtils.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 626BE1E51DF3819500C49514 /* HTMLDOMUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 626BE1E01DF3819500C49514 /* HTMLDOMUtils.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 626BE1E61DF3819600C49514 /* HTMLDOMUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 626BE1E11DF3819500C49514 /* HTMLDOMUtils.m */; }; + 626BE1E71DF3819600C49514 /* HTMLDOMUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 626BE1E11DF3819500C49514 /* HTMLDOMUtils.m */; }; + 626BE1E81DF3819600C49514 /* HTMLDOMUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 626BE1E11DF3819500C49514 /* HTMLDOMUtils.m */; }; + 626BE1E91DF3819600C49514 /* HTMLDOMUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 626BE1E11DF3819500C49514 /* HTMLDOMUtils.m */; }; 62857C6B1D3988EE008DC254 /* HTMLKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 625A14BC19C7829400AD0C32 /* HTMLKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 62857C6C1D3988EE008DC254 /* HTMLKitErrorDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = 626652F81C03D30F00C3F121 /* HTMLKitErrorDomain.h */; settings = {ATTRIBUTES = (Public, ); }; }; 62857C6D1D398907008DC254 /* HTMLInputStreamReader.h in Headers */ = {isa = PBXBuildFile; fileRef = 625A150619C78ABA00AD0C32 /* HTMLInputStreamReader.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -197,13 +229,13 @@ 62857D311D39A33C008DC254 /* HTML5LibTreeConstructionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6239755D1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m */; }; 62857D321D39A33C008DC254 /* HTMLKitTreeConstructionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623975591AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m */; }; 62857D331D39A33C008DC254 /* HTMLKitParserPerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 623CAF9C1AD88BEA00E34C32 /* HTMLKitParserPerformance.m */; }; - 62857D341D39A33F008DC254 /* HTMLKitNodeIteratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */; }; + 62857D341D39A33F008DC254 /* HTMLNodeIteratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLNodeIteratorTests.m */; }; 62857D351D39A33F008DC254 /* HTMLTreeWalkerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247171B1B2240B800C11912 /* HTMLTreeWalkerTests.m */; }; - 62857D361D39A33F008DC254 /* HTMLKitNodesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */; }; - 62857D371D39A33F008DC254 /* HTMLKitMutationAlgorithmsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62EC7AE51AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m */; }; - 62857D381D39A342008DC254 /* HTMLKitStringCategoryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB11AE0313300646C4C /* HTMLKitStringCategoryTests.m */; }; - 62857D391D39A342008DC254 /* HTMLKitOrderedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */; }; - 62857D3A1D39A342008DC254 /* HTMLKitDOMTokenListTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62567F551C0CC3AE0025D458 /* HTMLKitDOMTokenListTests.m */; }; + 62857D361D39A33F008DC254 /* HTMLNodesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624FC3791AE591D80015DDF9 /* HTMLNodesTests.m */; }; + 62857D371D39A33F008DC254 /* HTMLMutationAlgorithmsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62EC7AE51AEEAC6F0015D3BE /* HTMLMutationAlgorithmsTests.m */; }; + 62857D381D39A342008DC254 /* HTMLStringCategoryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB11AE0313300646C4C /* HTMLStringCategoryTests.m */; }; + 62857D391D39A342008DC254 /* HTMLOrderedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB41AE0391400646C4C /* HTMLOrderedDictionaryTests.m */; }; + 62857D3A1D39A342008DC254 /* HTMLDOMTokenListTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62567F551C0CC3AE0025D458 /* HTMLDOMTokenListTests.m */; }; 62857D3B1D39A345008DC254 /* CSSSelectorTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 62132E581C01F83200084175 /* CSSSelectorTest.m */; }; 62857D3C1D39A345008DC254 /* CSSSelectorParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 621FBE591BDAD68700BC9555 /* CSSSelectorParserTests.m */; }; 62857D3D1D39A345008DC254 /* CSSExtensionSelectorsParsingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6216ACFC1C28DCC80074CAB4 /* CSSExtensionSelectorsParsingTests.m */; }; @@ -277,7 +309,21 @@ 62857D901D39A47F008DC254 /* CSSCompoundSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 620C87791BD44CBE00FB3EEE /* CSSCompoundSelector.h */; settings = {ATTRIBUTES = (Public, ); }; }; 628AF6301BC99A6C00496128 /* CSSNthExpressionsParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 628AF62E1BC99A6C00496128 /* CSSNthExpressionsParserTests.m */; }; 62D8345A19FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 62D8345819FB1AC4009205A9 /* HTML5LibTokenizerTest.m */; }; - 62EC7AE71AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62EC7AE51AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m */; }; + 62D91C231DE218A500BEFADE /* HTMLRange.h in Headers */ = {isa = PBXBuildFile; fileRef = 62D91C211DE218A500BEFADE /* HTMLRange.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 62D91C241DE218A500BEFADE /* HTMLRange.h in Headers */ = {isa = PBXBuildFile; fileRef = 62D91C211DE218A500BEFADE /* HTMLRange.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 62D91C251DE218A500BEFADE /* HTMLRange.h in Headers */ = {isa = PBXBuildFile; fileRef = 62D91C211DE218A500BEFADE /* HTMLRange.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 62D91C261DE218A500BEFADE /* HTMLRange.h in Headers */ = {isa = PBXBuildFile; fileRef = 62D91C211DE218A500BEFADE /* HTMLRange.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 62D91C271DE218A500BEFADE /* HTMLRange.m in Sources */ = {isa = PBXBuildFile; fileRef = 62D91C221DE218A500BEFADE /* HTMLRange.m */; }; + 62D91C281DE218A500BEFADE /* HTMLRange.m in Sources */ = {isa = PBXBuildFile; fileRef = 62D91C221DE218A500BEFADE /* HTMLRange.m */; }; + 62D91C291DE218A500BEFADE /* HTMLRange.m in Sources */ = {isa = PBXBuildFile; fileRef = 62D91C221DE218A500BEFADE /* HTMLRange.m */; }; + 62D91C2A1DE218A500BEFADE /* HTMLRange.m in Sources */ = {isa = PBXBuildFile; fileRef = 62D91C221DE218A500BEFADE /* HTMLRange.m */; }; + 62E0BA971E25456700E4D193 /* HTMLCharacterDataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62E0BA961E25456700E4D193 /* HTMLCharacterDataTests.m */; }; + 62E0BA981E25456700E4D193 /* HTMLCharacterDataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62E0BA961E25456700E4D193 /* HTMLCharacterDataTests.m */; }; + 62E0BA991E25456700E4D193 /* HTMLCharacterDataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62E0BA961E25456700E4D193 /* HTMLCharacterDataTests.m */; }; + 62EC0A851E158BD80007786B /* HTMLRangeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62EC0A841E158BD80007786B /* HTMLRangeTests.m */; }; + 62EC0A861E158BD80007786B /* HTMLRangeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62EC0A841E158BD80007786B /* HTMLRangeTests.m */; }; + 62EC0A871E158BD80007786B /* HTMLRangeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62EC0A841E158BD80007786B /* HTMLRangeTests.m */; }; + 62EC7AE71AEEAC6F0015D3BE /* HTMLMutationAlgorithmsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62EC7AE51AEEAC6F0015D3BE /* HTMLMutationAlgorithmsTests.m */; }; 62ECBEE11C0B69FD00AF847B /* HTMLKitErrorDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = 626652F81C03D30F00C3F121 /* HTMLKitErrorDomain.h */; settings = {ATTRIBUTES = (Public, ); }; }; 62ECBEE21C0B69FD00AF847B /* HTMLInputStreamReader.h in Headers */ = {isa = PBXBuildFile; fileRef = 625A150619C78ABA00AD0C32 /* HTMLInputStreamReader.h */; settings = {ATTRIBUTES = (Private, ); }; }; 62ECBEE31C0B69FD00AF847B /* HTMLTokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493A419CCC54100BCDDF4 /* HTMLTokenizer.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -485,12 +531,12 @@ 62ECBFCD1C0B6E2E00AF847B /* HTML5LibTreeConstructionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6239755D1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m */; }; 62ECBFCE1C0B6E2E00AF847B /* HTMLKitTreeConstructionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623975591AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m */; }; 62ECBFCF1C0B6E2E00AF847B /* HTMLKitParserPerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 623CAF9C1AD88BEA00E34C32 /* HTMLKitParserPerformance.m */; }; - 62ECBFD01C0B6E2E00AF847B /* HTMLKitNodeIteratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */; }; + 62ECBFD01C0B6E2E00AF847B /* HTMLNodeIteratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLNodeIteratorTests.m */; }; 62ECBFD11C0B6E2E00AF847B /* HTMLTreeWalkerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247171B1B2240B800C11912 /* HTMLTreeWalkerTests.m */; }; - 62ECBFD21C0B6E2E00AF847B /* HTMLKitNodesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */; }; - 62ECBFD31C0B6E2E00AF847B /* HTMLKitMutationAlgorithmsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62EC7AE51AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m */; }; - 62ECBFD41C0B6E2E00AF847B /* HTMLKitStringCategoryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB11AE0313300646C4C /* HTMLKitStringCategoryTests.m */; }; - 62ECBFD51C0B6E2E00AF847B /* HTMLKitOrderedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */; }; + 62ECBFD21C0B6E2E00AF847B /* HTMLNodesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624FC3791AE591D80015DDF9 /* HTMLNodesTests.m */; }; + 62ECBFD31C0B6E2E00AF847B /* HTMLMutationAlgorithmsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62EC7AE51AEEAC6F0015D3BE /* HTMLMutationAlgorithmsTests.m */; }; + 62ECBFD41C0B6E2E00AF847B /* HTMLStringCategoryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB11AE0313300646C4C /* HTMLStringCategoryTests.m */; }; + 62ECBFD51C0B6E2E00AF847B /* HTMLOrderedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB41AE0391400646C4C /* HTMLOrderedDictionaryTests.m */; }; 62ECBFD61C0B6E2E00AF847B /* CSSSelectorTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 62132E581C01F83200084175 /* CSSSelectorTest.m */; }; 62ECBFD71C0B6E2E00AF847B /* CSSSelectorParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 621FBE591BDAD68700BC9555 /* CSSSelectorParserTests.m */; }; 62ECBFD81C0B6E2E00AF847B /* CSSNthExpressionsParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 628AF62E1BC99A6C00496128 /* CSSNthExpressionsParserTests.m */; }; @@ -502,6 +548,10 @@ 62ECBFDE1C0B6E4400AF847B /* HTML Standard.html in Resources */ = {isa = PBXBuildFile; fileRef = 624AC90D19FC702E00BD3C4A /* HTML Standard.html */; }; 62ECBFDF1C0B6E4400AF847B /* html5lib-tests in Resources */ = {isa = PBXBuildFile; fileRef = 624AC90419FBFE8A00BD3C4A /* html5lib-tests */; }; 62F658711BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F6586F1BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m */; }; + 62F7E5A21DEB63F900DEF09B /* HTMLRange+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 62F7E5A01DEB63F900DEF09B /* HTMLRange+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 62F7E5A31DEB63F900DEF09B /* HTMLRange+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 62F7E5A01DEB63F900DEF09B /* HTMLRange+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 62F7E5A41DEB63F900DEF09B /* HTMLRange+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 62F7E5A01DEB63F900DEF09B /* HTMLRange+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 62F7E5A51DEB63F900DEF09B /* HTMLRange+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 62F7E5A01DEB63F900DEF09B /* HTMLRange+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -529,6 +579,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 62056C4E1E2AD9FB009A4EE0 /* HTMLNodeIterator+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "HTMLNodeIterator+Private.h"; path = "include/HTMLNodeIterator+Private.h"; sourceTree = ""; }; 62079BE71AF56F1E00D3B402 /* CSSSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSSSelector.h; path = include/CSSSelector.h; sourceTree = ""; }; 620C87791BD44CBE00FB3EEE /* CSSCompoundSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSSCompoundSelector.h; path = include/CSSCompoundSelector.h; sourceTree = ""; }; 620C877A1BD44CBE00FB3EEE /* CSSCompoundSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSCompoundSelector.m; sourceTree = ""; }; @@ -538,6 +589,8 @@ 62132E581C01F83200084175 /* CSSSelectorTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSSelectorTest.m; sourceTree = ""; }; 62132E5B1C021FF200084175 /* css-tests */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "css-tests"; sourceTree = ""; }; 6216ACFC1C28DCC80074CAB4 /* CSSExtensionSelectorsParsingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSExtensionSelectorsParsingTests.m; sourceTree = ""; }; + 621CC48F1DEA704000D1992A /* HTMLDocument+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "HTMLDocument+Private.h"; path = "include/HTMLDocument+Private.h"; sourceTree = ""; }; + 621CC4A31DEA721A00D1992A /* HTMLParser+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "HTMLParser+Private.h"; path = "include/HTMLParser+Private.h"; sourceTree = ""; }; 621FBE591BDAD68700BC9555 /* CSSSelectorParserTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSSelectorParserTests.m; sourceTree = ""; }; 621FBE5C1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSCombinatorSelectorTests.m; sourceTree = ""; }; 6223211D1A969B9300BACED5 /* HTMLElementTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HTMLElementTypes.h; path = include/HTMLElementTypes.h; sourceTree = ""; }; @@ -575,7 +628,7 @@ 6238579A1A9E8934003A45D9 /* HTMLComment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLComment.m; sourceTree = ""; }; 6238C9831AB8D6330006512E /* HTMLKitDOMExceptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTMLKitDOMExceptions.h; path = include/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 = ""; }; + 623916C61AC7209E0066B4FE /* HTMLNodeIteratorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeIteratorTests.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 = ""; }; @@ -610,12 +663,14 @@ 624B371C1B24E9BA0010BDDF /* CSSInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSSInputStream.h; path = include/CSSInputStream.h; sourceTree = ""; }; 624B371D1B24E9BA0010BDDF /* CSSInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSInputStream.m; sourceTree = ""; }; 624B37211B250BB50010BDDF /* CSSCodePoints.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CSSCodePoints.h; path = include/CSSCodePoints.h; sourceTree = ""; }; - 624B9FB11AE0313300646C4C /* HTMLKitStringCategoryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitStringCategoryTests.m; sourceTree = ""; }; - 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitOrderedDictionaryTests.m; sourceTree = ""; }; - 624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitNodesTests.m; sourceTree = ""; }; + 624B9FB11AE0313300646C4C /* HTMLStringCategoryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLStringCategoryTests.m; sourceTree = ""; }; + 624B9FB41AE0391400646C4C /* HTMLOrderedDictionaryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLOrderedDictionaryTests.m; sourceTree = ""; }; + 624C77781DE9D05E00DD6DFA /* HTMLCharacterData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTMLCharacterData.h; path = include/HTMLCharacterData.h; sourceTree = ""; }; + 624C77791DE9D05E00DD6DFA /* HTMLCharacterData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLCharacterData.m; sourceTree = ""; }; + 624FC3791AE591D80015DDF9 /* HTMLNodesTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodesTests.m; sourceTree = ""; }; 62567F4E1C0CB5750025D458 /* HTMLDOMTokenList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTMLDOMTokenList.h; path = include/HTMLDOMTokenList.h; sourceTree = ""; }; 62567F4F1C0CB5750025D458 /* HTMLDOMTokenList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLDOMTokenList.m; sourceTree = ""; }; - 62567F551C0CC3AE0025D458 /* HTMLKitDOMTokenListTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitDOMTokenListTests.m; sourceTree = ""; }; + 62567F551C0CC3AE0025D458 /* HTMLDOMTokenListTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLDOMTokenListTests.m; sourceTree = ""; }; 625A14AC19C7829400AD0C32 /* HTMLKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = HTMLKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 625A14AF19C7829400AD0C32 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 625A14B219C7829400AD0C32 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -635,6 +690,9 @@ 625EE4591CBB171300F2CC8E /* HTMLKitTestUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLKitTestUtil.h; sourceTree = ""; }; 625EE45A1CBB171300F2CC8E /* HTMLKitTestUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitTestUtil.m; sourceTree = ""; }; 626652F81C03D30F00C3F121 /* HTMLKitErrorDomain.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HTMLKitErrorDomain.h; path = include/HTMLKitErrorDomain.h; sourceTree = ""; }; + 626679CD1DEA3DAF004353E7 /* HTMLCharacterData+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "HTMLCharacterData+Private.h"; path = "include/HTMLCharacterData+Private.h"; sourceTree = ""; }; + 626BE1E01DF3819500C49514 /* HTMLDOMUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTMLDOMUtils.h; path = include/HTMLDOMUtils.h; sourceTree = ""; }; + 626BE1E11DF3819500C49514 /* HTMLDOMUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLDOMUtils.m; sourceTree = ""; }; 6279F87119E17DC700F12EE5 /* HTMLParserInsertionModes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HTMLParserInsertionModes.h; path = include/HTMLParserInsertionModes.h; sourceTree = ""; }; 6279F87219E1808D00F12EE5 /* HTMLElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTMLElement.h; path = include/HTMLElement.h; sourceTree = ""; }; 6279F87319E1808D00F12EE5 /* HTMLElement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLElement.m; sourceTree = ""; }; @@ -661,8 +719,12 @@ 62AE594819F9948A0043F069 /* HTMLCharacterToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLCharacterToken.m; sourceTree = ""; }; 62D8345719FB1AC4009205A9 /* HTML5LibTokenizerTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTML5LibTokenizerTest.h; sourceTree = ""; }; 62D8345819FB1AC4009205A9 /* HTML5LibTokenizerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTML5LibTokenizerTest.m; sourceTree = ""; }; + 62D91C211DE218A500BEFADE /* HTMLRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTMLRange.h; path = include/HTMLRange.h; sourceTree = ""; }; + 62D91C221DE218A500BEFADE /* HTMLRange.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLRange.m; sourceTree = ""; }; + 62E0BA961E25456700E4D193 /* HTMLCharacterDataTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLCharacterDataTests.m; sourceTree = ""; }; 62E7CAAE19CDFFB500465A83 /* HTMLTokenizerCharacters.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HTMLTokenizerCharacters.h; path = include/HTMLTokenizerCharacters.h; sourceTree = ""; }; - 62EC7AE51AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitMutationAlgorithmsTests.m; sourceTree = ""; }; + 62EC0A841E158BD80007786B /* HTMLRangeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLRangeTests.m; sourceTree = ""; }; + 62EC7AE51AEEAC6F0015D3BE /* HTMLMutationAlgorithmsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLMutationAlgorithmsTests.m; sourceTree = ""; }; 62ECBF4D1C0B6C7600AF847B /* HTMLKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = HTMLKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 62ECBF561C0B6C7600AF847B /* HTMLKitTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "HTMLKitTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 62F31FDB19E9DCCF007F0657 /* HTMLTokenizerEntities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTMLTokenizerEntities.h; path = include/HTMLTokenizerEntities.h; sourceTree = ""; }; @@ -670,6 +732,7 @@ 62F501631BC9576B0069F47B /* CSSNthExpressionParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSSNthExpressionParser.h; path = include/CSSNthExpressionParser.h; sourceTree = ""; }; 62F501641BC9576B0069F47B /* CSSNthExpressionParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSNthExpressionParser.m; sourceTree = ""; }; 62F6586F1BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSNThExpressionSelectorTests.m; sourceTree = ""; }; + 62F7E5A01DEB63F900DEF09B /* HTMLRange+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "HTMLRange+Private.h"; path = "include/HTMLRange+Private.h"; sourceTree = ""; }; 62F873E919E088C90062683C /* HTMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTMLParser.h; path = include/HTMLParser.h; sourceTree = ""; }; 62F873EA19E088C90062683C /* HTMLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLParser.m; sourceTree = ""; }; 62FA04E11BCC360D009ABF98 /* CSSCombinatorSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSSCombinatorSelector.h; path = include/CSSCombinatorSelector.h; sourceTree = ""; }; @@ -776,6 +839,8 @@ 623406E51ADB05AD004677A3 /* HTMLDocumentFragment.m */, 6279F87219E1808D00F12EE5 /* HTMLElement.h */, 6279F87319E1808D00F12EE5 /* HTMLElement.m */, + 624C77781DE9D05E00DD6DFA /* HTMLCharacterData.h */, + 624C77791DE9D05E00DD6DFA /* HTMLCharacterData.m */, 623857991A9E8934003A45D9 /* HTMLComment.h */, 6238579A1A9E8934003A45D9 /* HTMLComment.m */, 62362A3A1A9FA70400301989 /* HTMLText.h */, @@ -788,10 +853,10 @@ 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */, 624717BA1B22009200B38302 /* HTMLTreeWalker.h */, 624717BB1B22009200B38302 /* HTMLTreeWalker.m */, - 624717161B22333200C11912 /* HTMLNodeTraversal.h */, - 624717171B22333200C11912 /* HTMLNodeTraversal.m */, 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */, 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */, + 62D91C211DE218A500BEFADE /* HTMLRange.h */, + 62D91C221DE218A500BEFADE /* HTMLRange.m */, 6238C9831AB8D6330006512E /* HTMLKitDOMExceptions.h */, 6238C9841AB8D6330006512E /* HTMLKitDOMExceptions.m */, 628B7CE61A080E1000602C87 /* HTMLNamespaces.h */, @@ -815,10 +880,12 @@ 624B9FB71AE072CB00646C4C /* DOM */ = { isa = PBXGroup; children = ( - 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */, + 623916C61AC7209E0066B4FE /* HTMLNodeIteratorTests.m */, 6247171B1B2240B800C11912 /* HTMLTreeWalkerTests.m */, - 624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */, - 62EC7AE51AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m */, + 624FC3791AE591D80015DDF9 /* HTMLNodesTests.m */, + 62EC7AE51AEEAC6F0015D3BE /* HTMLMutationAlgorithmsTests.m */, + 62EC0A841E158BD80007786B /* HTMLRangeTests.m */, + 62E0BA961E25456700E4D193 /* HTMLCharacterDataTests.m */, ); name = DOM; sourceTree = ""; @@ -826,7 +893,7 @@ 624B9FB81AE072D500646C4C /* Categories */ = { isa = PBXGroup; children = ( - 624B9FB11AE0313300646C4C /* HTMLKitStringCategoryTests.m */, + 624B9FB11AE0313300646C4C /* HTMLStringCategoryTests.m */, ); name = Categories; sourceTree = ""; @@ -834,8 +901,8 @@ 624E1A2D1B1D1C8A00E66AAC /* Structures */ = { isa = PBXGroup; children = ( - 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */, - 62567F551C0CC3AE0025D458 /* HTMLKitDOMTokenListTests.m */, + 624B9FB41AE0391400646C4C /* HTMLOrderedDictionaryTests.m */, + 62567F551C0CC3AE0025D458 /* HTMLDOMTokenListTests.m */, ); name = Structures; sourceTree = ""; @@ -912,6 +979,10 @@ 625A14CA19C7829400AD0C32 /* Tests */ = { isa = PBXGroup; children = ( + 625EE4551CBAA41D00F2CC8E /* HTMLKitTestObserver.h */, + 625EE4561CBAA41D00F2CC8E /* HTMLKitTestObserver.m */, + 625EE4591CBB171300F2CC8E /* HTMLKitTestUtil.h */, + 625EE45A1CBB171300F2CC8E /* HTMLKitTestUtil.m */, 6236738C1AC0CD2400FF89B3 /* Tokenizer */, 623975581AC362A5007E26F1 /* Tree Construction */, 624B9FB71AE072CB00646C4C /* DOM */, @@ -919,10 +990,6 @@ 624E1A2D1B1D1C8A00E66AAC /* Structures */, 62FF516C1C0A430A009BFDFE /* Selectors */, 625A14CB19C7829400AD0C32 /* Supporting Files */, - 625EE4551CBAA41D00F2CC8E /* HTMLKitTestObserver.h */, - 625EE4561CBAA41D00F2CC8E /* HTMLKitTestObserver.m */, - 625EE4591CBB171300F2CC8E /* HTMLKitTestUtil.h */, - 625EE45A1CBB171300F2CC8E /* HTMLKitTestUtil.m */, ); path = Tests; sourceTree = ""; @@ -941,7 +1008,16 @@ 625D0F071C2717E400D7BEB0 /* Private */ = { isa = PBXGroup; children = ( + 621CC48F1DEA704000D1992A /* HTMLDocument+Private.h */, 625D0F011C2717DE00D7BEB0 /* HTMLNode+Private.h */, + 626679CD1DEA3DAF004353E7 /* HTMLCharacterData+Private.h */, + 62F7E5A01DEB63F900DEF09B /* HTMLRange+Private.h */, + 621CC4A31DEA721A00D1992A /* HTMLParser+Private.h */, + 62056C4E1E2AD9FB009A4EE0 /* HTMLNodeIterator+Private.h */, + 624717161B22333200C11912 /* HTMLNodeTraversal.h */, + 624717171B22333200C11912 /* HTMLNodeTraversal.m */, + 626BE1E01DF3819500C49514 /* HTMLDOMUtils.h */, + 626BE1E11DF3819500C49514 /* HTMLDOMUtils.m */, ); name = Private; sourceTree = ""; @@ -1081,6 +1157,7 @@ 62ECBEE21C0B69FD00AF847B /* HTMLInputStreamReader.h in Headers */, 62ECBEE31C0B69FD00AF847B /* HTMLTokenizer.h in Headers */, 62ECBEE41C0B69FD00AF847B /* HTMLTokenizerStates.h in Headers */, + 62D91C231DE218A500BEFADE /* HTMLRange.h in Headers */, 62ECBEE51C0B69FD00AF847B /* HTMLTokenizerCharacters.h in Headers */, 62ECBEE61C0B69FD00AF847B /* HTMLTokenizerEntities.h in Headers */, 625D0F031C2717DE00D7BEB0 /* HTMLNode+Private.h in Headers */, @@ -1090,19 +1167,24 @@ 62ECBEE91C0B69FD00AF847B /* HTMLCharacterToken.h in Headers */, 62ECBEEA1C0B69FD00AF847B /* HTMLCommentToken.h in Headers */, 62ECBEEB1C0B69FD00AF847B /* HTMLDOCTYPEToken.h in Headers */, + 626BE1E21DF3819500C49514 /* HTMLDOMUtils.h in Headers */, 62ECBEEC1C0B69FD00AF847B /* HTMLParseErrorToken.h in Headers */, 62ECBEED1C0B69FD00AF847B /* HTMLTagToken.h in Headers */, 62ECBEEE1C0B69FD00AF847B /* HTMLEOFToken.h in Headers */, 62567F501C0CB5750025D458 /* HTMLDOMTokenList.h in Headers */, + 626679CF1DEA3DAF004353E7 /* HTMLCharacterData+Private.h in Headers */, + 621CC4911DEA704000D1992A /* HTMLDocument+Private.h in Headers */, 62ECBEEF1C0B69FD00AF847B /* HTMLParser.h in Headers */, 62ECBEF01C0B69FD00AF847B /* HTMLStackOfOpenElements.h in Headers */, 62ECBEF11C0B69FD00AF847B /* HTMLListOfActiveFormattingElements.h in Headers */, 62ECBEF21C0B69FD00AF847B /* HTMLElementTypes.h in Headers */, 62ECBEF31C0B69FD00AF847B /* HTMLElementAdjustment.h in Headers */, 62ECBEF41C0B69FD00AF847B /* HTMLParserInsertionModes.h in Headers */, + 624C777A1DE9D05E00DD6DFA /* HTMLCharacterData.h in Headers */, 62ECBEF51C0B69FD00AF847B /* HTMLMarker.h in Headers */, 62ECBEF61C0B69FE00AF847B /* HTMLDOM.h in Headers */, 62ECBEF71C0B69FE00AF847B /* HTMLNode.h in Headers */, + 621CC4A51DEA721A00D1992A /* HTMLParser+Private.h in Headers */, 62ECBEF81C0B69FE00AF847B /* HTMLDocument.h in Headers */, 62ECBEF91C0B69FE00AF847B /* HTMLDocumentType.h in Headers */, 62ECBEFA1C0B69FE00AF847B /* HTMLDocumentFragment.h in Headers */, @@ -1112,6 +1194,7 @@ 62ECBEFF1C0B69FE00AF847B /* HTMLNodeIterator.h in Headers */, 62ECBF001C0B69FF00AF847B /* HTMLTreeWalker.h in Headers */, 62ECBF011C0B69FF00AF847B /* HTMLNodeTraversal.h in Headers */, + 62F7E5A21DEB63F900DEF09B /* HTMLRange+Private.h in Headers */, 62ECBF021C0B69FF00AF847B /* HTMLNodeFilter.h in Headers */, 62ECBF031C0B69FF00AF847B /* HTMLKitDOMExceptions.h in Headers */, 62ECBF041C0B69FF00AF847B /* HTMLNamespaces.h in Headers */, @@ -1129,6 +1212,7 @@ 62ECBF101C0B6A0100AF847B /* CSSSelectorBlock.h in Headers */, 62ECBF111C0B6A0200AF847B /* CSSTypeSelector.h in Headers */, 62ECBF121C0B6A0200AF847B /* CSSAttributeSelector.h in Headers */, + 62056C4F1E2AD9FB009A4EE0 /* HTMLNodeIterator+Private.h in Headers */, 62ECBF131C0B6A0200AF847B /* CSSPseudoClassSelector.h in Headers */, 62ECBF141C0B6A0200AF847B /* CSSNthExpressionSelector.h in Headers */, 62ECBF151C0B6A0200AF847B /* CSSPseudoFunctionSelector.h in Headers */, @@ -1141,11 +1225,13 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 62F7E5A51DEB63F900DEF09B /* HTMLRange+Private.h in Headers */, 62857CBE1D398999008DC254 /* NSString+HTMLKit.h in Headers */, 62857CAC1D398973008DC254 /* HTMLKitDOMExceptions.h in Headers */, 62857CD61D3989C2008DC254 /* CSSSelectorParser.h in Headers */, 62857CAA1D398973008DC254 /* HTMLTreeWalker.h in Headers */, 62857CA31D398973008DC254 /* HTMLDocumentType.h in Headers */, + 62D91C261DE218A500BEFADE /* HTMLRange.h in Headers */, 62857C6B1D3988EE008DC254 /* HTMLKit.h in Headers */, 62857C701D398907008DC254 /* HTMLTokenizerCharacters.h in Headers */, 62857C6F1D398907008DC254 /* HTMLTokenizerStates.h in Headers */, @@ -1155,19 +1241,24 @@ 62857C841D398917008DC254 /* HTMLParser.h in Headers */, 62857CDB1D3989CE008DC254 /* CSSAttributeSelector.h in Headers */, 62857C791D398907008DC254 /* HTMLEOFToken.h in Headers */, + 626BE1E51DF3819500C49514 /* HTMLDOMUtils.h in Headers */, 62857CC41D3989A9008DC254 /* CSSSelectors.h in Headers */, 62857C731D398907008DC254 /* HTMLToken.h in Headers */, 62857CC51D3989AB008DC254 /* CSSStructuralPseudoSelectors.h in Headers */, 62857CDE1D3989CE008DC254 /* CSSPseudoFunctionSelector.h in Headers */, + 626679D21DEA3DAF004353E7 /* HTMLCharacterData+Private.h in Headers */, + 621CC4941DEA704000D1992A /* HTMLDocument+Private.h in Headers */, 62857CAB1D398973008DC254 /* HTMLNodeFilter.h in Headers */, 62857CA61D398973008DC254 /* HTMLText.h in Headers */, 62857C751D398907008DC254 /* HTMLCommentToken.h in Headers */, 62857C891D398927008DC254 /* HTMLParserInsertionModes.h in Headers */, 62857CA81D398973008DC254 /* HTMLDOMTokenList.h in Headers */, 62857C851D398927008DC254 /* HTMLStackOfOpenElements.h in Headers */, + 624C777D1DE9D05E00DD6DFA /* HTMLCharacterData.h in Headers */, 62857CDF1D3989CE008DC254 /* CSSCombinatorSelector.h in Headers */, 62857CA91D398973008DC254 /* HTMLNodeIterator.h in Headers */, 62857CAE1D398973008DC254 /* HTMLQuirksMode.h in Headers */, + 621CC4A81DEA721A00D1992A /* HTMLParser+Private.h in Headers */, 62857CAF1D398977008DC254 /* HTMLNodeTraversal.h in Headers */, 62857C771D398907008DC254 /* HTMLParseErrorToken.h in Headers */, 62857CDC1D3989CE008DC254 /* CSSPseudoClassSelector.h in Headers */, @@ -1194,6 +1285,7 @@ 62857CD41D3989BF008DC254 /* CSSCodePoints.h in Headers */, 62857C871D398927008DC254 /* HTMLElementTypes.h in Headers */, 62857C711D398907008DC254 /* HTMLTokenizerEntities.h in Headers */, + 62056C521E2AD9FB009A4EE0 /* HTMLNodeIterator+Private.h in Headers */, 62857C6C1D3988EE008DC254 /* HTMLKitErrorDomain.h in Headers */, 62857CD51D3989BF008DC254 /* CSSInputStream.h in Headers */, 62857C741D398907008DC254 /* HTMLCharacterToken.h in Headers */, @@ -1206,11 +1298,13 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 62F7E5A41DEB63F900DEF09B /* HTMLRange+Private.h in Headers */, 62857D831D39A46D008DC254 /* CSSStructuralPseudoSelectors.h in Headers */, 62857D7D1D39A457008DC254 /* HTMLNodeTraversal.h in Headers */, 62857D841D39A472008DC254 /* CSSSelectorParser.h in Headers */, 62857D7F1D39A461008DC254 /* NSString+HTMLKit.h in Headers */, 62857D7E1D39A45B008DC254 /* HTMLNode+Private.h in Headers */, + 62D91C251DE218A500BEFADE /* HTMLRange.h in Headers */, 62857D701D39A44E008DC254 /* HTMLDocumentType.h in Headers */, 62857D861D39A476008DC254 /* CSSCodePoints.h in Headers */, 62857D8A1D39A47F008DC254 /* CSSTypeSelector.h in Headers */, @@ -1220,19 +1314,24 @@ 62857D4E1D39A40A008DC254 /* HTMLTokenizerCharacters.h in Headers */, 62857D4D1D39A40A008DC254 /* HTMLTokenizerStates.h in Headers */, 62857D501D39A411008DC254 /* HTMLTokens.h in Headers */, + 626BE1E41DF3819500C49514 /* HTMLDOMUtils.h in Headers */, 62857D7B1D39A452008DC254 /* HTMLNamespaces.h in Headers */, 62857D581D39A416008DC254 /* HTMLParser.h in Headers */, 62857D571D39A411008DC254 /* HTMLEOFToken.h in Headers */, 62857D8E1D39A47F008DC254 /* CSSPseudoFunctionSelector.h in Headers */, + 626679D11DEA3DAF004353E7 /* HTMLCharacterData+Private.h in Headers */, + 621CC4931DEA704000D1992A /* HTMLDocument+Private.h in Headers */, 62857D511D39A411008DC254 /* HTMLToken.h in Headers */, 62857D531D39A411008DC254 /* HTMLCommentToken.h in Headers */, 62857D741D39A44E008DC254 /* HTMLText.h in Headers */, 62857D5D1D39A41D008DC254 /* HTMLParserInsertionModes.h in Headers */, 62857D591D39A41D008DC254 /* HTMLStackOfOpenElements.h in Headers */, 62857D781D39A44E008DC254 /* HTMLTreeWalker.h in Headers */, + 624C777C1DE9D05E00DD6DFA /* HTMLCharacterData.h in Headers */, 62857D551D39A411008DC254 /* HTMLParseErrorToken.h in Headers */, 62857D541D39A411008DC254 /* HTMLDOCTYPEToken.h in Headers */, 62857D871D39A476008DC254 /* CSSInputStream.h in Headers */, + 621CC4A71DEA721A00D1992A /* HTMLParser+Private.h in Headers */, 62857D8F1D39A47F008DC254 /* CSSCombinatorSelector.h in Headers */, 62857D5C1D39A41D008DC254 /* HTMLElementAdjustment.h in Headers */, 62857D891D39A47F008DC254 /* CSSSelectorBlock.h in Headers */, @@ -1259,6 +1358,7 @@ 62857D881D39A47F008DC254 /* CSSSelector.h in Headers */, 62857D4F1D39A40A008DC254 /* HTMLTokenizerEntities.h in Headers */, 62857D901D39A47F008DC254 /* CSSCompoundSelector.h in Headers */, + 62056C511E2AD9FB009A4EE0 /* HTMLNodeIterator+Private.h in Headers */, 62857D4A1D39A3F4008DC254 /* HTMLKitErrorDomain.h in Headers */, 62857D7C1D39A452008DC254 /* HTMLQuirksMode.h in Headers */, 62857D791D39A44E008DC254 /* HTMLNodeFilter.h in Headers */, @@ -1276,6 +1376,7 @@ 62ECBF9F1C0B6D3900AF847B /* HTMLParser.h in Headers */, 62ECBFA61C0B6D3A00AF847B /* HTMLDOM.h in Headers */, 62ECBFA71C0B6D3A00AF847B /* HTMLNode.h in Headers */, + 62D91C241DE218A500BEFADE /* HTMLRange.h in Headers */, 62ECBFA81C0B6D3A00AF847B /* HTMLDocument.h in Headers */, 62ECBFA91C0B6D3A00AF847B /* HTMLDocumentType.h in Headers */, 62ECBFAA1C0B6D3A00AF847B /* HTMLDocumentFragment.h in Headers */, @@ -1289,15 +1390,19 @@ 62567F511C0CB5750025D458 /* HTMLDOMTokenList.h in Headers */, 62ECBFB31C0B6D3C00AF847B /* HTMLKitDOMExceptions.h in Headers */, 62ECBFB41C0B6D3D00AF847B /* HTMLNamespaces.h in Headers */, + 626679D01DEA3DAF004353E7 /* HTMLCharacterData+Private.h in Headers */, + 621CC4921DEA704000D1992A /* HTMLDocument+Private.h in Headers */, 62ECBFB51C0B6D3D00AF847B /* HTMLQuirksMode.h in Headers */, 62ECBFB61C0B6D3D00AF847B /* NSString+HTMLKit.h in Headers */, 62ECBFB71C0B6D3D00AF847B /* NSCharacterSet+HTMLKit.h in Headers */, 62ECBFB81C0B6D3E00AF847B /* HTMLOrderedDictionary.h in Headers */, 62ECBFB91C0B6D3E00AF847B /* CSSSelectors.h in Headers */, 62ECBFBA1C0B6D3E00AF847B /* CSSStructuralPseudoSelectors.h in Headers */, + 624C777B1DE9D05E00DD6DFA /* HTMLCharacterData.h in Headers */, 62ECBFBD1C0B6D3F00AF847B /* CSSSelectorParser.h in Headers */, 62ECBFBE1C0B6D4000AF847B /* CSSNthExpressionParser.h in Headers */, 62ECBFBF1C0B6D4000AF847B /* CSSSelector.h in Headers */, + 621CC4A61DEA721A00D1992A /* HTMLParser+Private.h in Headers */, 62ECBFC01C0B6D4000AF847B /* CSSSelectorBlock.h in Headers */, 62ECBFC11C0B6D4100AF847B /* CSSTypeSelector.h in Headers */, 62ECBFC21C0B6D4100AF847B /* CSSAttributeSelector.h in Headers */, @@ -1305,6 +1410,8 @@ 62ECBFC41C0B6D4200AF847B /* CSSNthExpressionSelector.h in Headers */, 62ECBFC51C0B6D4200AF847B /* CSSPseudoFunctionSelector.h in Headers */, 62ECBFC61C0B6D4300AF847B /* CSSCombinatorSelector.h in Headers */, + 62F7E5A31DEB63F900DEF09B /* HTMLRange+Private.h in Headers */, + 626BE1E31DF3819500C49514 /* HTMLDOMUtils.h in Headers */, 625D0F041C2717DE00D7BEB0 /* HTMLNode+Private.h in Headers */, 62ECBFC71C0B6D4300AF847B /* CSSCompoundSelector.h in Headers */, 62ECBFB11C0B6D3C00AF847B /* HTMLNodeTraversal.h in Headers */, @@ -1324,6 +1431,7 @@ 62ECBFA01C0B6D3900AF847B /* HTMLStackOfOpenElements.h in Headers */, 62ECBFA11C0B6D3900AF847B /* HTMLListOfActiveFormattingElements.h in Headers */, 62ECBFA21C0B6D3900AF847B /* HTMLElementTypes.h in Headers */, + 62056C501E2AD9FB009A4EE0 /* HTMLNodeIterator+Private.h in Headers */, 62ECBFA31C0B6D3900AF847B /* HTMLElementAdjustment.h in Headers */, 62ECBFA41C0B6D3900AF847B /* HTMLParserInsertionModes.h in Headers */, 62ECBFA51C0B6D3900AF847B /* HTMLMarker.h in Headers */, @@ -1591,6 +1699,7 @@ 62ECBF211C0B6B7900AF847B /* HTMLCommentToken.m in Sources */, 62ECBF221C0B6B7900AF847B /* HTMLDOCTYPEToken.m in Sources */, 62ECBF231C0B6B7900AF847B /* HTMLParseErrorToken.m in Sources */, + 62D91C271DE218A500BEFADE /* HTMLRange.m in Sources */, 62ECBF241C0B6B7900AF847B /* HTMLTagToken.m in Sources */, 62ECBF251C0B6B7900AF847B /* HTMLEOFToken.m in Sources */, 62ECBF261C0B6B7900AF847B /* HTMLParser.m in Sources */, @@ -1628,6 +1737,8 @@ 62ECBF461C0B6B7900AF847B /* CSSCombinatorSelector.m in Sources */, 62ECBF471C0B6B7A00AF847B /* CSSCompoundSelector.m in Sources */, 62567F521C0CB5750025D458 /* HTMLDOMTokenList.m in Sources */, + 624C777E1DE9D05E00DD6DFA /* HTMLCharacterData.m in Sources */, + 626BE1E61DF3819600C49514 /* HTMLDOMUtils.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1635,6 +1746,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 62EC0A851E158BD80007786B /* HTMLRangeTests.m in Sources */, + 62E0BA971E25456700E4D193 /* HTMLCharacterDataTests.m in Sources */, 6216ACFD1C28DCC80074CAB4 /* CSSExtensionSelectorsParsingTests.m in Sources */, 62132E5A1C01F83200084175 /* CSSSelectorTest.m in Sources */, 6239755B1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */, @@ -1642,18 +1755,18 @@ 623CAF9E1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */, 625EE4571CBAA41D00F2CC8E /* HTMLKitTestObserver.m in Sources */, 6247171D1B2240B800C11912 /* HTMLTreeWalkerTests.m in Sources */, - 62EC7AE71AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m in Sources */, + 62EC7AE71AEEAC6F0015D3BE /* HTMLMutationAlgorithmsTests.m in Sources */, 624AB31B1B050A4D00F3830D /* CSSAttributeSelectorTests.m in Sources */, - 624B9FB61AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */, + 624B9FB61AE0391400646C4C /* HTMLOrderedDictionaryTests.m in Sources */, 624AB3181B0508AE00F3830D /* CSSTypeSelectorTests.m in Sources */, 6236738F1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */, - 62567F561C0CC3AE0025D458 /* HTMLKitDOMTokenListTests.m in Sources */, + 62567F561C0CC3AE0025D458 /* HTMLDOMTokenListTests.m in Sources */, 624AC90219FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */, - 624B9FB31AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */, - 623916C81AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m in Sources */, + 624B9FB31AE0313300646C4C /* HTMLStringCategoryTests.m in Sources */, + 623916C81AC7209E0066B4FE /* HTMLNodeIteratorTests.m in Sources */, 62D8345A19FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */, 6239755F1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */, - 624FC37B1AE591D80015DDF9 /* HTMLKitNodesTests.m in Sources */, + 624FC37B1AE591D80015DDF9 /* HTMLNodesTests.m in Sources */, 621FBE5B1BDAD68700BC9555 /* CSSSelectorParserTests.m in Sources */, 621FBE5E1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m in Sources */, 625EE45B1CBB171300F2CC8E /* HTMLKitTestUtil.m in Sources */, @@ -1673,6 +1786,7 @@ 62857CCB1D3989B8008DC254 /* CSSSelector.m in Sources */, 62857C8B1D39892D008DC254 /* HTMLParser.m in Sources */, 62857CC61D3989AF008DC254 /* CSSSelectors.m in Sources */, + 62D91C2A1DE218A500BEFADE /* HTMLRange.m in Sources */, 62857CD31D3989B8008DC254 /* CSSCompoundSelector.m in Sources */, 62857C7B1D398912008DC254 /* HTMLTokenizer.m in Sources */, 62857CB21D398992008DC254 /* HTMLDocument.m in Sources */, @@ -1710,6 +1824,8 @@ 62857CD01D3989B8008DC254 /* CSSNthExpressionSelector.m in Sources */, 62857CB81D398992008DC254 /* HTMLDOMTokenList.m in Sources */, 62857C831D398912008DC254 /* HTMLEOFToken.m in Sources */, + 624C77811DE9D05E00DD6DFA /* HTMLCharacterData.m in Sources */, + 626BE1E91DF3819600C49514 /* HTMLDOMUtils.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1725,6 +1841,7 @@ 62857D251D39A334008DC254 /* CSSSelector.m in Sources */, 62857D021D39A324008DC254 /* HTMLTokenizer.m in Sources */, 62857D201D39A334008DC254 /* CSSSelectors.m in Sources */, + 62D91C291DE218A500BEFADE /* HTMLRange.m in Sources */, 62857D2D1D39A334008DC254 /* CSSCompoundSelector.m in Sources */, 62857D061D39A324008DC254 /* HTMLCommentToken.m in Sources */, 62857D111D39A32A008DC254 /* HTMLDocumentType.m in Sources */, @@ -1762,6 +1879,8 @@ 62857D2A1D39A334008DC254 /* CSSNthExpressionSelector.m in Sources */, 62857D171D39A32A008DC254 /* HTMLDOMTokenList.m in Sources */, 62857D0A1D39A324008DC254 /* HTMLEOFToken.m in Sources */, + 624C77801DE9D05E00DD6DFA /* HTMLCharacterData.m in Sources */, + 626BE1E81DF3819600C49514 /* HTMLDOMUtils.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1769,25 +1888,27 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 62EC0A871E158BD80007786B /* HTMLRangeTests.m in Sources */, + 62E0BA991E25456700E4D193 /* HTMLCharacterDataTests.m in Sources */, 62857D3B1D39A345008DC254 /* CSSSelectorTest.m in Sources */, 62857D421D39A345008DC254 /* CSSCombinatorSelectorTests.m in Sources */, 62857D3C1D39A345008DC254 /* CSSSelectorParserTests.m in Sources */, 62857D301D39A339008DC254 /* HTMLKitTokenizerPerformance.m in Sources */, 62857D321D39A33C008DC254 /* HTMLKitTreeConstructionTests.m in Sources */, 62857D431D39A34B008DC254 /* HTMLKitTestObserver.m in Sources */, - 62857D371D39A33F008DC254 /* HTMLKitMutationAlgorithmsTests.m in Sources */, + 62857D371D39A33F008DC254 /* HTMLMutationAlgorithmsTests.m in Sources */, 62857D3E1D39A345008DC254 /* CSSNthExpressionsParserTests.m in Sources */, 62857D3F1D39A345008DC254 /* CSSTypeSelectorTests.m in Sources */, - 62857D361D39A33F008DC254 /* HTMLKitNodesTests.m in Sources */, - 62857D3A1D39A342008DC254 /* HTMLKitDOMTokenListTests.m in Sources */, + 62857D361D39A33F008DC254 /* HTMLNodesTests.m in Sources */, + 62857D3A1D39A342008DC254 /* HTMLDOMTokenListTests.m in Sources */, 62857D3D1D39A345008DC254 /* CSSExtensionSelectorsParsingTests.m in Sources */, 62857D401D39A345008DC254 /* CSSAttributeSelectorTests.m in Sources */, 62857D311D39A33C008DC254 /* HTML5LibTreeConstructionTest.m in Sources */, - 62857D391D39A342008DC254 /* HTMLKitOrderedDictionaryTests.m in Sources */, + 62857D391D39A342008DC254 /* HTMLOrderedDictionaryTests.m in Sources */, 62857D411D39A345008DC254 /* CSSNThExpressionSelectorTests.m in Sources */, - 62857D381D39A342008DC254 /* HTMLKitStringCategoryTests.m in Sources */, + 62857D381D39A342008DC254 /* HTMLStringCategoryTests.m in Sources */, 62857D331D39A33C008DC254 /* HTMLKitParserPerformance.m in Sources */, - 62857D341D39A33F008DC254 /* HTMLKitNodeIteratorTests.m in Sources */, + 62857D341D39A33F008DC254 /* HTMLNodeIteratorTests.m in Sources */, 62857D2E1D39A339008DC254 /* HTML5LibTokenizerTest.m in Sources */, 62857D351D39A33F008DC254 /* HTMLTreeWalkerTests.m in Sources */, 62857D441D39A34D008DC254 /* HTMLKitTestUtil.m in Sources */, @@ -1807,6 +1928,7 @@ 62ECBF691C0B6D2A00AF847B /* HTMLCommentToken.m in Sources */, 62ECBF6A1C0B6D2A00AF847B /* HTMLDOCTYPEToken.m in Sources */, 62ECBF6B1C0B6D2A00AF847B /* HTMLParseErrorToken.m in Sources */, + 62D91C281DE218A500BEFADE /* HTMLRange.m in Sources */, 62ECBF6C1C0B6D2A00AF847B /* HTMLTagToken.m in Sources */, 62ECBF6D1C0B6D2A00AF847B /* HTMLEOFToken.m in Sources */, 62ECBF6E1C0B6D2A00AF847B /* HTMLParser.m in Sources */, @@ -1844,6 +1966,8 @@ 62ECBF8E1C0B6D2A00AF847B /* CSSCombinatorSelector.m in Sources */, 62ECBF8F1C0B6D2A00AF847B /* CSSCompoundSelector.m in Sources */, 62567F531C0CB5750025D458 /* HTMLDOMTokenList.m in Sources */, + 624C777F1DE9D05E00DD6DFA /* HTMLCharacterData.m in Sources */, + 626BE1E71DF3819600C49514 /* HTMLDOMUtils.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1851,6 +1975,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 62EC0A861E158BD80007786B /* HTMLRangeTests.m in Sources */, + 62E0BA981E25456700E4D193 /* HTMLCharacterDataTests.m in Sources */, 6216ACFE1C28DCC80074CAB4 /* CSSExtensionSelectorsParsingTests.m in Sources */, 62ECBFCA1C0B6E2E00AF847B /* HTML5LibTokenizerTest.m in Sources */, 62ECBFCB1C0B6E2E00AF847B /* HTMLKitTokenizerTests.m in Sources */, @@ -1859,13 +1985,13 @@ 625EE4581CBAA41D00F2CC8E /* HTMLKitTestObserver.m in Sources */, 62ECBFCE1C0B6E2E00AF847B /* HTMLKitTreeConstructionTests.m in Sources */, 62ECBFCF1C0B6E2E00AF847B /* HTMLKitParserPerformance.m in Sources */, - 62ECBFD01C0B6E2E00AF847B /* HTMLKitNodeIteratorTests.m in Sources */, + 62ECBFD01C0B6E2E00AF847B /* HTMLNodeIteratorTests.m in Sources */, 62ECBFD11C0B6E2E00AF847B /* HTMLTreeWalkerTests.m in Sources */, - 62ECBFD21C0B6E2E00AF847B /* HTMLKitNodesTests.m in Sources */, - 62ECBFD31C0B6E2E00AF847B /* HTMLKitMutationAlgorithmsTests.m in Sources */, - 62567F571C0CC3AE0025D458 /* HTMLKitDOMTokenListTests.m in Sources */, - 62ECBFD41C0B6E2E00AF847B /* HTMLKitStringCategoryTests.m in Sources */, - 62ECBFD51C0B6E2E00AF847B /* HTMLKitOrderedDictionaryTests.m in Sources */, + 62ECBFD21C0B6E2E00AF847B /* HTMLNodesTests.m in Sources */, + 62ECBFD31C0B6E2E00AF847B /* HTMLMutationAlgorithmsTests.m in Sources */, + 62567F571C0CC3AE0025D458 /* HTMLDOMTokenListTests.m in Sources */, + 62ECBFD41C0B6E2E00AF847B /* HTMLStringCategoryTests.m in Sources */, + 62ECBFD51C0B6E2E00AF847B /* HTMLOrderedDictionaryTests.m in Sources */, 62ECBFD61C0B6E2E00AF847B /* CSSSelectorTest.m in Sources */, 62ECBFD71C0B6E2E00AF847B /* CSSSelectorParserTests.m in Sources */, 62ECBFD81C0B6E2E00AF847B /* CSSNthExpressionsParserTests.m in Sources */, diff --git a/HTMLKit.xcodeproj/xcshareddata/xcschemes/HTMLKit-macOS.xcscheme b/HTMLKit.xcodeproj/xcshareddata/xcschemes/HTMLKit-macOS.xcscheme index a7178bb..e115eb8 100644 --- a/HTMLKit.xcodeproj/xcshareddata/xcschemes/HTMLKit-macOS.xcscheme +++ b/HTMLKit.xcodeproj/xcshareddata/xcschemes/HTMLKit-macOS.xcscheme @@ -26,7 +26,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> diff --git a/HTMLKit.xcodeproj/xcshareddata/xcschemes/HTMLKit-tvOS.xcscheme b/HTMLKit.xcodeproj/xcshareddata/xcschemes/HTMLKit-tvOS.xcscheme index 883c1b3..fc392c9 100644 --- a/HTMLKit.xcodeproj/xcshareddata/xcschemes/HTMLKit-tvOS.xcscheme +++ b/HTMLKit.xcodeproj/xcshareddata/xcschemes/HTMLKit-tvOS.xcscheme @@ -26,7 +26,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> diff --git a/HTMLKit.xcodeproj/xcshareddata/xcschemes/HTMLKit-watchOS.xcscheme b/HTMLKit.xcodeproj/xcshareddata/xcschemes/HTMLKit-watchOS.xcscheme index 2c81d4b..1fe0add 100644 --- a/HTMLKit.xcodeproj/xcshareddata/xcschemes/HTMLKit-watchOS.xcscheme +++ b/HTMLKit.xcodeproj/xcshareddata/xcschemes/HTMLKit-watchOS.xcscheme @@ -26,7 +26,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> diff --git a/README.md b/README.md index 004b57a..fdf1e55 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,19 @@ An Objective-C framework for your everyday HTML needs. -[![Build Status](https://img.shields.io/travis/iabudiab/HTMLKit/master.svg?style=flat)](https://travis-ci.org/iabudiab/HTMLKit) +[![Build Status](https://img.shields.io/travis/iabudiab/HTMLKit/develop.svg?style=flat)](https://travis-ci.org/iabudiab/HTMLKit) [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![CocoaPods Compatible](https://img.shields.io/cocoapods/v/HTMLKit.svg?style=flat)](https://cocoapods.org/pods/HTMLKit) [![CocoaDocs](https://img.shields.io/cocoapods/metrics/doc-percent/HTMLKit.svg?style=flat)](http://cocoadocs.org/docsets/HTMLKit) [![Platform](https://img.shields.io/cocoapods/p/HTMLKit.svg?style=flat)](http://cocoadocs.org/docsets/HTMLKit) [![License MIT](https://img.shields.io/badge/license-MIT-4481C7.svg?style=flat)](https://opensource.org/licenses/MIT) +- [Quick Overview](#overview) +- [Installation](#installation) +- [Parsing](#parsing) +- [The DOM](#the-dom) +- [CSS3 Selectors](#css3-selectors) + # Quick Overview HTMLKit is a [WHATWG specification](https://html.spec.whatwg.org/multipage/)-compliant framework for parsing and serializing HTML documents and document fragments for iOS and OSX. HTMLKit parses real-world HTML the same way modern web browsers would. @@ -33,7 +39,7 @@ Check out the playground! # Installation -### Carthage +## Carthage [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. @@ -56,7 +62,7 @@ Then run the following command to build the framework and drag the built `HTMLKi $ carthage update ``` -### CocoaPods +## CocoaPods [CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. @@ -72,7 +78,7 @@ To add `HTMLKit` as a dependency into your project using CocoaPods just add the use_frameworks! target 'MyTarget' do - pod 'HTMLKit', '~> 0.9' + pod 'HTMLKit', '~> 1.1' end ``` @@ -82,14 +88,14 @@ Then, run the following command: $ pod install ``` -### Swift Package Manager +## Swift Package Manager [Swift Package Manager](https://github.com/apple/swift-package-manager) is the package manager for the Swift programming language. Add `HTMLKit` to your `Package.swift` dependecies: ```swift -.Package(url: "https://github.com/iabudiab/HTMLKit", majorVersion: 0, minor: 9) +.Package(url: "https://github.com/iabudiab/HTMLKit", majorVersion: 1) ``` Then run: @@ -98,7 +104,7 @@ Then run: $ swift build ``` -### Manually +## Manually 1- Add `HTMLKit` as git submodule @@ -110,9 +116,9 @@ $ git submodule add https://github.com/iabudiab/HTMLKit.git 3- In the General panel of your target add `HTMLKit.framework` under the `Embedded Binaries` -# Features +# Parsing -# Parsing Documents +## Parsing Documents Given some HTML content, you can parse it either via the `HTMLParser` or instatiate a `HTMLDocument` directly: @@ -127,7 +133,7 @@ HTMLDocument *document = [parser parseDocument]; HTMLDocument *document = [HTMLDocument documentWithString:htmlString]; ``` -# Parsing Fragments +## Parsing Fragments You can also prase HTML content as a document fragment with a specified context element: @@ -150,7 +156,7 @@ nodes = [parser parseFragmentWithContextElement:bodyContext]; # The DOM -Here are some of the things you can do: +The DOM tree can be manipulated in several ways, here are just a few: * Create new elements and assign attributes @@ -174,7 +180,7 @@ NSArray *nodes = @[ [body appendNodes:nodes]; ``` -* Enumerate child elements and perform DOM manipulation +* Enumerate child elements and perform DOM editing ```objective-c [body enumerateChildElementsUsingBlock:^(HTMLElement *element, NSUInteger idx, BOOL *stop) { @@ -194,6 +200,12 @@ NSArray *nodes = @[ [body.lastChild removeFromParentNode]; ``` +* Manipulate the HTML directly + +```objective-c +greenDiv.innerHTML = @"
  • item 1
  • item 2"; +``` + * Navigate to child and sibling nodes ```objective-c @@ -201,12 +213,6 @@ HTMLNode *firstChild = body.firstChild; HTMLNode *greenDiv = firstChild.nextSibling; ``` -* Manipulate the HTML directly - -```objective-c -greenDiv.innerHTML = @"
    • item 1
    • item 2"; -``` - * Iterate the DOM tree with custom filters ```objective-c @@ -222,6 +228,17 @@ for (HTMLElement *element in [body nodeIteratorWithShowOptions:HTMLNodeFilterSho } ``` +* Create and manipulate DOM Ranges + +```objective-c +HTMLDocument *document = [HTMLDocument documentWithString:@"

      HTMLKit

      Hello there!

      "]; +HTMLRange *range = [[HTMLRange alloc] initWithDocument:document]; + +HTMLNode *paragraph = [document querySelector:@"#foo"]; +[range selectNode:paragraph]; +[range extractContents]; +``` + # CSS3 Selectors All CSS3 Selectors are supported except for the pseudo-elements (`::first-line`, `::first-letter`, ...etc.). You can use them the way you always have: diff --git a/Sources/HTMLCharacterData.m b/Sources/HTMLCharacterData.m new file mode 100644 index 0000000..68a7e19 --- /dev/null +++ b/Sources/HTMLCharacterData.m @@ -0,0 +1,103 @@ +// +// HTMLCharacterData.m +// HTMLKit +// +// Created by Iska on 26/11/16. +// Copyright © 2016 BrainCookie. All rights reserved. +// + +#import "HTMLCharacterData.h" +#import "HTMLNode+Private.h" +#import "HTMLDocument+Private.h" +#import "HTMLKitDOMExceptions.h" + +@interface HTMLCharacterData () +{ + NSMutableString *_data; +} +@end + +@implementation HTMLCharacterData +@synthesize data = _data; + +- (instancetype)initWithName:(NSString *)name type:(HTMLNodeType)type data:(NSString *)data +{ + self = [super initWithName:name type:type]; + if (self) { + _data = [[NSMutableString alloc] initWithString:data ?: @""]; + } + return self; +} + +- (NSString *)textContent +{ + return [_data copy]; +} + +- (void)setTextContent:(NSString *)textContent +{ + [self setData:textContent]; +} + +- (NSUInteger)length +{ + return _data.length; +} + +#pragma mark - Data + +NS_INLINE void CheckValidOffset(HTMLCharacterData *node, NSUInteger offset, NSString *cmd) +{ + if (offset > node.length) { + [NSException raise:HTMLKitIndexSizeError + format:@"%@: Index Size Error, invalid index %lu for character data node %@.", + cmd, (unsigned long)offset, node]; + } +} + +- (void)setData:(NSString *)data +{ + [self replaceDataInRange:NSMakeRange(0, self.length) withData:data]; +} + +- (void)appendData:(NSString *)data +{ + [self replaceDataInRange:NSMakeRange(self.length, 0) withData:data]; +} + +- (void)insertData:(NSString *)data atOffset:(NSUInteger)offset +{ + [self replaceDataInRange:NSMakeRange(offset, 0) withData:data]; +} + +- (void)deleteDataInRange:(NSRange)range +{ + [self replaceDataInRange:range withData:@""]; +} + +- (void)replaceDataInRange:(NSRange)range withData:(NSString *)data +{ + CheckValidOffset(self, range.location, NSStringFromSelector(_cmd)); + + range.length = MIN(range.length, self.length - range.location); + + [_data replaceCharactersInRange:range withString:data]; + [self.ownerDocument didRemoveCharacterDataInNode:self atOffset:range.location withLength:range.length]; + [self.ownerDocument didAddCharacterDataToNode:self atOffset:range.location withLength:data.length]; +} + +- (NSString *)substringDataWithRange:(NSRange)range +{ + return [_data substringWithRange:range]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone +{ + HTMLCharacterData *copy = [super copyWithZone:zone]; + copy->_data = [_data mutableCopy]; + return copy; +} + +@end diff --git a/Sources/HTMLComment.m b/Sources/HTMLComment.m index c126999..afebf89 100644 --- a/Sources/HTMLComment.m +++ b/Sources/HTMLComment.m @@ -7,7 +7,7 @@ // #import "HTMLComment.h" -#import "HTMLNode+Private.h" +#import "HTMLCharacterData+Private.h" @implementation HTMLComment @@ -18,30 +18,7 @@ - (instancetype)initWithData:(NSString *)data { - self = [super initWithName:@"#comment" type:HTMLNodeComment]; - if (self) { - self.data = data ?: @""; - } - return self; -} - -- (NSString *)textContent -{ - return self.data; -} - -- (void)setTextContent:(NSString *)textContent -{ - self.data = textContent ?: @""; -} - -#pragma mark - NSCopying - -- (id)copyWithZone:(NSZone *)zone -{ - HTMLComment *copy = [super copyWithZone:zone]; - copy.data = self.data; - return copy; + return [super initWithName:@"#comment" type:HTMLNodeComment data:data]; } #pragma mark - Serialization diff --git a/Sources/HTMLDOMUtils.m b/Sources/HTMLDOMUtils.m new file mode 100644 index 0000000..efc2d82 --- /dev/null +++ b/Sources/HTMLDOMUtils.m @@ -0,0 +1,32 @@ +// +// HTMLDOMUtils.m +// HTMLKit +// +// Created by Iska on 03/12/16. +// Copyright © 2016 BrainCookie. All rights reserved. +// + +#import "HTMLDOMUtils.h" +#import "HTMLNode.h" + +extern HTMLNode * GetCommonAncestorContainer(HTMLNode *nodeA, HTMLNode *nodeB) +{ + for (HTMLNode *parentA = nodeA; parentA != nil; parentA = parentA.parentNode) { + for (HTMLNode *parentB = nodeB; parentB != nil; parentB = parentB.parentNode) { + if (parentA == parentB) { + return parentA; + } + } + } + + return nil; +} + +extern NSArray * GetAncestorNodes(HTMLNode *node) +{ + NSMutableArray *ancestors = [NSMutableArray array]; + for (HTMLNode *it = node; it; it = it.parentNode) { + [ancestors addObject:it]; + } + return ancestors; +} diff --git a/Sources/HTMLDocument.m b/Sources/HTMLDocument.m index 120cfac..0d34a78 100644 --- a/Sources/HTMLDocument.m +++ b/Sources/HTMLDocument.m @@ -9,19 +9,19 @@ #import "HTMLDocument.h" #import "HTMLParser.h" #import "HTMLNodeIterator.h" +#import "HTMLRange.h" +#import "HTMLCharacterData.h" +#import "HTMLText.h" #import "HTMLKitDOMExceptions.h" #import "HTMLNode+Private.h" - -@interface HTMLNodeIterator (Private) -- (void)runRemovingStepsForNode:(HTMLNode *)oldNode - withOldParent:(HTMLNode *)oldParent - andOldPreviousSibling:(HTMLNode *)oldPreviousSibling; -@end +#import "HTMLNodeIterator+Private.h" +#import "HTMLRange+Private.h" @interface HTMLDocument () { HTMLDocument *_inertTemplateDocument; NSMutableArray *_nodeIterators; + NSMutableArray *_ranges; } @property (nonatomic, assign) HTMLDocumentReadyState readyState; @end @@ -42,6 +42,7 @@ if (self) { _readyState = HTMLDocumentLoading; _nodeIterators = [NSMutableArray new]; + _ranges = [NSMutableArray new]; } return self; } @@ -143,14 +144,62 @@ [_nodeIterators removeObject:iterator]; } +#pragma mark - Ranges + +- (void)attachRange:(HTMLRange *)range +{ + [_ranges addObject:range]; +} + +- (void)detachRange:(HTMLRange *)range +{ + [_ranges removeObject:range]; +} + +- (void)didRemoveCharacterDataInNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length +{ + for (HTMLRange *range in _ranges) { + [range didRemoveCharacterDataInNode:node atOffset:offset withLength:length]; + } +} + +- (void)didAddCharacterDataToNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length +{ + for (HTMLRange *range in _ranges) { + [range didAddCharacterDataToNode:node atOffset:offset withLength:length]; + } +} + +- (void)didInsertNewTextNode:(HTMLText *)newNode intoParent:(HTMLNode *)parent afterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset +{ + for (HTMLRange *range in _ranges) { + [range didInsertNewTextNode:newNode intoParent:parent afterSplittingTextNode:node atOffset:offset]; + } +} + +- (void)clampRangesAfterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset +{ + for (HTMLRange *range in _ranges) { + [range clampRangesAfterSplittingTextNode:node atOffset:offset]; + } +} + +#pragma mark - Mutation Callback + - (void)runRemovingStepsForNode:(HTMLNode *)oldNode withOldParent:(HTMLNode *)oldParent andOldPreviousSibling:(HTMLNode *)oldPreviousSibling { + for (HTMLRange *range in _ranges) { + [range runRemovingStepsForNode:oldNode + withOldParent:oldParent + andOldPreviousSibling:oldPreviousSibling]; + } + for (HTMLNodeIterator *iterator in _nodeIterators) { [iterator runRemovingStepsForNode:oldNode - withOldParent:oldParent - andOldPreviousSibling:oldPreviousSibling]; + withOldParent:oldParent + andOldPreviousSibling:oldPreviousSibling]; } } diff --git a/Sources/HTMLDocumentType.m b/Sources/HTMLDocumentType.m index 8a4636c..0bff20c 100644 --- a/Sources/HTMLDocumentType.m +++ b/Sources/HTMLDocumentType.m @@ -129,13 +129,18 @@ NS_INLINE BOOL nilOrEqual(id first, id second) { return HTMLQuirksModeNoQuirks; } +- (NSUInteger)length +{ + return 0; +} + #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { HTMLDocumentType *copy = [super copyWithZone:zone]; - copy->_publicIdentifier = self.publicIdentifier; - copy->_systemIdentifier = self.systemIdentifier; + copy->_publicIdentifier = [_publicIdentifier copy]; + copy->_systemIdentifier = [_systemIdentifier copy]; return copy; } diff --git a/Sources/HTMLKit-Info.plist b/Sources/HTMLKit-Info.plist index ecef4c7..7fde877 100644 --- a/Sources/HTMLKit-Info.plist +++ b/Sources/HTMLKit-Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0.0 + 1.1.0 CFBundleSignature ???? CFBundleVersion diff --git a/Sources/HTMLKitDOMExceptions.m b/Sources/HTMLKitDOMExceptions.m index 7a0d349..d568734 100644 --- a/Sources/HTMLKitDOMExceptions.m +++ b/Sources/HTMLKitDOMExceptions.m @@ -14,3 +14,7 @@ NSString * const HTMLKitNotFoundError = @"NotFoundError"; NSString * const HTMLKitNotSupportedError = @"NotSupportedError"; NSString * const HTMLKitSyntaxError = @"SyntaxError"; NSString * const HTMLKitInvalidCharacterError = @"InvalidCharacterError"; +NSString * const HTMLKitInvalidNodeTypeError = @"InvalidNodeTypeError"; +NSString * const HTMLKitIndexSizeError = @"IndexSizeError"; +NSString * const HTMLKitWrongDocumentError = @"WrongDocumentError"; +NSString * const HTMLKitInvalidStateError = @"InvalidStateError"; diff --git a/Sources/HTMLNode.m b/Sources/HTMLNode.m index 83291d7..e1c74c3 100644 --- a/Sources/HTMLNode.m +++ b/Sources/HTMLNode.m @@ -16,12 +16,8 @@ #import "HTMLKitDOMExceptions.h" #import "HTMLNodeFilter.h" #import "CSSSelector.h" - -@interface HTMLDocument (Private) -- (void)runRemovingStepsForNode:(HTMLNode *)oldNode - withOldParent:(HTMLNode *)oldParent - andOldPreviousSibling:(HTMLNode *)oldPreviousSibling; -@end +#import "HTMLDocument+Private.h" +#import "HTMLDOMUtils.h" @interface HTMLNode () { @@ -62,6 +58,11 @@ [self.childNodes.array makeObjectsPerformSelector:@selector(setOwnerDocument:) withObject:ownerDocument]; } +- (HTMLNode *)rootNode +{ + return _parentNode == nil ? self : _parentNode.rootNode; +} + - (void)setParentNode:(HTMLNode *)parentNode { _parentNode = parentNode; @@ -118,11 +119,21 @@ return node.asElement; } +- (NSUInteger)index +{ + return [self.parentNode indexOfChildNode:self]; +} + - (NSString *)textContent { return nil; } +- (NSUInteger)length +{ + return self.childNodesCount; +} + #pragma mark - Cast - (HTMLElement *)asElement @@ -155,6 +166,11 @@ return self.childNodes.count; } +- (BOOL)isEmpty +{ + return self.length == 0; +} + - (HTMLNode *)childNodeAtIndex:(NSUInteger)index { return [self.childNodes objectAtIndex:index]; @@ -328,41 +344,43 @@ 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; + if (self.ownerDocument != otherNode.ownerDocument) { + return (HTMLDocumentPositionDisconnected | HTMLDocumentPositionImplementationSpecific | + self.hash < otherNode.hash ? HTMLDocumentPositionPreceding : HTMLDocumentPositionFollowing); } - for (NSUInteger i = MIN(ancestors1.count - 1, ancestors2.count - 1); i; --i) { - HTMLNode *child1 = ancestors1[i]; - HTMLNode *child2 = ancestors2[i]; + NSArray *ancestors1 = GetAncestorNodes(self); + NSArray *ancestors2 = GetAncestorNodes(otherNode); + + if (ancestors1.lastObject != ancestors2.lastObject) { + return (HTMLDocumentPositionDisconnected | HTMLDocumentPositionImplementationSpecific | + self.hash < otherNode.hash ? HTMLDocumentPositionPreceding : HTMLDocumentPositionFollowing); + } + + NSUInteger index1 = ancestors1.count; + NSUInteger index2 = ancestors2.count; + + for (NSUInteger i = MIN(index1, index2); i; --i) { + index1 -= 1; + index2 -= 1; + + HTMLNode *child1 = ancestors1[index1]; + HTMLNode *child2 = ancestors2[index2]; if (child1 != child2) { for (HTMLNode *sibling = child1.nextSibling; sibling; sibling = sibling.nextSibling) { if (sibling == child2) { - return HTMLDocumentPositionFollowing; + return HTMLDocumentPositionPreceding; } } - return HTMLDocumentPositionPreceding; + return HTMLDocumentPositionFollowing; } } if (ancestors1.count < ancestors2.count) { - return HTMLDocumentPositionContainedBy | HTMLDocumentPositionFollowing; - } else { return HTMLDocumentPositionContains | HTMLDocumentPositionPreceding; + } else { + return HTMLDocumentPositionContainedBy | HTMLDocumentPositionFollowing; } } @@ -650,6 +668,21 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin #endif +#pragma mark - Clone + +- (instancetype)cloneNodeDeep:(BOOL)deep +{ + HTMLNode *copy = [self copy]; + + if (deep) { + for (HTMLNode *child in self.childNodes) { + [copy appendNode:[child cloneNodeDeep:YES]]; + } + } + + return copy; +} + #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone diff --git a/Sources/HTMLNodeIterator.m b/Sources/HTMLNodeIterator.m index 5e71387..7c0937e 100644 --- a/Sources/HTMLNodeIterator.m +++ b/Sources/HTMLNodeIterator.m @@ -11,6 +11,7 @@ #import "HTMLNode.h" #import "HTMLNodeFilter.h" #import "HTMLNodeTraversal.h" +#import "HTMLDocument+Private.h" typedef NS_ENUM(short, TraverseDirection) { @@ -18,11 +19,6 @@ typedef NS_ENUM(short, TraverseDirection) TraverseDirectionPrevious }; -@interface HTMLDocument (Private) -- (void)attachNodeIterator:(HTMLNodeIterator *)iterator; -- (void)detachNodeIterator:(HTMLNodeIterator *)iterator; -@end - @interface HTMLNodeIterator () { HTMLNode *_root; diff --git a/Sources/HTMLParser.m b/Sources/HTMLParser.m index 230ad53..aff3f97 100644 --- a/Sources/HTMLParser.m +++ b/Sources/HTMLParser.m @@ -18,14 +18,7 @@ #import "HTMLMarker.h" #import "NSString+HTMLKit.h" #import "CSSSelectors.h" - -@interface HTMLTokenizer (Private) -@property (nonatomic, weak) HTMLParser *parser; -@end - -@interface HTMLDocument (Private) -@property (nonatomic, assign) HTMLDocumentReadyState readyState; -@end +#import "HTMLDocument+Private.h" @interface HTMLParser () { @@ -457,9 +450,11 @@ beforeChildNode:&child]; if (adjustedInsertionLocation.nodeType != HTMLNodeDocument) { if (child != nil && child.previousSibling.nodeType == HTMLNodeText) { - [(HTMLText *)child.previousSibling appendString:data]; + HTMLText *textNode = (HTMLText *)child.previousSibling; + [textNode appendData:data]; } else if (adjustedInsertionLocation.lastChild.nodeType == HTMLNodeText) { - [(HTMLText *)adjustedInsertionLocation.lastChild appendString:data]; + HTMLText *textNode = (HTMLText *)adjustedInsertionLocation.lastChild; + [textNode appendData:data]; } else { HTMLText *text = [[HTMLText alloc] initWithData:data]; [adjustedInsertionLocation insertNode:text beforeChildNode:child]; diff --git a/Sources/HTMLRange.m b/Sources/HTMLRange.m new file mode 100644 index 0000000..e2b9454 --- /dev/null +++ b/Sources/HTMLRange.m @@ -0,0 +1,753 @@ +// +// HTMLRange.m +// HTMLKit +// +// Created by Iska on 20/11/16. +// Copyright © 2016 BrainCookie. All rights reserved. +// + +#import "HTMLRange.h" +#import "HTMLDocument.h" +#import "HTMLKitDOMExceptions.h" +#import "HTMLDocument+Private.h" +#import "HTMLDOMUtils.h" +#import "HTMLNodeTraversal.h" + +@interface HTMLRange () +{ + HTMLDocument *_ownerDocument; +} +@end + +@implementation HTMLRange + +#pragma mark - Lifecycle + +- (instancetype)initWithDowcument:(HTMLDocument *)document +{ + return [self initWithDowcument:document startContainer:document startOffset:0 endContainer:document endOffset:0]; +} + +- (instancetype)initWithDowcument:(HTMLDocument *)document + startContainer:(HTMLNode *)startContainer startOffset:(NSUInteger)startOffset + endContainer:(HTMLNode *)endContainer endOffset:(NSUInteger)endOffset +{ + self = [super init]; + if (self) { + _ownerDocument = document; + [_ownerDocument attachRange:self]; + [self setStartNode:startContainer startOffset:startOffset]; + [self setEndNode:endContainer endOffset:endOffset]; + } + return self; +} + +- (void)dealloc +{ + [_ownerDocument detachRange:self]; +} + +#pragma mark - Properties + +- (BOOL)isCollapsed +{ + return _startContainer == _endContainer && _startOffset == _endOffset; +} + +- (HTMLNode *)commonAncestorContainer +{ + return GetCommonAncestorContainer(_startContainer, _endContainer); +} + +- (HTMLNode *)rootNode +{ + return _startContainer.rootNode; +} + +#pragma mark - Boundaries + +NS_INLINE void CheckValidBoundaryNode(HTMLDocument *document, HTMLNode *node, NSString *cmd) +{ + if (node.ownerDocument != document) { + [NSException raise:HTMLKitWrongDocumentError + format:@"%@: Invalid Node Error, %@ is not in the same document.", + cmd, node]; + } +} + +NS_INLINE void CheckValidBoundaryNodeType(HTMLNode *node, NSString *cmd) +{ + if (node == nil || node.nodeType == HTMLNodeDocumentType) { + [NSException raise:HTMLKitInvalidNodeTypeError + format:@"%@: Invalid Node Type Error, %@ is not a valid range boundary node.", + cmd, node]; + } +} + +NS_INLINE void CheckValidBoundaryOffset(HTMLNode *node, NSUInteger offset, NSString *cmd) +{ + if (node.length < offset) { + [NSException raise:HTMLKitIndexSizeError + format:@"%@: Index Size Error, invalid index %lu for range boundary node %@.", + cmd, (unsigned long)offset, node]; + } +} + +NS_INLINE void CheckValidDocument(HTMLRange *lhs, HTMLRange *rhs, NSString *cmd) +{ + if (lhs.rootNode != rhs.rootNode) { + [NSException raise:HTMLKitWrongDocumentError + format:@"%@: Wrong Document Error, ranges %@ and %@ are not in the same document.", + cmd, lhs, rhs]; + } +} + +NS_INLINE NSComparisonResult CompareBoundaries(HTMLNode *startNode, NSUInteger startOffset, HTMLNode *endNode, NSUInteger endOffset) +{ + if (startNode == endNode) { + if (startOffset == endOffset) { + return NSOrderedSame; + } else if (startOffset < endOffset) { + return NSOrderedAscending; + } else { + return NSOrderedDescending; + } + } + + HTMLDocumentPosition position = [startNode compareDocumentPositionWithNode:endNode]; + if ((position & HTMLDocumentPositionFollowing) == HTMLDocumentPositionFollowing) { + if (CompareBoundaries(endNode, endOffset, startNode, startOffset) == NSOrderedAscending) { + return NSOrderedDescending; + } else { + return NSOrderedAscending; + } + } + + if ((position & HTMLDocumentPositionContains) == HTMLDocumentPositionContains) { + HTMLNode *child = endNode; + while (child.parentNode != startNode) { + child = child.parentNode; + } + if (child.index < startOffset) { + return NSOrderedDescending; + } + } + + return NSOrderedAscending; +} + +- (void)setStartNode:(HTMLNode *)node startOffset:(NSUInteger)offset +{ + CheckValidBoundaryNode(_ownerDocument, node, NSStringFromSelector(_cmd)); + + CheckValidBoundaryNodeType(node, NSStringFromSelector(_cmd)); + + CheckValidBoundaryOffset(node, offset, NSStringFromSelector(_cmd)); + + if (self.rootNode != node.rootNode || + CompareBoundaries(node, offset, _endContainer, _endOffset) == NSOrderedDescending) { + _endContainer = node; + _endOffset = offset; + } + + _startContainer = node; + _startOffset = offset; +} + +- (void)setEndNode:(HTMLNode *)node endOffset:(NSUInteger)offset +{ + CheckValidBoundaryNode(_ownerDocument, node, NSStringFromSelector(_cmd)); + + CheckValidBoundaryNodeType(node, NSStringFromSelector(_cmd)); + + CheckValidBoundaryOffset(node, offset, NSStringFromSelector(_cmd)); + + if (self.rootNode != node.rootNode || + CompareBoundaries(node, offset, _startContainer, _startOffset) == NSOrderedAscending) { + _startContainer = node; + _startOffset = offset; + } + + _endContainer = node; + _endOffset = offset; +} + +- (void)setStartBeforeNode:(HTMLNode *)node +{ + HTMLNode *parent = node.parentNode; + [self setStartNode:parent startOffset:node.index]; +} + +- (void)setStartAfterNode:(HTMLNode *)node +{ + HTMLNode *parent = node.parentNode; + [self setStartNode:parent startOffset:node.index + 1]; +} + +- (void)setEndBeforeNode:(HTMLNode *)node +{ + HTMLNode *parent = node.parentNode; + [self setEndNode:parent endOffset:node.index]; +} + +- (void)setEndAfterNode:(HTMLNode *)node +{ + HTMLNode *parent = node.parentNode; + [self setEndNode:parent endOffset:node.index + 1]; +} + +- (void)collapseToStart +{ + [self setEndNode:_startContainer endOffset:_startOffset]; +} + +- (void)collapseToEnd +{ + [self setStartNode:_endContainer startOffset:_endOffset]; +} + +- (void)selectNode:(HTMLNode *)node +{ + HTMLNode *parent = node.parentNode; + [self setStartNode:parent startOffset:node.index]; + [self setEndNode:parent endOffset:node.index + 1]; +} + +- (void)selectNodeContents:(HTMLNode *)node +{ + [self setStartNode:node startOffset:0]; + [self setEndNode:node endOffset:node.length]; +} + +- (NSComparisonResult)compareBoundaryPoints:(HTMLRangeComparisonMethod)method sourceRange:(HTMLRange *)sourceRange +{ + CheckValidDocument(self, sourceRange, NSStringFromSelector(_cmd)); + + switch (method) { + case HTMLRangeComparisonMethodStartToStart: + return CompareBoundaries(_startContainer, _startOffset, sourceRange.startContainer, sourceRange.startOffset); + case HTMLRangeComparisonMethodStartToEnd: + return CompareBoundaries(_endContainer, _endOffset, sourceRange.startContainer, sourceRange.startOffset); + case HTMLRangeComparisonMethodEndToEnd: + return CompareBoundaries(_endContainer, _endOffset, sourceRange.endContainer, sourceRange.endOffset); + case HTMLRangeComparisonMethodEndToStart: + return CompareBoundaries(_startContainer, _startOffset, sourceRange.endContainer, sourceRange.endOffset); + } +} + +#pragma mark - Containment + +- (NSComparisonResult)comparePoint:(HTMLNode *)node offset:(NSUInteger)offset +{ + CheckValidBoundaryNode(_ownerDocument, node, NSStringFromSelector(_cmd)); + + CheckValidBoundaryNodeType(node, NSStringFromSelector(_cmd)); + + CheckValidBoundaryOffset(node, offset, NSStringFromSelector(_cmd)); + + if (CompareBoundaries(node, offset, _startContainer, _startOffset) == NSOrderedAscending) { + return NSOrderedAscending; + } + + if (CompareBoundaries(node, offset, _endContainer, _endOffset) == NSOrderedDescending) { + return NSOrderedDescending; + } + + return NSOrderedSame; +} + +- (BOOL)containsPoint:(HTMLNode *)node offset:(NSUInteger)offset +{ + return [self comparePoint:node offset:offset] == NSOrderedSame; +} + +- (BOOL)intersectsNode:(HTMLNode *)node +{ + if (self.rootNode != node.rootNode) { + return NO; + } + + HTMLNode *parent = node.parentNode; + if (parent == nil) { + return YES; + } + + NSUInteger offset = node.index; + if (CompareBoundaries(parent, offset, _endContainer, _endOffset) == NSOrderedAscending && + CompareBoundaries(parent, offset + 1, _startContainer, _startOffset) == NSOrderedDescending) { + return YES; + } + + return NO; +} + +- (BOOL)containsNode:(HTMLNode *)node +{ + return CompareBoundaries(_startContainer, _startOffset, node, 0) == NSOrderedAscending && + CompareBoundaries(_endContainer, _endOffset, node, node.length) == NSOrderedDescending; +} + +- (BOOL)partiallyContainsNode:(HTMLNode *)node +{ + return [GetAncestorNodes(_startContainer) containsObject:node] || [GetAncestorNodes(_endContainer) containsObject:node]; +} + +- (NSArray *)containedNodes:(HTMLNode *)commonAncestor +{ + NSMutableArray *containedNodes = [NSMutableArray array]; + [commonAncestor.childNodes enumerateObjectsUsingBlock:^(HTMLNode * _Nonnull node, NSUInteger idx, BOOL * _Nonnull stop) { + if (node.nodeType == HTMLNodeDocumentType) { + [NSException raise:HTMLKitHierarchyRequestError format:@"Hierarchy Request Error, encountered a DOCTYPE contained in range: %@", self]; + } + if ([self containsNode:node]) { + [containedNodes addObject:node]; + } + }]; + + return containedNodes; +} + +#pragma mark - Update Callbacks + +- (void)didRemoveCharacterDataInNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length +{ + if (_startContainer == node && _startOffset > offset) { + if (_startOffset <= offset + length) { + _startOffset = offset; + } else { + _startOffset = _startOffset - length; + } + } + if (_endContainer == node && _endOffset > offset) { + if (_endOffset <= offset + length) { + _endOffset = offset; + } else { + _endOffset = _endOffset - length; + } + } +} + +- (void)didAddCharacterDataToNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length +{ + if (_startContainer == node && _startOffset > offset) { + _startOffset = _startOffset + length; + } + if (_endContainer == node && _endOffset > offset) { + _endOffset = _endOffset + length; + } +} + +- (void)didInsertNewTextNode:(HTMLText *)newNode intoParent:(HTMLNode *)parent afterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset +{ + if (_startContainer == node && _startOffset > offset) { + _startContainer = newNode; + _startOffset -= offset; + } + + if (_endContainer == node && _endOffset > offset) { + _endContainer = newNode; + _endOffset -= offset; + } + + if (_startContainer == parent && _startOffset == node.index + 1) { + _startOffset += 1; + } + + if (_endContainer == parent && _endOffset == node.index + 1) { + _endOffset += 1; + } +} + +- (void)clampRangesAfterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset +{ + if (_startContainer == node && _startOffset > offset) { + _startOffset = offset; + } + + if (_endContainer == node && _endOffset > offset) { + _endOffset = offset; + } +} + +- (void)runRemovingStepsForNode:(HTMLNode *)oldNode withOldParent:(HTMLNode *)oldParent andOldPreviousSibling:(HTMLNode *)oldPreviousSibling +{ + NSUInteger oldIndex = oldPreviousSibling.index + 1; + + if ([_startContainer containsNode:oldNode]) { + [self setStartNode:oldNode startOffset:oldIndex]; + } + + if ([_endContainer containsNode:oldNode]) { + [self setEndNode:oldNode endOffset:oldIndex]; + } + + if (_startContainer == oldParent && _startOffset > oldIndex) { + _startOffset -= 1; + } + + if (_endContainer == oldParent && _endOffset > oldIndex) { + _endOffset -= 1; + } +} + +#pragma mark - Mutations + +NS_INLINE HTMLNode * GetHighestPartiallyContainedChild(HTMLNode *node, HTMLNode *root) +{ + if (node == root) { + return nil; + } + + while (node.parentNode != root) { + node = node.parentNode; + } + return node; +} + +NS_INLINE HTMLCharacterData * CloneCharachterData(HTMLNode *node, NSUInteger start, NSUInteger length, BOOL delete) +{ + HTMLCharacterData *clone = (HTMLCharacterData *)[node copy]; + NSRange range = NSMakeRange(start, length); + [clone setData:[clone.data substringWithRange:range]]; + + if (delete) { + [(HTMLCharacterData *)node deleteDataInRange:range]; + } + + return clone; +} + +- (void)deleteContents +{ + if (self.isCollapsed) { + return; + } + + if (_startContainer == _endContainer && [_startContainer isKindOfClass:[HTMLCharacterData class]]) { + [(HTMLCharacterData *)_startContainer deleteDataInRange:NSMakeRange(_startOffset, _endOffset - _startOffset)]; + return; + } + + HTMLNode *commonAncestor = self.commonAncestorContainer; + + NSMutableArray *containedNodes = [NSMutableArray array]; + + HTMLNode *node = FollowingNode(_startContainer, commonAncestor); + while (node) { + if ([self containsNode:node]) { + [containedNodes addObject:node]; + node = FollowingNodeSkippingChildren(node, commonAncestor); + } else { + node = FollowingNode(node, commonAncestor); + } + } + + HTMLNode *newNode = _startContainer; + NSUInteger newOffset = _startOffset; + + if (![_startContainer containsNode:_endContainer]) { + HTMLNode *referenceNode = _startContainer; + while (referenceNode.parentNode) { + if ([referenceNode.parentNode containsNode:_endContainer]) { + newNode = referenceNode.parentNode; + newOffset = referenceNode.index + 1; + break; + } + referenceNode = referenceNode.parentNode; + } + } + + if ([_startContainer isKindOfClass:[HTMLCharacterData class]]) { + [(HTMLCharacterData *)_startContainer deleteDataInRange:NSMakeRange(_startOffset, _startContainer.length - _startOffset)]; + } + + for (HTMLNode *node in containedNodes) { + [node removeFromParentNode]; + } + + if ([_endContainer isKindOfClass:[HTMLCharacterData class]]) { + [(HTMLCharacterData *)_endContainer deleteDataInRange:NSMakeRange(0, _endOffset)]; + } + + [self setStartNode:newNode startOffset:newOffset]; + [self setEndNode:newNode endOffset:newOffset]; +} + +- (HTMLDocumentFragment *)extractContents +{ + HTMLDocumentFragment *fragment = [[HTMLDocumentFragment alloc] initWithDocument:_ownerDocument]; + + // Nothing todo + if (self.isCollapsed) { + return fragment; + } + + // Same character data container, handle that and return + if (_startContainer == _endContainer && [_startContainer isKindOfClass:[HTMLCharacterData class]]) { + HTMLCharacterData *clone = CloneCharachterData(_startContainer, _startOffset, _endOffset - _startOffset, YES); + [fragment appendNode:clone]; + + return fragment; + } + + HTMLNode *commonAncestor = self.commonAncestorContainer; + HTMLNode *firstPartiallyContainedChild = GetHighestPartiallyContainedChild(_startContainer, commonAncestor); + HTMLNode *lastPartiallyContainedChild = GetHighestPartiallyContainedChild(_endContainer, commonAncestor); + NSArray *containedNodes = [self containedNodes:commonAncestor]; + + HTMLNode *newNode = _startContainer; + NSUInteger newOffset = _startOffset; + + if (![_startContainer containsNode:_endContainer]) { + HTMLNode *referenceNode = _startContainer; + while (referenceNode.parentNode) { + if ([referenceNode.parentNode containsNode:_endContainer]) { + newNode = referenceNode.parentNode; + newOffset = referenceNode.index + 1; + break; + } + referenceNode = referenceNode.parentNode; + } + } + + if ([firstPartiallyContainedChild isKindOfClass:[HTMLCharacterData class]]) { + HTMLCharacterData *clone = CloneCharachterData(_startContainer, _startOffset, _startContainer.length - _startOffset, YES); + [fragment appendNode:clone]; + } else if (firstPartiallyContainedChild != nil) { + HTMLNode *clone = [firstPartiallyContainedChild copy]; + [fragment appendNode:clone]; + + HTMLRange *subRange = [[HTMLRange alloc] initWithDowcument:_ownerDocument + startContainer:_startContainer + startOffset:_startOffset + endContainer:firstPartiallyContainedChild + endOffset:firstPartiallyContainedChild.length]; + HTMLDocumentFragment *subFragment = [subRange extractContents]; + [clone appendNode:subFragment]; + } + + for (HTMLNode *node in containedNodes) { + [fragment appendNode:node]; + } + + if ([lastPartiallyContainedChild isKindOfClass:[HTMLCharacterData class]]) { + HTMLCharacterData *clone = CloneCharachterData(_endContainer, 0, _endOffset, YES); + [fragment appendNode:clone]; + } else if (lastPartiallyContainedChild != nil) { + HTMLNode *clone = [lastPartiallyContainedChild copy]; + [fragment appendNode:clone]; + + HTMLRange *subRange = [[HTMLRange alloc] initWithDowcument:_ownerDocument + startContainer:lastPartiallyContainedChild + startOffset:0 + endContainer:_endContainer + endOffset:_endOffset]; + HTMLDocumentFragment *subFragment = [subRange extractContents]; + [clone appendNode:subFragment]; + } + + [self setStartNode:newNode startOffset:newOffset]; + [self setEndNode:newNode endOffset:newOffset]; + + return fragment; +} + +- (HTMLDocumentFragment *)cloneContents +{ + HTMLDocumentFragment *fragment = [[HTMLDocumentFragment alloc] initWithDocument:_ownerDocument]; + + // Nothing todo + if (self.isCollapsed) { + return fragment; + } + + // Same character data container, handle that and return + if (_startContainer == _endContainer && [_startContainer isKindOfClass:[HTMLCharacterData class]]) { + HTMLCharacterData *clone = CloneCharachterData(_startContainer, _startOffset, _endOffset - _startOffset, NO); + [fragment appendNode:clone]; + return fragment; + } + + HTMLNode *commonAncestor = self.commonAncestorContainer; + HTMLNode *firstPartiallyContainedChild = GetHighestPartiallyContainedChild(_startContainer, commonAncestor); + HTMLNode *lastPartiallyContainedChild = GetHighestPartiallyContainedChild(_endContainer, commonAncestor); + NSArray *containedNodes = [self containedNodes:commonAncestor]; + + if ([firstPartiallyContainedChild isKindOfClass:[HTMLCharacterData class]]) { + HTMLCharacterData *clone = CloneCharachterData(_startContainer, _startOffset, _startContainer.length - _startOffset, NO); + [fragment appendNode:clone]; + } else if (firstPartiallyContainedChild != nil) { + HTMLNode *clone = [firstPartiallyContainedChild copy]; + [fragment appendNode:clone]; + + HTMLRange *subRange = [[HTMLRange alloc] initWithDowcument:_ownerDocument + startContainer:_startContainer + startOffset:_startOffset + endContainer:firstPartiallyContainedChild + endOffset:firstPartiallyContainedChild.length]; + HTMLDocumentFragment *subFragment = [subRange cloneContents]; + [clone appendNode:subFragment]; + } + + for (HTMLNode *node in containedNodes) { + HTMLNode *clone = [node cloneNodeDeep:YES]; + [fragment appendNode:clone]; + } + + if ([lastPartiallyContainedChild isKindOfClass:[HTMLCharacterData class]]) { + HTMLCharacterData *clone = CloneCharachterData(_endContainer, 0, _endOffset, NO); + [fragment appendNode:clone]; + } else if (lastPartiallyContainedChild != nil) { + HTMLNode *clone = [lastPartiallyContainedChild copy]; + [fragment appendNode:clone]; + + HTMLRange *subRange = [[HTMLRange alloc] initWithDowcument:_ownerDocument + startContainer:lastPartiallyContainedChild + startOffset:0 + endContainer:_endContainer + endOffset:_endOffset]; + HTMLDocumentFragment *subFragment = [subRange cloneContents]; + [clone appendNode:subFragment]; + } + + return fragment; +} + +#pragma mark - Insertion & Surround + +NS_INLINE void CheckValidInsertionNode(HTMLNode *startContainer, HTMLNode *node, NSString *cmd) +{ + if (startContainer == node || startContainer.nodeType == HTMLNodeComment || + (startContainer.nodeType == HTMLNodeText && startContainer.parentNode == nil)) { + [NSException raise:HTMLKitHierarchyRequestError + format:@"%@: Hierarchy Request Error, cannot insert node into range: %@", cmd, node]; + } +} + +- (void)insertNode:(HTMLNode *)node +{ + CheckValidInsertionNode(_startContainer, node, NSStringFromSelector(_cmd)); + + HTMLNode *referenceNode = nil; + + if (_startContainer.nodeType == HTMLNodeText) { + referenceNode = _startContainer; + } else { + referenceNode = [_startContainer childNodeAtIndex:_startOffset]; + } + + HTMLNode *parent = _startContainer; + if (referenceNode != nil) { + parent = referenceNode.parentNode; + } + + if (_startContainer.nodeType == HTMLNodeText) { + referenceNode = [(HTMLText *)_startContainer splitTextAtOffset:_startOffset]; + } + + if (node == referenceNode) { + referenceNode = referenceNode.nextSibling; + } + + [node removeFromParentNode]; + + NSUInteger newOffset = referenceNode ? referenceNode.index : parent.length; + newOffset += (node.nodeType == HTMLNodeDocumentFragment) ? node.length : 1; + + [parent insertNode:node beforeChildNode:referenceNode]; + + if (self.isCollapsed) { + [self setEndNode:parent endOffset:newOffset]; + } +} + +NS_INLINE void CheckValidSurroundState(HTMLRange *range, NSString *cmd) +{ + for (HTMLNode *node in GetAncestorNodes(range.startContainer)) { + if ([node containsNode:range.endContainer]) { + return; + } + + if (node.nodeType != HTMLNodeText) { + [NSException raise:HTMLKitInvalidStateError + format:@"%@: Invalid State Error, cannot surround range with a partially-contaied non-text node.", cmd]; + } + }; + + for (HTMLNode *node in GetAncestorNodes(range.endContainer)) { + if ([node containsNode:range.startContainer]) { + return; + } + + if (node.nodeType != HTMLNodeText) { + [NSException raise:HTMLKitInvalidNodeTypeError + format:@"%@: Invalid State Error, cannot surround range with a partially-contaied non-text node.", cmd]; + } + }; +} + +NS_INLINE void CheckValidSurroundNodeType(HTMLNode *node, NSString *cmd) +{ + if (node == nil || node.nodeType == HTMLNodeDocumentType || node.nodeType == HTMLNodeDocument || + node.nodeType == HTMLNodeDocumentFragment) { + [NSException raise:HTMLKitInvalidNodeTypeError + format:@"%@: Invalid Node Type Error, %@ is not a valid new parent for a range.", + cmd, node]; + } +} + +- (void)surroundContents:(HTMLNode *)newParent +{ + CheckValidSurroundState(self, NSStringFromSelector(_cmd)); + + CheckValidSurroundNodeType(newParent, NSStringFromSelector(_cmd)); + + HTMLDocumentFragment *fragment = [self extractContents]; + [newParent removeAllChildNodes]; + + [self insertNode:newParent]; + [newParent appendNode:fragment]; + [self selectNode:newParent]; +} + +#pragma mark - Description & Stringifier + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: %p (%@, %lu), (%@, %lu)>", self.class, self, + _startContainer, (unsigned long)_startOffset, + _endContainer, (unsigned long)_endOffset]; +} + +- (NSString *)textContent +{ + HTMLNode *lastNode = nil; + if ([_endContainer isKindOfClass:[HTMLCharacterData class]]) { + lastNode = FollowingNodeSkippingChildren(_endContainer, _ownerDocument); + } else if (_endContainer.childNodesCount > _endOffset) { + lastNode = [_endContainer childNodeAtIndex:_endOffset]; + } else { + lastNode = FollowingNodeSkippingChildren(_endContainer, _ownerDocument); + } + + NSMutableString *content = [NSMutableString string]; + for (HTMLNode *node = _startContainer; node != lastNode; node = FollowingNode(node, _ownerDocument)) { + if (node.nodeType == HTMLNodeText) { + HTMLText *text = (HTMLText *)node; + + if (node == _startContainer) { + NSString *string = [text substringDataWithRange:NSMakeRange(_startOffset, _startContainer.length - _startOffset)]; + [content appendString:string]; + } else if (node == _endContainer) { + NSString *string = [text substringDataWithRange:NSMakeRange(0, _endOffset)]; + [content appendString:string]; + } else { + [content appendString:text.data]; + } + } + } + + return content; +} + +@end diff --git a/Sources/HTMLTemplate.m b/Sources/HTMLTemplate.m index 9eebbc0..aa6fe59 100644 --- a/Sources/HTMLTemplate.m +++ b/Sources/HTMLTemplate.m @@ -8,10 +8,7 @@ #import "HTMLTemplate.h" #import "HTMLDocument.h" - -@interface HTMLNode (Private) -@property (nonatomic, weak) HTMLDocument *ownerDocument; -@end +#import "HTMLNode+Private.h" @implementation HTMLTemplate diff --git a/Sources/HTMLText.m b/Sources/HTMLText.m index 55daaae..76e94d3 100644 --- a/Sources/HTMLText.m +++ b/Sources/HTMLText.m @@ -9,7 +9,9 @@ #import "HTMLText.h" #import "HTMLElement.h" #import "NSString+HTMLKit.h" -#import "HTMLNode+Private.h" +#import "HTMLCharacterData+Private.h" +#import "HTMLKitDOMExceptions.h" +#import "HTMLDocument+Private.h" @implementation HTMLText @@ -20,35 +22,48 @@ - (instancetype)initWithData:(NSString *)data { - self = [super initWithName:@"#text" type:HTMLNodeText]; - if (self) { - _data = [[NSMutableString alloc] initWithString:data ?: @""]; - } - return self; -} - -- (NSString *)textContent -{ - return [self.data copy]; -} - -- (void)setTextContent:(NSString *)textContent -{ - [self.data setString:textContent ?: @""]; + return [super initWithName:@"#text" type:HTMLNodeText data:data]; } - (void)appendString:(NSString *)string { - [self.data appendString:string]; + [self appendData:string]; } -#pragma mark - NSCopying - -- (id)copyWithZone:(NSZone *)zone +NS_INLINE void CheckValidOffset(HTMLNode *node, NSUInteger offset, NSString *cmd) { - HTMLText *copy = [super copyWithZone:zone]; - copy.data = self.data; - return copy; + if (offset > node.length) { + [NSException raise:HTMLKitIndexSizeError + format:@"%@: Index Size Error, invalid offset %lu for splitting text node %@.", + cmd, (unsigned long)offset, node]; + } +} + +- (HTMLText *)splitTextAtOffset:(NSUInteger)offset +{ + CheckValidOffset(self, offset, NSStringFromSelector(_cmd)); + + NSUInteger length = self.length; + NSUInteger count = length - offset; + NSRange range = NSMakeRange(offset, count); + + NSString *newData = [self.data substringWithRange:range]; + HTMLText *newNode = [[HTMLText alloc] initWithData:newData]; + [self.ownerDocument adoptNode:newNode]; + + HTMLNode *parent = self.parentNode; + if (parent != nil) { + [parent insertNode:newNode beforeChildNode:self.nextSibling]; + [self.ownerDocument didInsertNewTextNode:newNode intoParent:parent afterSplittingTextNode:self atOffset:offset]; + } + + [self deleteDataInRange:range]; + + if (parent != nil) { + [self.ownerDocument clampRangesAfterSplittingTextNode:self atOffset:offset]; + } + + return newNode; } #pragma mark - Serialization diff --git a/Sources/HTMLTokenizer.m b/Sources/HTMLTokenizer.m index 34068c2..705eb45 100644 --- a/Sources/HTMLTokenizer.m +++ b/Sources/HTMLTokenizer.m @@ -13,10 +13,7 @@ #import "HTMLTokenizerStates.h" #import "HTMLTokenizerCharacters.h" #import "HTMLTokenizerEntities.h" - -@interface HTMLParser (Private) -@property (nonatomic, strong, readonly) HTMLElement *adjustedCurrentNode; -@end +#import "HTMLParser+Private.h" @interface HTMLTokenizer () { @@ -45,7 +42,6 @@ BOOL _eof; } -@property (nonatomic, weak) HTMLParser *parser; @end @implementation HTMLTokenizer diff --git a/Sources/include/HTMLCharacterData+Private.h b/Sources/include/HTMLCharacterData+Private.h new file mode 100644 index 0000000..762a0d5 --- /dev/null +++ b/Sources/include/HTMLCharacterData+Private.h @@ -0,0 +1,28 @@ +// +// HTMLCharacterData+Private.h +// HTMLKit +// +// Created by Iska on 26/11/16. +// Copyright © 2016 BrainCookie. All rights reserved. +// + +#import "HTMLCharacterData.h" +#import "HTMLNode.h" + +/** + Private HTML Character Data methods which are not intended for public API. + */ +@interface HTMLCharacterData () + +/** + Designated initializer of the HTML CharacterData, which, however, should not be used directly. It is intended to be + called only by subclasses, i.e. HTMLText and HTMLComment. + + @param name The node's name. + @param type The node's type. + @param data The node's data string. + @return A new instance of a HTML CharacterData. + */ +- (instancetype)initWithName:(NSString *)name type:(HTMLNodeType)type data:(NSString *)data NS_DESIGNATED_INITIALIZER; + +@end diff --git a/Sources/include/HTMLCharacterData.h b/Sources/include/HTMLCharacterData.h new file mode 100644 index 0000000..302d2c5 --- /dev/null +++ b/Sources/include/HTMLCharacterData.h @@ -0,0 +1,32 @@ +// +// HTMLCharacterData.h +// HTMLKit +// +// Created by Iska on 26/11/16. +// Copyright © 2016 BrainCookie. All rights reserved. +// + +#import "HTMLNode.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + A HTML CharacterData + + https://dom.spec.whatwg.org/#characterdata + */ +@interface HTMLCharacterData : HTMLNode + +/** @brief The associated data string. */ +@property (nonatomic, copy, readonly) NSString *data; + +- (void)setData:(NSString *)data; +- (void)appendData:(NSString *)data; +- (void)insertData:(NSString *)data atOffset:(NSUInteger)offset; +- (void)deleteDataInRange:(NSRange)range; +- (void)replaceDataInRange:(NSRange)range withData:(NSString *)data; +- (NSString *)substringDataWithRange:(NSRange)range; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/include/HTMLComment.h b/Sources/include/HTMLComment.h index 41a378e..d9ab331 100644 --- a/Sources/include/HTMLComment.h +++ b/Sources/include/HTMLComment.h @@ -6,17 +6,14 @@ // Copyright (c) 2015 BrainCookie. All rights reserved. // -#import "HTMLNode.h" +#import "HTMLCharacterData.h" NS_ASSUME_NONNULL_BEGIN /** A HTML Comment node */ -@interface HTMLComment : HTMLNode - -/** @brief The comment string. */ -@property (nonatomic, copy) NSString *data; +@interface HTMLComment : HTMLCharacterData /** Initializes a new HTML comment node. diff --git a/Sources/include/HTMLDOM.h b/Sources/include/HTMLDOM.h index 50eadbc..567062b 100644 --- a/Sources/include/HTMLDOM.h +++ b/Sources/include/HTMLDOM.h @@ -11,9 +11,11 @@ #import "HTMLDocumentType.h" #import "HTMLDocumentFragment.h" #import "HTMLElement.h" +#import "HTMLCharacterData.h" #import "HTMLComment.h" #import "HTMLText.h" #import "HTMLTemplate.h" +#import "HTMLRange.h" #import "HTMLDOMTokenList.h" #import "HTMLNodeIterator.h" #import "HTMLTreeWalker.h" diff --git a/Sources/include/HTMLDOMUtils.h b/Sources/include/HTMLDOMUtils.h new file mode 100644 index 0000000..b792197 --- /dev/null +++ b/Sources/include/HTMLDOMUtils.h @@ -0,0 +1,15 @@ +// +// HTMLDOMUtils.h +// HTMLKit +// +// Created by Iska on 03/12/16. +// Copyright © 2016 BrainCookie. All rights reserved. +// + +#import +#import "HTMLDOM.h" + +@class HTMLNode; + +extern HTMLNode * GetCommonAncestorContainer(HTMLNode *nodeA, HTMLNode *nodeB); +extern NSArray * GetAncestorNodes(HTMLNode *node); diff --git a/Sources/include/HTMLDocument+Private.h b/Sources/include/HTMLDocument+Private.h new file mode 100644 index 0000000..7678b9f --- /dev/null +++ b/Sources/include/HTMLDocument+Private.h @@ -0,0 +1,99 @@ +// +// HTMLDocument+Private.h +// HTMLKit +// +// Created by Iska on 27/11/16. +// Copyright © 2016 BrainCookie. All rights reserved. +// + +#import "HTMLDocument.h" +#import "HTMLNode.h" +#import "HTMLCharacterData.h" +#import "HTMLNodeIterator.h" +#import "HTMLRange.h" +#import "HTMLText.h" + +/** + Private HTML Document methods which are not intended for public API. + */ +@interface HTMLDocument () + +@property (nonatomic, assign) HTMLDocumentReadyState readyState; + +/** + Runs the necessary steps after removing a node from the DOM. + + @param oldNode The old node that was removed. + @param oldParent The old parent of the node that was removed. + @param oldPreviousSibling The old previous sibling node of the node that was removed. + */ +- (void)runRemovingStepsForNode:(HTMLNode *)oldNode + withOldParent:(HTMLNode *)oldParent + andOldPreviousSibling:(HTMLNode *)oldPreviousSibling; + +/** + Attaches a node iterator to this document. + + @param iterator The iterator to attach. + */ +- (void)attachNodeIterator:(HTMLNodeIterator *)iterator; + +/** + Detaches a node interator from this document. + + @param iterator The iterator to detach. + */ +- (void)detachNodeIterator:(HTMLNodeIterator *)iterator; + + +/** + Attaches a range to this document. + + @param range The range to attach. + */ +- (void)attachRange:(HTMLRange *)range; + +/** + Detaches a range from this document. + + @param range The range to detach. + */ +- (void)detachRange:(HTMLRange *)range; + +/** + Callback on removing text from a CharacterData node. + + @param node The CharacterData node. + @param offset The offset at which the data was removed. + @param length The length of the data that was removed. + */ +- (void)didRemoveCharacterDataInNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length; + +/** + Callback on adding text from a CharacterData node. + + @param node The CharacterData node. + @param offset The offset at which the data was added. + @param length The length of the data that was added. + */ +- (void)didAddCharacterDataToNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length; + +/** + Callback on inserting a new text node when an old text node is split. + + @param newNode The new text node after splitting. + @param parent The parent where newNode was inserted. + @param node The old text node that was split. + @param offset The offset of splitting. + */ +- (void)didInsertNewTextNode:(HTMLText *)newNode intoParent:(HTMLNode *)parent afterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset; + +/** + Callback for clamping current ranges whose end boundary is after the text node upon splitting it. + + @param node The text node that was split. + @param offset The offset of splitting + */ +- (void)clampRangesAfterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset; + +@end diff --git a/Sources/include/HTMLKitDOMExceptions.h b/Sources/include/HTMLKitDOMExceptions.h index 115b884..4e357cc 100644 --- a/Sources/include/HTMLKitDOMExceptions.h +++ b/Sources/include/HTMLKitDOMExceptions.h @@ -14,3 +14,8 @@ extern NSString * const HTMLKitNotSupportedError; extern NSString * const HTMLKitSyntaxError; extern NSString * const HTMLKitInvalidCharacterError; + +extern NSString * const HTMLKitInvalidNodeTypeError; +extern NSString * const HTMLKitIndexSizeError; +extern NSString * const HTMLKitWrongDocumentError; +extern NSString * const HTMLKitInvalidStateError; diff --git a/Sources/include/HTMLNode.h b/Sources/include/HTMLNode.h index da1a5c5..c9ca332 100644 --- a/Sources/include/HTMLNode.h +++ b/Sources/include/HTMLNode.h @@ -28,7 +28,7 @@ typedef NS_ENUM(short, HTMLNodeType) /** A node's position in the HTML document when compared with other nodes. */ -typedef NS_ENUM(unsigned short, HTMLDocumentPosition) +typedef NS_OPTIONS(unsigned short, HTMLDocumentPosition) { HTMLDocumentPositionEquivalent = 0x0, HTMLDocumentPositionDisconnected = 0x01, @@ -71,6 +71,11 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition) */ @property (nonatomic, weak, readonly, nullable) HTMLDocument *ownerDocument; +/** + The root node of this node, if any. + */ +@property (nonatomic, weak, readonly, nullable) HTMLNode *rootNode; + /** The parent node of this node, if any. */ @@ -122,6 +127,11 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition) */ @property (nonatomic, strong, readonly, nullable) HTMLElement *nextSiblingElement; +/** + The index of this node. + */ +@property (nonatomic, readonly, assign) NSUInteger index; + /** The text content of this node. */ @@ -137,6 +147,11 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition) */ @property (nonatomic, copy) NSString *innerHTML; +/** + The length of the node as described in https://dom.spec.whatwg.org/#concept-node-length + */ +@property (nonatomic, assign) NSUInteger length; + /** @abstract Use concrete subclasses of the HTML Node. */ @@ -164,6 +179,21 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition) */ - (NSUInteger)childNodesCount; +/** + Checks whether the node is empty as described in https://dom.spec.whatwg.org/#concept-node-length + + @return `YES` if the node is empty, `NO` otherwise. + */ +- (BOOL)isEmpty; + +/** + Clones this node. + + @param deep If `YES` then also clones child nodes. Otherwise a shallow clone is returned, which behaves the same as `copy`. + @return A clone of this node. + */ +- (instancetype)cloneNodeDeep:(BOOL)deep; + /** Returns the child node at a given index. @@ -318,7 +348,8 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition) - (BOOL)isDescendantOfNode:(HTMLNode *)node; /** - Checks whether this node contains the given node. + Checks whether this node contains the given node. This performs an `invlusive ancestor` check, i.e. it returns `YES` + if the given node is the same object as this node. @param node The node to check. @return `YES` if this node contains the given node, `NO` otherwsie. diff --git a/Sources/include/HTMLNodeIterator+Private.h b/Sources/include/HTMLNodeIterator+Private.h new file mode 100644 index 0000000..3714eac --- /dev/null +++ b/Sources/include/HTMLNodeIterator+Private.h @@ -0,0 +1,28 @@ +// +// HTMLNodeIterator+Private.h +// HTMLKit +// +// Created by Iska on 27/11/16. +// Copyright © 2016 BrainCookie. All rights reserved. +// + +#import "HTMLNodeIterator.h" +#import "HTMLNode.h" + +/** + Private HTML Node Iterator methods which are not intended for public API. + */ +@interface HTMLNodeIterator (Private) + +/** + Runs the necessary steps after removing a node from the DOM. + + @param oldNode The old node that was removed. + @param oldParent The old parent of the node that was removed. + @param oldPreviousSibling The old previous sibling node of the node that was removed. + */ +- (void)runRemovingStepsForNode:(HTMLNode *)oldNode + withOldParent:(HTMLNode *)oldParent + andOldPreviousSibling:(HTMLNode *)oldPreviousSibling; + +@end diff --git a/Sources/include/HTMLParser+Private.h b/Sources/include/HTMLParser+Private.h new file mode 100644 index 0000000..35db646 --- /dev/null +++ b/Sources/include/HTMLParser+Private.h @@ -0,0 +1,23 @@ +// +// HTLMLParser+Private.h +// HTMLKit +// +// Created by Iska on 27/11/16. +// Copyright © 2016 BrainCookie. All rights reserved. +// + +#import "HTMLParser.h" +#import "HTMLElement.h" + +/** + Private HTML Parser properties & methods which are not intended for public API. + */ +@interface HTMLParser (Private) + +/** + The adjusted current node in the context of HTML parsing as described in: + https://html.spec.whatwg.org/#adjusted-current-node + */ +@property (nonatomic, strong, readonly) HTMLElement *adjustedCurrentNode; + +@end diff --git a/Sources/include/HTMLRange+Private.h b/Sources/include/HTMLRange+Private.h new file mode 100644 index 0000000..e745578 --- /dev/null +++ b/Sources/include/HTMLRange+Private.h @@ -0,0 +1,61 @@ +// +// HTMLRange+Private.h +// HTMLKit +// +// Created by Iska on 27/11/16. +// Copyright © 2016 BrainCookie. All rights reserved. +// + +#import "HTMLRange.h" +#import "HTMLCharacterData.h" + +@interface HTMLRange () + +/** + Runs the necessary steps after removing data from a character data node that may be a range's boundary. + + @param node The character data node. + @param offset The offset at which the data was removed. + @param length The length of the data that was removed. + */ +- (void)didRemoveCharacterDataInNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length; + +/** + Runs the necessary steps after adding data to a character data node that may be a range's boundary. + + @param node The character data node. + @param offset The offset at which the data was added. + @param length The length of the data that was added. + */ +- (void)didAddCharacterDataToNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length; + +/** + Runs the necessary steps after inserting a new text node when an old text node is split. + + @param newNode The new text node after splitting. + @param parent The parent where newNode was inserted. + @param node The old text node that was split. + @param offset The offset of splitting. + */ +- (void)didInsertNewTextNode:(HTMLText *)newNode intoParent:(HTMLNode *)parent afterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset; + +/** + Runs the necessary steps to clamp the range whose end boundary is after the text node upon splitting it. + + @param node The text node that was split. + @param offset The offset of splitting + */ +- (void)clampRangesAfterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset; + +/** + Runs the necessary steps after removing a node from the DOM. + + @param oldNode The old node that was removed. + @param oldParent The old parent of the node that was removed. +@param oldPreviousSibling The old previous sibling node of the node that was removed. + */ +- (void)runRemovingStepsForNode:(HTMLNode *)oldNode + withOldParent:(HTMLNode *)oldParent + andOldPreviousSibling:(HTMLNode *)oldPreviousSibling; + +@end diff --git a/Sources/include/HTMLRange.h b/Sources/include/HTMLRange.h new file mode 100644 index 0000000..a35c468 --- /dev/null +++ b/Sources/include/HTMLRange.h @@ -0,0 +1,252 @@ +// +// HTMLRange.h +// HTMLKit +// +// Created by Iska on 20/11/16. +// Copyright © 2016 BrainCookie. All rights reserved. +// + +#import "HTMLNode.h" +#import "HTMLDocumentFragment.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + The comparison method of range boundaries + + - HTMLRangeComparisonMethodStartToStart: Compares the start boundary-point of sourceRange to the start boundary-point of this range. + - HTMLRangeComparisonMethodStartToEnd: Compares the start boundary-point of sourceRange to the end boundary-point of this range. + - HTMLRangeComparisonMethodEndToEnd: Compares the end boundary-point of sourceRange to the end boundary-point of this range. + - HTMLRangeComparisonMethodEndToStart: Compares the end boundary-point of sourceRange to the start boundary-point of this range. + */ +typedef NS_ENUM(unsigned short, HTMLRangeComparisonMethod) +{ + HTMLRangeComparisonMethodStartToStart = 0, + HTMLRangeComparisonMethodStartToEnd = 1, + HTMLRangeComparisonMethodEndToEnd = 2, + HTMLRangeComparisonMethodEndToStart = 3 +}; + +#pragma mark - DOM Range + +/** + A HTML Range, represents a sequence of content within a node tree. + Each range has a start and an end which are boundary points. + A boundary point is a tuple consisting of a node and a non-negative numeric offset. + + https://dom.spec.whatwg.org/#ranges + */ +@interface HTMLRange : NSObject + +/** + The node of the start boundary point. + */ +@property (nonatomic, readonly, strong) HTMLNode *startContainer; + +/** + The offset of the start boundary point. + */ +@property (nonatomic, readonly, assign) NSUInteger startOffset; + +/** + The node of the end boundary point. + */ +@property (nonatomic, readonly, strong) HTMLNode *endContainer; + +/** + The offset of the end boundary point. + */ +@property (nonatomic, readonly, assign) NSUInteger endOffset; + +/** + Checks whether the range is collapsed, i.e. if start is the same as end. + + @return `YES` if the range is collapsed, `NO` otherwise. + */ +@property (nonatomic, readonly, assign, getter=isCollapsed) BOOL collapsed; + +/** + The common container node that contains both start and end nodes. + */ +@property (nonatomic, readonly, weak) HTMLNode *commonAncestorContainer; + +/** + @abstract A range is always associated with a HTML Document. Use `initWithDocument:` initializer instead. + */ +- (instancetype)init NS_UNAVAILABLE; + +/** + Initializes a new range instance for the given document. + + @param document The HTML doucment for which the range will be constructed. + @return A new HTML Range instance. + */ +- (instancetype)initWithDowcument:(HTMLDocument *)document; + +/** + Initializes a new range instance for the given document and boundaries. + + @param document The HTML doucment for which the range will be constructed. + @param startContainer The node for the start boundary + @param startOffset The offset of the start boundary + @param endContainer The node for the end boundary + @param endOffset The offset of the end boundary + @return A new HTML Range instance. + */ +- (instancetype)initWithDowcument:(HTMLDocument *)document + startContainer:(HTMLNode *)startContainer startOffset:(NSUInteger)startOffset + endContainer:(HTMLNode *)endContainer endOffset:(NSUInteger)endOffset; + +/** + Sets the start boundary. + + @param startNode The new node of the start boundary. + @param startOffset The new offset of the start boundary. + */ +- (void)setStartNode:(HTMLNode *)node startOffset:(NSUInteger)offset; + +/** + Sets the end boundary. + + @param startNode The new node of the end boundary. + @param startOffset The new offset of the end boundary. + */ +- (void)setEndNode:(HTMLNode *)node endOffset:(NSUInteger)offset; + +/** + Sets the start boundary before the given node. + + @param node The node before which the boundary will be set. + */ +- (void)setStartBeforeNode:(HTMLNode *)node; + +/** + Sets the start boundary after the given node. + + @param node The node after which the boundary will be set. + */ +- (void)setStartAfterNode:(HTMLNode *)node; + +/** + Sets the end boundary before the given node. + + @param node The node before which the boundary will be set. + */ +- (void)setEndBeforeNode:(HTMLNode *)node; + +/** + Sets the end boundary after the given node. + + @param node The node after which the boundary will be set. + */ +- (void)setEndAfterNode:(HTMLNode *)node; + +/** + Collapses this range to its start. + */ +- (void)collapseToStart; + +/** + Collapses this range to its end. + */ +- (void)collapseToEnd; + +/** + Selects the given node in the range. + + @param node The node to select + */ +- (void)selectNode:(HTMLNode *)node; + +/** + Selects the node's contents in the range. + + @param node The node to select the contents. + */ +- (void)selectNodeContents:(HTMLNode *)node; + +/** + Compares the boundary points of the given range with this range. + + @param method The comparison method. + @param sourceRange The source range for comparison. + @return `NSOrderedAscending` if ordered before, `NSOrderedSame` if ordered same, `NSOrderedDescending` otherwise. + + @see HTMLRangeComparisonMethod + */ +- (NSComparisonResult)compareBoundaryPoints:(HTMLRangeComparisonMethod)method sourceRange:(HTMLRange *)sourceRange; + +/** + Compares the given point (reference node, offset) with this range. + + @param node The node to compare with this range. + @param offset The offset inside the reference node. + @return `NSOrderedAscending`, `NSOrderedSame`, or `NSOrderedDescending` depending on whether the node is before, the same as, or after this range. + */ +- (NSComparisonResult)comparePoint:(HTMLNode *)node offset:(NSUInteger)offset; + +/** + Checks if the given point (reference node, offset) is in this range. + + @param node The node to compare with this range. + @param offset The offset inside the reference node. + @return `YES` if the given point is in this range, `NO` otherwise. + */ +- (BOOL)containsPoint:(HTMLNode *)node offset:(NSUInteger)offset; + +/** + Checks if the given node intersects this range. + + @param node The node to compare with this range. + @return `YES` if the given node intersects the range, `NO` otherwise. + */ +- (BOOL)intersectsNode:(HTMLNode *)node; + +/** + Deletes the contents represented by this range from the associated document. + */ +- (void)deleteContents; + +/** + Extracts the contents represented by this range from the associated document. + + @return A document fragment with the extracted contents. + */ +- (HTMLDocumentFragment *)extractContents; + +/** + Clones the contents represented by this range in the associated document. + + @return A document fragment with the cloned contents. + */ +- (HTMLDocumentFragment *)cloneContents; + +/** + Inserts the given node at the start of this range. + + If the node is being added to a text node, then the text node is split at the insertion point and the given node + is inserted between the resulting text nodes. + + @param node The node to insert. + */ +- (void)insertNode:(HTMLNode *)node; + +/** + Surrounds the contents of this range with the given node. + + The range's boundaries will placed around the given node, i.e. start is before and end is after newParent. + + @param newParent The new parent node which will surround the range. + */ +- (void)surroundContents:(HTMLNode *)newParent; + +/** + The stringifier of the range. + + @return The text contents of the range. + */ +- (NSString *)textContent; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/include/HTMLText.h b/Sources/include/HTMLText.h index e97df7e..4e8c6d6 100644 --- a/Sources/include/HTMLText.h +++ b/Sources/include/HTMLText.h @@ -6,17 +6,14 @@ // Copyright (c) 2015 BrainCookie. All rights reserved. // -#import "HTMLNode.h" +#import "HTMLCharacterData.h" NS_ASSUME_NONNULL_BEGIN /** A HTML Text node */ -@interface HTMLText : HTMLNode - -/** @brief The text string. */ -@property (nonatomic, copy) NSMutableString *data; +@interface HTMLText : HTMLCharacterData /** Initializes a new HTML text node. @@ -31,7 +28,9 @@ NS_ASSUME_NONNULL_BEGIN @param string The string to append. */ -- (void)appendString:(NSString *)string; +- (void)appendString:(NSString *)string __attribute__((deprecated("Use `appendData:` instead."))); + +- (HTMLText *)splitTextAtOffset:(NSUInteger)offset; @end diff --git a/Sources/include/HTMLTokenizer.h b/Sources/include/HTMLTokenizer.h index 04a0673..0c37262 100644 --- a/Sources/include/HTMLTokenizer.h +++ b/Sources/include/HTMLTokenizer.h @@ -37,7 +37,7 @@ @see HTMLParser */ -@property (nonatomic, weak, readonly) HTMLParser *parser; +@property (nonatomic, weak) HTMLParser *parser; /** Initializes a new Tokenizer with the given string. diff --git a/Sources/module.modulemap b/Sources/module.modulemap index 4172c3e..6eca8e6 100644 --- a/Sources/module.modulemap +++ b/Sources/module.modulemap @@ -1,30 +1,38 @@ module HTMLKit { umbrella header "HTMLKit.h" - private header "CSSCodePoints.h" - private header "CSSInputStream.h" - private header "HTMLCharacterToken.h" - private header "HTMLCommentToken.h" - private header "HTMLDOCTYPEToken.h" - private header "HTMLElementAdjustment.h" - private header "HTMLElementTypes.h" - private header "HTMLEOFToken.h" - private header "HTMLInputStreamReader.h" - private header "HTMLListOfActiveFormattingElements.h" - private header "HTMLMarker.h" - private header "HTMLNode+Private.h" - private header "HTMLNodeTraversal.h" - private header "HTMLParseErrorToken.h" - private header "HTMLParserInsertionModes.h" - private header "HTMLStackOfOpenElements.h" - private header "HTMLTagToken.h" - private header "HTMLToken.h" - private header "HTMLTokenizer.h" - private header "HTMLTokenizerCharacters.h" - private header "HTMLTokenizerEntities.h" - private header "HTMLTokenizerStates.h" - private header "HTMLTokens.h" - module * { export * } export * + + explicit module Private { + header "CSSCodePoints.h" + header "CSSInputStream.h" + header "HTMLCharacterToken.h" + header "HTMLCommentToken.h" + header "HTMLDOCTYPEToken.h" + header "HTMLElementAdjustment.h" + header "HTMLElementTypes.h" + header "HTMLEOFToken.h" + header "HTMLInputStreamReader.h" + header "HTMLListOfActiveFormattingElements.h" + header "HTMLMarker.h" + header "HTMLParseErrorToken.h" + header "HTMLParserInsertionModes.h" + header "HTMLStackOfOpenElements.h" + header "HTMLTagToken.h" + header "HTMLToken.h" + header "HTMLTokenizer.h" + header "HTMLTokenizerCharacters.h" + header "HTMLTokenizerEntities.h" + header "HTMLTokenizerStates.h" + header "HTMLTokens.h" + header "HTMLNode+Private.h" + header "HTMLDocument+Private.h" + header "HTMLCharacterData+Private.h" + header "HTMLRange+Private.h" + header "HTMLNodeIterator+Private.h" + header "HTMLParser+Private.h" + header "HTMLNodeTraversal.h" + header "HTMLDOMUtils.h" + } } diff --git a/Tests/HTMLCharacterDataTests.m b/Tests/HTMLCharacterDataTests.m new file mode 100644 index 0000000..9d66891 --- /dev/null +++ b/Tests/HTMLCharacterDataTests.m @@ -0,0 +1,100 @@ +// +// HTMLCharacterDataTests.m +// HTMLKit +// +// Created by Iska on 10/01/17. +// Copyright © 2017 BrainCookie. All rights reserved. +// + +#import +#import "HTMLElement.h" +#import "HTMLCharacterData.h" +#import "HTMLText.h" + +@interface HTMLCharacterDataTests : XCTestCase + +@end + +@implementation HTMLCharacterDataTests + +- (void)testSetData +{ + HTMLText *text = [[HTMLText alloc] initWithData:@"This is a text"]; + + [text setData:@"New text"]; + XCTAssertEqualObjects(text.data, @"New text"); +} + +- (void)testAppendData +{ + HTMLText *text = [[HTMLText alloc] initWithData:@"This is a text"]; + + [text appendData:@"New text"]; + XCTAssertEqualObjects(text.data, @"This is a textNew text"); +} + +- (void)testReplaceData +{ + HTMLText *text = [[HTMLText alloc] initWithData:@"This is a text"]; + + [text replaceDataInRange:NSMakeRange(0, 5) withData:@"New text "]; + XCTAssertEqualObjects(text.data, @"New text is a text"); + + [text replaceDataInRange:NSMakeRange(4, 4) withData:@"data"]; + XCTAssertEqualObjects(text.data, @"New data is a text"); +} + +- (void)testDeleteData +{ + HTMLText *text = [[HTMLText alloc] initWithData:@"This is a text"]; + + [text deleteDataInRange:NSMakeRange(5, 3)]; + XCTAssertEqualObjects(text.data, @"This a text"); + + [text deleteDataInRange:NSMakeRange(0, text.data.length)]; + XCTAssertEqualObjects(text.data, @""); +} + +- (void)testInsertData +{ + HTMLText *text = [[HTMLText alloc] initWithData:@"This is a text"]; + + [text insertData:@"New " atOffset:10]; + XCTAssertEqualObjects(text.data, @"This is a New text"); + + [text insertData:@"Prefix " atOffset:0]; + XCTAssertEqualObjects(text.data, @"Prefix This is a New text"); +} + +- (void)testSplitText_Invalid +{ + HTMLText *text = [[HTMLText alloc] initWithData:@"text"]; + XCTAssertThrows([text splitTextAtOffset:5]); +} + +- (void)testSplitText +{ + HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div"]; + HTMLText *text = [[HTMLText alloc] initWithData:@"This is a text"]; + [div appendNode:text]; + + HTMLText *newText = [text splitTextAtOffset:7]; + XCTAssertEqualObjects(newText.data, @" a text"); + + XCTAssertEqual(div.childNodesCount, 2); + XCTAssertEqualObjects(div.firstChild.textContent, @"This is"); + XCTAssertEqualObjects(div.lastChild.textContent, @" a text"); + + [div appendNode:[[HTMLElement alloc] initWithTagName:@"p"]]; + + newText = [newText splitTextAtOffset:0]; + XCTAssertEqualObjects(newText.data, @" a text"); + + XCTAssertEqual(div.childNodesCount, 4); + XCTAssertEqualObjects([div childNodeAtIndex:0].textContent, @"This is"); + XCTAssertEqualObjects([div childNodeAtIndex:1].textContent, @""); + XCTAssertEqualObjects([div childNodeAtIndex:2].textContent, @" a text"); + XCTAssertEqual([div childNodeAtIndex:3].nodeType, HTMLNodeElement); +} + +@end diff --git a/Tests/HTMLKitDOMTokenListTests.m b/Tests/HTMLDOMTokenListTests.m similarity index 100% rename from Tests/HTMLKitDOMTokenListTests.m rename to Tests/HTMLDOMTokenListTests.m diff --git a/Tests/HTMLKitMutationAlgorithmsTests.m b/Tests/HTMLMutationAlgorithmsTests.m similarity index 100% rename from Tests/HTMLKitMutationAlgorithmsTests.m rename to Tests/HTMLMutationAlgorithmsTests.m diff --git a/Tests/HTMLKitNodeIteratorTests.m b/Tests/HTMLNodeIteratorTests.m similarity index 100% rename from Tests/HTMLKitNodeIteratorTests.m rename to Tests/HTMLNodeIteratorTests.m diff --git a/Tests/HTMLKitNodesTests.m b/Tests/HTMLNodesTests.m similarity index 89% rename from Tests/HTMLKitNodesTests.m rename to Tests/HTMLNodesTests.m index 7193ecb..8e994dd 100644 --- a/Tests/HTMLKitNodesTests.m +++ b/Tests/HTMLNodesTests.m @@ -483,4 +483,33 @@ XCTAssertEqualObjects([element childNodeAtIndex:1].asElement.tagName, @"p"); } +- (void)testCompareDocumentPosition +{ + HTMLDocument *document = [HTMLDocument documentWithString:@"

      hello

      "]; + + HTMLNode *outerDiv = document.body.firstChild; + HTMLNode *paragraph = document.body.firstChild.firstChild; + HTMLNode *anchor = [[document.body.firstChild childNodeAtIndex:1] childNodeAtIndex:0]; + HTMLNode *image = [[document.body.firstChild childNodeAtIndex:1] childNodeAtIndex:1]; + + XCTAssertTrue([paragraph compareDocumentPositionWithNode:paragraph] == HTMLDocumentPositionEquivalent); + + HTMLElement *element = [HTMLElement new]; + XCTAssertTrue([paragraph compareDocumentPositionWithNode:element] == + (HTMLDocumentPositionDisconnected | HTMLDocumentPositionImplementationSpecific | + paragraph.hash < element.hash ? HTMLDocumentPositionPreceding: HTMLDocumentPositionFollowing)); + + XCTAssertTrue([paragraph compareDocumentPositionWithNode:image] == HTMLDocumentPositionPreceding); + XCTAssertTrue([anchor compareDocumentPositionWithNode:image] == HTMLDocumentPositionPreceding); + XCTAssertTrue([image compareDocumentPositionWithNode:anchor] == HTMLDocumentPositionFollowing); + + XCTAssertTrue([outerDiv compareDocumentPositionWithNode:paragraph] == (HTMLDocumentPositionContains | HTMLDocumentPositionPreceding)); + XCTAssertTrue([outerDiv compareDocumentPositionWithNode:anchor] == (HTMLDocumentPositionContains | HTMLDocumentPositionPreceding)); + XCTAssertTrue([outerDiv compareDocumentPositionWithNode:image] == (HTMLDocumentPositionContains | HTMLDocumentPositionPreceding)); + + XCTAssertTrue([paragraph compareDocumentPositionWithNode:outerDiv] == (HTMLDocumentPositionContainedBy | HTMLDocumentPositionFollowing)); + XCTAssertTrue([anchor compareDocumentPositionWithNode:outerDiv] == (HTMLDocumentPositionContainedBy | HTMLDocumentPositionFollowing)); + XCTAssertTrue([image compareDocumentPositionWithNode:outerDiv] == (HTMLDocumentPositionContainedBy | HTMLDocumentPositionFollowing)); +} + @end diff --git a/Tests/HTMLKitOrderedDictionaryTests.m b/Tests/HTMLOrderedDictionaryTests.m similarity index 100% rename from Tests/HTMLKitOrderedDictionaryTests.m rename to Tests/HTMLOrderedDictionaryTests.m diff --git a/Tests/HTMLRangeTests.m b/Tests/HTMLRangeTests.m new file mode 100644 index 0000000..48e5fe0 --- /dev/null +++ b/Tests/HTMLRangeTests.m @@ -0,0 +1,2101 @@ +// +// HTMLRangeTests.m +// HTMLKit +// +// Created by Iska on 29/12/16. +// Copyright © 2016 BrainCookie. All rights reserved. +// + +#import + +#import "HTMLDOM.h" +#import "HTMLNode+Private.h" +#import "HTMLRange+Private.h" + +#define BodyOf(doc) doc.body.innerHTML +#define InnerHTML(str) [HTMLDocument documentWithString:str].body.innerHTML +#define DoubleQuote(str) [str stringByReplacingOccurrencesOfString:@"'" withString:@"\""] + +@interface HTMLRangeTests : XCTestCase +{ + HTMLDocument *_document; + HTMLElement *_h1; + HTMLElement *_p; + HTMLElement *_div1; + HTMLElement *_div2; + HTMLText *_title; + HTMLText *_paragraphText; + HTMLText *_firstText; + HTMLText *_secondText; + HTMLComment *_firstComment; + HTMLComment *_secondComment; +} +@end + +@implementation HTMLRangeTests + +#pragma mark - Setup + +- (void)setUp +{ + [super setUp]; + [self setupDocument]; +} + +- (void)setupDocument +{ + // HTML:

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // DOM: + // + // | + // +------------+-----------+ + // | | | + //

      + // | | | + // "Title" "Hello" | + // | + // +--------+----------+ + // | | + //
      + // | + // +-----------------+-----------------+ + // | | | + // "First text" "Second text" + + _h1 = [[HTMLElement alloc] initWithTagName:@"h1" attributes:@{@"id": @"h1"}]; + _p = [[HTMLElement alloc] initWithTagName:@"p" attributes:@{@"id": @"p"}]; + + _div1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"div1"}]; + _div2 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"div2"}]; + + _title = [[HTMLText alloc] initWithData:@"Title"]; + _paragraphText = [[HTMLText alloc] initWithData:@"Hello"]; + _firstText = [[HTMLText alloc] initWithData:@"First text"]; + _secondText = [[HTMLText alloc] initWithData:@"Second text"]; + + _firstComment = [[HTMLComment alloc] initWithData:@"First comment"]; + _secondComment = [[HTMLComment alloc] initWithData:@"Second comment"]; + + [_h1 appendNode:_title]; + [_p appendNode:_paragraphText]; + [_div1 appendNodes:@[_div2, _secondComment]]; + [_div2 appendNodes:@[_firstText, _firstComment, _secondText]]; + + _document = [HTMLDocument documentWithString:@""]; + [_document.body appendNodes:@[_h1, _p, _div1]]; +} + +#pragma mark - Tests + +- (void)testInitRange +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + + XCTAssertEqualObjects(range.startContainer, _document); + XCTAssertEqual(range.startOffset, 0); + + XCTAssertEqualObjects(range.endContainer, _document); + XCTAssertEqual(range.endOffset, 0); +} + +- (void)testSetStartBoundary +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + + XCTAssertThrows([range setStartNode:[HTMLElement new] startOffset:0], @"Cannot set boundary to a node outside of the range's document"); + XCTAssertThrows([range setStartNode:[HTMLDocumentType new] startOffset:0], @"DOCTYPE as range boundary is invalid"); + XCTAssertThrows([range setStartNode:_firstText startOffset:_firstText.length + 1], @"Offset is outside the boundary node"); + + //

      Title

      Hello

      First textSecond text
      + // | + // collapsed range + + [range setStartNode:_paragraphText startOffset:4]; + XCTAssertEqualObjects(range.startContainer, _paragraphText); + XCTAssertEqual(range.startOffset, 4); + XCTAssertEqualObjects(range.endContainer, _paragraphText); + XCTAssertEqual(range.endOffset, 4); + + //

      Title

      Hello

      First textSecond text
      + // |______________| + // s e + + [range setStartNode:_title startOffset:2]; + XCTAssertEqualObjects(range.startContainer, _title); + XCTAssertEqual(range.startOffset, 2); + XCTAssertEqualObjects(range.endContainer, _paragraphText); + XCTAssertEqual(range.endOffset, 4); +} + +- (void)testSetEndBoundary +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + + XCTAssertThrows([range setEndNode:[HTMLDocumentType new] endOffset: 0], @"DOCTYPE as range boundary is invalid"); + XCTAssertThrows([range setEndNode:_firstText endOffset:_firstText.length + 1], @"Offset is outside the boundary node"); + + // ....

      Title

      Hello

      First textSecond text
      + // |____________________| + // s e + + [range setEndNode:_title endOffset:4]; + XCTAssertEqualObjects(range.startContainer, _document); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _title); + XCTAssertEqual(range.endOffset, 4); + + //

      Title

      Hello

      First textSecond text
      + // |_| + // s e + + [range setStartNode:_title startOffset:3]; + XCTAssertEqualObjects(range.startContainer, _title); + XCTAssertEqual(range.startOffset, 3); + XCTAssertEqualObjects(range.endContainer, _title); + XCTAssertEqual(range.endOffset, 4); + + //

      Title

      Hello

      First textSecond text
      + // | + // collapsed range + + [range setEndNode:_title endOffset:0]; + XCTAssertEqualObjects(range.startContainer, _title); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _title); + XCTAssertEqual(range.endOffset, 0); +} + +- (void)testSetStartBeforeNode +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + + //

      Title

      Hello

      First textSecond text
      + // |___| + // s e + + [range setStartNode:_paragraphText startOffset:0]; + [range setEndNode:_paragraphText endOffset:4]; + + //

      Title

      Hello

      First textSecond text
      + // (p, 0)->|_____| + // s e + + [range setStartBeforeNode:_paragraphText]; + + XCTAssertEqualObjects(range.startContainer, _p); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _paragraphText); + XCTAssertEqual(range.endOffset, 4); + + //

      Title

      Hello

      First textSecond text
      + // |___| + // s e + + [range setStartNode:_paragraphText startOffset:0]; + [range setEndNode:_paragraphText endOffset:4]; + + //

      Title

      Hello

      First textSecond text
      + // (body, 1)->|_____| + // s e + + [range setStartBeforeNode:_p]; + + XCTAssertEqualObjects(range.startContainer, _document.body); + XCTAssertEqual(range.startOffset, 1); + XCTAssertEqualObjects(range.endContainer, _paragraphText); + XCTAssertEqual(range.endOffset, 4); + + //

      Title

      Hello

      First textSecond text
      + // |___| + // s e + + [range setStartNode:_paragraphText startOffset:0]; + [range setEndNode:_paragraphText endOffset:4]; + + //

      Title

      Hello

      First textSecond text
      + //(body, 0)->|__________________| + // s e + + [range setStartBeforeNode:_h1]; + + XCTAssertEqualObjects(range.startContainer, _document.body); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _paragraphText); + XCTAssertEqual(range.endOffset, 4); + + //

      Title

      Hello

      First textSecond text
      + // |____________________| + // s e + + [range setStartNode:_firstText startOffset:_firstText.length]; + [range setEndNode:_secondText endOffset:0]; + + //

      Title

      Hello

      First textSecond text
      + // (div1, 0)->|________________________________| + // s e + + [range setStartBeforeNode:_div2]; + + XCTAssertEqualObjects(range.startContainer, _div1); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _secondText); + XCTAssertEqual(range.endOffset, 0); + + //

      Title

      Hello

      First textSecond text
      + // |____________________| + // s e + + [range setStartNode:_firstText startOffset:_firstText.length]; + [range setEndNode:_secondText endOffset:0]; + + //

      Title

      Hello

      First textSecond text
      + // (body, 2)->|_____________________________________| + // s e + + [range setStartBeforeNode:_div1]; + + XCTAssertEqualObjects(range.startContainer, _document.body); + XCTAssertEqual(range.startOffset, 2); + XCTAssertEqualObjects(range.endContainer, _secondText); + XCTAssertEqual(range.endOffset, 0); +} + +- (void)testSetStartAfterNode +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + + //

      Title

      Hello

      First textSecond text
      + // |___| + // s e + + [range setStartNode:_paragraphText startOffset:0]; + [range setEndNode:_paragraphText endOffset:4]; + + //

      Title

      Hello

      First textSecond text
      + // (p, 1)->| + // collapsed range + + [range setStartAfterNode:_paragraphText]; + + XCTAssertEqualObjects(range.startContainer, _p); + XCTAssertEqual(range.startOffset, 1); + XCTAssertEqualObjects(range.endContainer, _p); + XCTAssertEqual(range.endOffset, 1); + + //

      Title

      Hello

      First textSecond text
      + // |___| + // s e + + [range setStartNode:_paragraphText startOffset:0]; + [range setEndNode:_paragraphText endOffset:4]; + + //

      Title

      Hello

      First textSecond text
      + // (body, 2)->| + // collapsed range + + [range setStartAfterNode:_p]; + + XCTAssertEqualObjects(range.startContainer, _document.body); + XCTAssertEqual(range.startOffset, 2); + XCTAssertEqualObjects(range.endContainer, _document.body); + XCTAssertEqual(range.endOffset, 2); + + //

      Title

      Hello

      First textSecond text
      + // |___| + // s e + + [range setStartNode:_paragraphText startOffset:0]; + [range setEndNode:_paragraphText endOffset:4]; + + //

      Title

      Hello

      First textSecond text
      + // (body, 1)->|_____| + // s e + + [range setStartAfterNode:_h1]; + + XCTAssertEqualObjects(range.startContainer, _document.body); + XCTAssertEqual(range.startOffset, 1); + XCTAssertEqualObjects(range.endContainer, _paragraphText); + XCTAssertEqual(range.endOffset, 4); + + //

      Title

      Hello

      First textSecond text
      + // |____________________| + // s e + + [range setStartNode:_firstText startOffset:_firstText.length]; + [range setEndNode:_secondText endOffset:0]; + + //

      Title

      Hello

      First textSecond text
      + // (div1, 1)->| + // collapsed range + + [range setStartAfterNode:_div2]; + + XCTAssertEqualObjects(range.startContainer, _div1); + XCTAssertEqual(range.startOffset, 1); + XCTAssertEqualObjects(range.endContainer, _div1); + XCTAssertEqual(range.endOffset, 1); + + //

      Title

      Hello

      First textSecond text
      + // |____________________| + // s e + + [range setStartNode:_firstText startOffset:_firstText.length]; + [range setEndNode:_secondText endOffset:0]; + + //

      Title

      Hello

      First textSecond text
      + // (_div2, 1)->|___________________| + // s e + + [range setStartAfterNode:_firstText]; + + XCTAssertEqualObjects(range.startContainer, _div2); + XCTAssertEqual(range.startOffset, 1); + XCTAssertEqualObjects(range.endContainer, _secondText); + XCTAssertEqual(range.endOffset, 0); +} + +- (void)testSetEndBeforeNode +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + + //

      Title

      Hello

      First textSecond text
      + // |___| + // s e + + [range setStartNode:_paragraphText startOffset:0]; + [range setEndNode:_paragraphText endOffset:4]; + + //

      Title

      Hello

      First textSecond text
      + // (p, 0)->| + // collapsed range + + [range setEndBeforeNode:_paragraphText]; + + XCTAssertEqualObjects(range.startContainer, _p); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _p); + XCTAssertEqual(range.endOffset, 0); + + //

      Title

      Hello

      First textSecond text
      + // |___| + // s e + + [range setStartNode:_paragraphText startOffset:0]; + [range setEndNode:_paragraphText endOffset:4]; + + //

      Title

      Hello

      First textSecond text
      + // (body, 1)->| + // collapsed range + + [range setEndBeforeNode:_p]; + + XCTAssertEqualObjects(range.startContainer, _document.body); + XCTAssertEqual(range.startOffset, 1); + XCTAssertEqualObjects(range.endContainer, _document.body); + XCTAssertEqual(range.endOffset, 1); + + //

      Title

      Hello

      First textSecond text
      + // |___| + // s e + + [range setStartNode:_paragraphText startOffset:0]; + [range setEndNode:_paragraphText endOffset:4]; + + //

      Title

      Hello

      First textSecond text
      + //(body, 0)->| + // collapsed range + + [range setEndBeforeNode:_h1]; + + XCTAssertEqualObjects(range.startContainer, _document.body); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _document.body); + XCTAssertEqual(range.endOffset, 0); + + //

      Title

      Hello

      First textSecond text
      + // |_____________________________| + // s e + + [range setStartNode:_firstText startOffset:0]; + [range setEndNode:_secondText endOffset:0]; + + //

      Title

      Hello

      First textSecond text
      + // |__________|<-(div2, 1) + // s e + + [range setEndBeforeNode:_firstComment]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _div2); + XCTAssertEqual(range.endOffset, 1); + + //

      Title

      Hello

      First textSecond text
      + // |______________________| + // s e + + [range setStartNode:_firstText startOffset:_firstText.length]; + [range setEndNode:_secondText endOffset:2]; + + //

      Title

      Hello

      First textSecond text
      + // |___________________|<-(div1, 2) + // s e + + [range setEndBeforeNode:_secondText]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, _firstText.length); + XCTAssertEqualObjects(range.endContainer, _div2); + XCTAssertEqual(range.endOffset, 2); +} + +- (void)testSetEndAfterNode +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + + //

      Title

      Hello

      First textSecond text
      + // |___| + // s e + + [range setStartNode:_paragraphText startOffset:0]; + [range setEndNode:_paragraphText endOffset:4]; + + //

      Title

      Hello

      First textSecond text
      + // |____|<-(p, 1) + // s e + + [range setEndAfterNode:_paragraphText]; + + XCTAssertEqualObjects(range.startContainer, _paragraphText); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _p); + XCTAssertEqual(range.endOffset, 1); + + //

      Title

      Hello

      First textSecond text
      + // |___| + // s e + + [range setStartNode:_paragraphText startOffset:0]; + [range setEndNode:_paragraphText endOffset:4]; + + //

      Title

      Hello

      First textSecond text
      + // |_______________|<-(body, 2) + // s e + + [range setEndAfterNode:_p]; + + XCTAssertEqualObjects(range.startContainer, _paragraphText); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _document.body); + XCTAssertEqual(range.endOffset, 2); + + //

      Title

      Hello

      First textSecond text
      + // |___| + // s e + + [range setStartNode:_paragraphText startOffset:0]; + [range setEndNode:_paragraphText endOffset:4]; + + //

      Title

      Hello

      First textSecond text
      + // (body, 1)->| + // collapsed range + + [range setEndAfterNode:_h1]; + + XCTAssertEqualObjects(range.startContainer, _document.body); + XCTAssertEqual(range.startOffset, 1); + XCTAssertEqualObjects(range.endContainer, _document.body); + XCTAssertEqual(range.endOffset, 1); + + //

      Title

      Hello

      First textSecond text
      + // |____________________| + // s e + + [range setStartNode:_firstText startOffset:_firstText.length]; + [range setEndNode:_secondText endOffset:0]; + + //

      Title

      Hello

      First textSecond text
      + // |______________________________________|<-(div1, 1) + // s e + + [range setEndAfterNode:_div2]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, _firstText.length); + XCTAssertEqualObjects(range.endContainer, _div1); + XCTAssertEqual(range.endOffset, 1); + + //

      Title

      Hello

      First textSecond text
      + // |____________________| + // s e + + [range setStartNode:_firstText startOffset:_firstText.length]; + [range setEndNode:_secondText endOffset:0]; + + //

      Title

      Hello

      First textSecond text
      + // |__________________|<-(div2, 2) + // s e + + [range setEndAfterNode:_firstComment]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, _firstText.length); + XCTAssertEqualObjects(range.endContainer, _div2); + XCTAssertEqual(range.endOffset, 2); +} + +- (void)testIsCollapsed +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + XCTAssertTrue(range.isCollapsed); + + [range setEndNode:_title endOffset:1]; + XCTAssertFalse(range.isCollapsed); + + [range setStartNode:_title startOffset:1]; + XCTAssertTrue(range.isCollapsed); +} + +- (void)testCollapseToStart +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // |______________| + // s e + + [range setStartNode:_title startOffset:2]; + [range setEndNode:_paragraphText endOffset:4]; + [range collapseToStart]; + + XCTAssertEqualObjects(range.startContainer, _title); + XCTAssertEqual(range.startOffset, 2); + XCTAssertEqualObjects(range.endContainer, _title); + XCTAssertEqual(range.endOffset, 2); + XCTAssertTrue(range.isCollapsed); +} + +- (void)testCollapseToEnd +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // |______________| + // s e + + [range setStartNode:_title startOffset:2]; + [range setEndNode:_paragraphText endOffset:4]; + [range collapseToEnd]; + + XCTAssertEqualObjects(range.startContainer, _paragraphText); + XCTAssertEqual(range.startOffset, 4); + XCTAssertEqualObjects(range.endContainer, _paragraphText); + XCTAssertEqual(range.endOffset, 4); + XCTAssertTrue(range.isCollapsed); +} + +- (void)testSelectNode +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ---------- + [range selectNode:_firstText]; + XCTAssertEqualObjects(range.startContainer, _div2); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _div2); + XCTAssertEqual(range.endOffset, 1); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // -------------------- + [range selectNode:_firstComment]; + XCTAssertEqualObjects(range.startContainer, _div2); + XCTAssertEqual(range.startOffset, 1); + XCTAssertEqualObjects(range.endContainer, _div2); + XCTAssertEqual(range.endOffset, 2); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ----------------------------------------------------------------------------------- + [range selectNode:_div1]; + XCTAssertEqualObjects(range.startContainer, _document.body); + XCTAssertEqual(range.startOffset, 2); + XCTAssertEqualObjects(range.endContainer, _document.body); + XCTAssertEqual(range.endOffset, 3); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ---------------------------------------------------- + [range selectNode:_div2]; + XCTAssertEqualObjects(range.startContainer, _div1); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _div1); + XCTAssertEqual(range.endOffset, 1); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // -------------------- + [range selectNode:_secondComment]; + XCTAssertEqualObjects(range.startContainer, _div1); + XCTAssertEqual(range.startOffset, 1); + XCTAssertEqualObjects(range.endContainer, _div1); + XCTAssertEqual(range.endOffset, 2); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // -------------- + [range selectNode:_h1]; + XCTAssertEqualObjects(range.startContainer, _document.body); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _document.body); + XCTAssertEqual(range.endOffset, 1); +} + +- (void)testSelectNodeContents +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ---------- + [range selectNodeContents:_firstText]; + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _firstText); + XCTAssertEqual(range.endOffset, _firstText.length); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ------------- + [range selectNodeContents:_firstComment]; + XCTAssertEqualObjects(range.startContainer, _firstComment); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _firstComment); + XCTAssertEqual(range.endOffset, _firstComment.length); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ------------------------------------------------------------------------ + [range selectNodeContents:_div1]; + XCTAssertEqualObjects(range.startContainer, _div1); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _div1); + XCTAssertEqual(range.endOffset, 2); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ----------------------------------------- + [range selectNodeContents:_div2]; + XCTAssertEqualObjects(range.startContainer, _div2); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _div2); + XCTAssertEqual(range.endOffset, 3); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // -------------- + [range selectNodeContents:_secondComment]; + XCTAssertEqualObjects(range.startContainer, _secondComment); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _secondComment); + XCTAssertEqual(range.endOffset, _secondComment.length); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ----- + [range selectNodeContents:_h1]; + XCTAssertEqualObjects(range.startContainer, _h1); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _h1); + XCTAssertEqual(range.endOffset, 1); +} + +- (void)testCompareBoundaries +{ + HTMLRange *range1 = [[HTMLRange alloc] initWithDowcument:_document]; + + HTMLRange *range2 = [[HTMLRange alloc] initWithDowcument:_document]; + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ---------- + [range1 selectNode:_firstText]; + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ----------- + [range2 selectNode:_secondText]; + + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodStartToStart sourceRange:range2] == NSOrderedAscending); + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodStartToEnd sourceRange:range2] == NSOrderedAscending); + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodEndToEnd sourceRange:range2] == NSOrderedAscending); + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodEndToStart sourceRange:range2] == NSOrderedAscending); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ---------- + [range1 selectNode:_firstText]; + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // -------------------- + [range2 selectNode:_firstComment]; + + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodStartToStart sourceRange:range2] == NSOrderedAscending); + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodStartToEnd sourceRange:range2] == NSOrderedSame); + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodEndToEnd sourceRange:range2] == NSOrderedAscending); + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodEndToStart sourceRange:range2] == NSOrderedAscending); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ---------- + [range1 selectNode:_firstText]; + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ---------------------------------------------------- + [range2 selectNode:_div2]; + + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodStartToStart sourceRange:range2] == NSOrderedDescending); + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodStartToEnd sourceRange:range2] == NSOrderedDescending); + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodEndToEnd sourceRange:range2] == NSOrderedAscending); + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodEndToStart sourceRange:range2] == NSOrderedAscending); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ----------- + [range1 selectNode:_secondText]; + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ---------------------------------------------------- + [range2 selectNode:_div2]; + + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodStartToStart sourceRange:range2] == NSOrderedDescending); + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodStartToEnd sourceRange:range2] == NSOrderedDescending); + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodEndToEnd sourceRange:range2] == NSOrderedAscending); + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodEndToStart sourceRange:range2] == NSOrderedAscending); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ----------------------------------------------------------------------------------- + [range1 selectNode:_div1]; + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ---------------------------------------------------- + [range2 selectNode:_div2]; + + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodStartToStart sourceRange:range2] == NSOrderedAscending); + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodStartToEnd sourceRange:range2] == NSOrderedDescending); + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodEndToEnd sourceRange:range2] == NSOrderedDescending); + XCTAssertTrue([range1 compareBoundaryPoints:HTMLRangeComparisonMethodEndToStart sourceRange:range2] == NSOrderedAscending); +} + +- (void)testContainmentAndComparisons +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + + /*********** Compare ***********/ + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ---------------------------------------------------- + [range selectNode:_div2]; + XCTAssertTrue([range comparePoint:_div1 offset:0] == NSOrderedSame); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ----------------------------------------- + [range selectNodeContents:_div2]; + XCTAssertTrue([range comparePoint:_div1 offset:0] == NSOrderedAscending); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ----- + [range selectNodeContents:_p]; + XCTAssertTrue([range comparePoint:_div1 offset:0] == NSOrderedDescending); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ------------ + [range selectNode:_p]; + XCTAssertTrue([range comparePoint:_document.body offset:0] == NSOrderedAscending); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ------------ + [range selectNode:_p]; + XCTAssertTrue([range comparePoint:_document.body offset:1] == NSOrderedSame); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ------------ + [range selectNode:_p]; + XCTAssertTrue([range comparePoint:_document.body offset:2] == NSOrderedSame); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ------------ + [range selectNode:_p]; + XCTAssertTrue([range comparePoint:_document.body offset:3] == NSOrderedDescending); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ------------- + [range selectNodeContents:_firstComment]; + XCTAssertTrue([range comparePoint:_firstComment offset:0] == NSOrderedSame); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ------------- + [range selectNodeContents:_firstComment]; + XCTAssertTrue([range comparePoint:_firstComment offset:3] == NSOrderedSame); + + /*********** Contains ***********/ + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ---------------------------------------------------- + [range selectNode:_div2]; + XCTAssertTrue([range containsPoint:_div1 offset:0]); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ------------ + [range selectNode:_p]; + XCTAssertTrue([range containsPoint:_document.body offset:1]); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ------------ + [range selectNode:_p]; + XCTAssertTrue([range containsPoint:_document.body offset:2]); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ------------- + [range selectNodeContents:_firstComment]; + XCTAssertTrue([range containsPoint:_firstComment offset:3]); +} + +- (void)testIntersections +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + + XCTAssertFalse([range intersectsNode:[HTMLText new]]); + XCTAssertTrue([range intersectsNode:_document]); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ------------ + [range selectNode:_p]; + XCTAssertFalse([range intersectsNode:_h1]); + XCTAssertTrue([range intersectsNode:_p]); + XCTAssertFalse([range intersectsNode:_div1]); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ----------------- + [range selectNode:_p]; + [range setEndAfterNode:_div1]; + XCTAssertFalse([range intersectsNode:_h1]); + XCTAssertTrue([range intersectsNode:_p]); + XCTAssertTrue([range intersectsNode:_div1]); + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // --------------------------- + [range selectNode:_firstComment]; + [range setEndAfterNode:_secondText]; + XCTAssertTrue([range intersectsNode:_div2]); + XCTAssertFalse([range intersectsNode:_firstText]); + XCTAssertTrue([range intersectsNode:_firstComment]); + XCTAssertTrue([range intersectsNode:_secondText]); +} + +- (void)testThatCharacterDataMutationsUpdateRangeCorrectly +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // |________| + [range selectNodeContents:_firstText]; + + //

      Title

      Hello

      First text New TextSecond text
      <--Second comment-->
      + // |________| + [_firstText appendData:@" New Text"]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _firstText); + XCTAssertEqual(range.endOffset, 10); + + //

      Title

      Hello

      New text New TextSecond text
      <--Second comment-->
      + // |______| + [_firstText replaceDataInRange:NSMakeRange(0, 5) withData:@"New"]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _firstText); + XCTAssertEqual(range.endOffset, 8); + + //

      Title

      Hello

      New TextSecond text
      <--Second comment-->
      + // | + [_firstText setData:@"New Text"]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _firstText); + XCTAssertEqual(range.endOffset, 0); + + //

      Title

      Hello

      New TextSecond text
      <--Second comment-->
      + // |______| + [range selectNodeContents:_firstText]; + + //

      Title

      Hello

      NewTextSecond text
      <--Second comment-->
      + // |_____| + [_firstText deleteDataInRange:NSMakeRange(3, 1)]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _firstText); + XCTAssertEqual(range.endOffset, 7); + + //

      Title

      Hello

      NewNewTextSecond text
      <--Second comment-->
      + // |________| + [_firstText insertData:@"New" atOffset:3]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _firstText); + XCTAssertEqual(range.endOffset, 10); + + //

      Title

      Hello

      NewNewTextSecond text
      <--Second comment-->
      + // |_____| + [range setStartNode:_firstText startOffset:3]; + + //

      Title

      Hello

      PrefixNewNewTextSecond text
      <--Second comment-->
      + // |_____| + [_firstText insertData:@"Prefix" atOffset:0]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 9); + XCTAssertEqualObjects(range.endContainer, _firstText); + XCTAssertEqual(range.endOffset, 16); + + //

      Title

      Hello

      PrefixNewTextSecond text
      <--Second comment-->
      + // |_____| + [_firstText deleteDataInRange:NSMakeRange(6, 3)]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 6); + XCTAssertEqualObjects(range.endContainer, _firstText); + XCTAssertEqual(range.endOffset, 13); + + //

      Title

      Hello

      PreABCDTextSecond text
      <--Second comment-->
      + // |______| + [_firstText replaceDataInRange:NSMakeRange(3, 6) withData:@"ABCD"]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 3); + XCTAssertEqualObjects(range.endContainer, _firstText); + XCTAssertEqual(range.endOffset, 11); + + //

      Title

      Hello

      Pre TextSecond text
      <--Second comment-->
      + // |___| + [_firstText replaceDataInRange:NSMakeRange(3, 4) withData:@" "]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 3); + XCTAssertEqualObjects(range.endContainer, _firstText); + XCTAssertEqual(range.endOffset, 8); +} + +- (void)testThatCharacterDataMutationsUpdateRangeCorrectly_DifferentBoundaries +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // |___________________________| + [range setStartNode:_firstText startOffset:5]; + [range setEndNode:_secondText endOffset:3]; + + //

      Title

      Hello

      First text New TextSecond text
      <--Second comment-->
      + // |____________________________________| + [_firstText appendData:@" New Text"]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 5); + XCTAssertEqualObjects(range.endContainer, _secondText); + XCTAssertEqual(range.endOffset, 3); + + //

      Title

      Hello

      New text New TextSecond text
      <--Second comment-->
      + // |______________________________________| + [_firstText replaceDataInRange:NSMakeRange(0, 5) withData:@"New"]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _secondText); + XCTAssertEqual(range.endOffset, 3); + + //

      Title

      Hello

      New TextSecond text
      <--Second comment-->
      + // |_____________________________| + [_firstText setData:@"New Text"]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _secondText); + XCTAssertEqual(range.endOffset, 3); + + + //

      Title

      Hello

      NewTextSecond text
      <--Second comment-->
      + // |____________________________| + [_firstText deleteDataInRange:NSMakeRange(3, 1)]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _secondText); + XCTAssertEqual(range.endOffset, 3); + + //

      Title

      Hello

      NewNewTextSecond text
      <--Second comment-->
      + // |_______________________________| + [_firstText insertData:@"New" atOffset:3]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _secondText); + XCTAssertEqual(range.endOffset, 3); + + //

      Title

      Hello

      NewNewTextSecond text
      <--Second comment-->
      + // |____________________________| + [range setStartNode:_firstText startOffset:3]; + + //

      Title

      Hello

      PrefixNewNewTextSecond text
      <--Second comment-->
      + // |____________________________| + [_firstText insertData:@"Prefix" atOffset:0]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 9); + XCTAssertEqualObjects(range.endContainer, _secondText); + XCTAssertEqual(range.endOffset, 3); + + //

      Title

      Hello

      PrefixNewTextSecond text
      <--Second comment-->
      + // |____________________________| + [_firstText deleteDataInRange:NSMakeRange(6, 3)]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 6); + XCTAssertEqualObjects(range.endContainer, _secondText); + XCTAssertEqual(range.endOffset, 3); + + //

      Title

      Hello

      PreABCDTextSecond text
      <--Second comment-->
      + // |_____________________________| + [_firstText replaceDataInRange:NSMakeRange(3, 6) withData:@"ABCD"]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 3); + XCTAssertEqualObjects(range.endContainer, _secondText); + XCTAssertEqual(range.endOffset, 3); + + //

      Title

      Hello

      Pre TextSecond text
      <--Second comment-->
      + // |__________________________| + [_firstText replaceDataInRange:NSMakeRange(3, 4) withData:@" "]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 3); + XCTAssertEqualObjects(range.endContainer, _secondText); + XCTAssertEqual(range.endOffset, 3); + + //

      Title

      Hello

      Pre TextPrefixSecond text
      <--Second comment-->
      + // |________________________________| + [_secondText insertData:@"Prefix" atOffset:0]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 3); + XCTAssertEqualObjects(range.endContainer, _secondText); + XCTAssertEqual(range.endOffset, 9); + + //

      Title

      Hello

      Pre TextPrefixSecond text
      <--Second comment-->
      + // |___________________| + [_firstComment setData:@"X"]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 3); + XCTAssertEqualObjects(range.endContainer, _secondText); + XCTAssertEqual(range.endOffset, 9); +} + +- (void)testThatTextSplitUpdateRangeCorrectly_BeforeStartTextNode +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // |________| + [range selectNodeContents:_firstText]; + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ^________| + HTMLText *split = [_firstText splitTextAtOffset:0]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, split); + XCTAssertEqual(range.endOffset, 10); +} + +- (void)testThatTextSplitUpdateRangeCorrectly_AfterEndTextNode +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // |________| + [range selectNodeContents:_firstText]; + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // |________^ + [_firstText splitTextAtOffset:_firstText.length]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, _firstText); + XCTAssertEqual(range.endOffset, 10); +} + +- (void)testThatTextSplitUpdateRangeCorrectly_MidleSameTextNode +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // |________| + [range selectNodeContents:_firstText]; + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // |____^___| + HTMLText *split = [_firstText splitTextAtOffset:6]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 0); + XCTAssertEqualObjects(range.endContainer, split); + XCTAssertEqual(range.endOffset, 4); +} + +- (void)testThatTextSplitUpdateRangeCorrectly_MidleDifferentTextNodes +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // |______________________________| + [range setStartNode:_firstText startOffset:2]; + [range setEndNode:_secondText endOffset:3]; + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // |____^__________________________| + [_firstText splitTextAtOffset:6]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 2); + XCTAssertEqualObjects(range.endContainer, _secondText); + XCTAssertEqual(range.endOffset, 3); +} + +- (void)testThatTextSplitUpdateRangeCorrectly_BeforeStartDifferentTextNodes +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // |______________________________| + [range setStartNode:_firstText startOffset:6]; + [range setEndNode:_secondText endOffset:3]; + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // ^ |__________________________| + HTMLText *split = [_firstText splitTextAtOffset:2]; + + XCTAssertEqualObjects(range.startContainer, split); + XCTAssertEqual(range.startOffset, 4); + XCTAssertEqualObjects(range.endContainer, _secondText); + XCTAssertEqual(range.endOffset, 3); +} + +- (void)testThatTextSplitUpdateRangeCorrectly_BeforeEndDifferentTextNodes +{ + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:_document]; + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // |_______________________________| + [range setStartNode:_firstText startOffset:6]; + [range setEndNode:_secondText endOffset:4]; + + //

      Title

      Hello

      First textSecond text
      <--Second comment-->
      + // |_____________________________^_| + HTMLText *split = [_secondText splitTextAtOffset:2]; + + XCTAssertEqualObjects(range.startContainer, _firstText); + XCTAssertEqual(range.startOffset, 6); + XCTAssertEqualObjects(range.endContainer, split); + XCTAssertEqual(range.endOffset, 2); +} + +#pragma mark - Editing + +- (HTMLDocument *)editingDocument +{ + //
      + //
      + //

      This is a text

      + //

      Hello

      + //
      + //

      World

      + //
      + //

      Another text

      + //
      + //
      + //
      + // | + // +---------------+---------------+ + // | | | + //

      + // | | | + // +--------+-------+ "World"

      + // | | | + //

      +-----+-----+ + // | | | | + // +-------+-------+ "Hello" "Another" + // | | | + // "This " " text" | + // | "Text" + // "is a" + // + // + return [HTMLDocument documentWithString: + @"

      " + @"

      This is a text

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      "]; +} + +#pragma mark - Delete Contents + +- (void)testDeleteContents_SameTextNode +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:1]; + HTMLNode *end = [document querySelector:@"#P1"].firstChild; + [range setEndNode:end endOffset:4]; + [range deleteContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      T is a text

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); +} + +- (void)testDeleteContents_SameTextNode_Selected +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *node = [document querySelector:@"#P1"].firstChild; + [range selectNode:node]; + [range deleteContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      is a text

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); + + XCTAssertEqual([document querySelector:@"#P1"].childNodesCount, 2); +} + +- (void)testDeleteContents_SameTextNode_SelectedContents +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *node = [document querySelector:@"#P1"].firstChild; + [range selectNodeContents:node]; + [range deleteContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      is a text

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); + + XCTAssertEqual([document querySelector:@"#P1"].childNodesCount, 3); + XCTAssertEqual([document querySelector:@"#P1"].firstChild.nodeType, HTMLNodeText); +} + +- (void)testDeleteContents_DifferentTextNodesOfSingleParent +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:3]; + HTMLNode *end = [document querySelector:@"#P1"].lastChild; + [range setEndNode:end endOffset:2]; + [range deleteContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      Thiext

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); +} + +- (void)testDeleteContents_DifferentTextNodesOfDifferentParents +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:3]; + HTMLNode *end = [document querySelector:@"#P2"].lastChild; + [range setEndNode:end endOffset:4]; + [range deleteContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      Thi

      o

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); +} + +- (void)testDeleteContents_DifferentTextNodesOfDifferentParents_HavingContainedNodesInBetween +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:3]; + HTMLNode *end = [document querySelector:@"#P4"].firstChild; + [range setEndNode:end endOffset:2]; + [range deleteContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      Thi

      " + @"

      other text

      " + @"
      ")); +} + +- (void)testDeleteContents_SameContainerNode +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"]; + [range setStartNode:start startOffset:0]; + HTMLNode *end = [document querySelector:@"#P1"]; + [range setEndNode:end endOffset:2]; + [range deleteContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      text

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); +} + +- (void)testDeleteContents_SameContainerNode_Selected +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *node = [document querySelector:@"#P1"]; + [range selectNode:node]; + [range deleteContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); +} + +- (void)testDeleteContents_SameContainerNode_SelectedContents +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *node = [document querySelector:@"#P1"]; + [range selectNodeContents:node]; + [range deleteContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); +} + +- (void)testDeleteContents_StartContainerIsCommonRoot +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#D1"]; + [range setStartNode:start startOffset:0]; + HTMLNode *end = [document querySelector:@"#P2"].firstChild; + [range setEndNode:end endOffset:2]; + [range deleteContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      llo

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); +} + +- (void)testDeleteContents_EndContainerIsCommonRoot +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:1]; + HTMLNode *end = [document querySelector:@"#D1"]; + [range setEndNode:end endOffset:1]; + [range deleteContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      T

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); +} + +#pragma mark - Clone Contents + +- (void)testCloneContents_SameTextNode +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:1]; + HTMLNode *end = [document querySelector:@"#P1"].firstChild; + [range setEndNode:end endOffset:4]; + HTMLDocumentFragment *fragment = [range cloneContents]; + + XCTAssertEqualObjects(fragment.innerHTML, @"his"); + XCTAssertEqual(fragment.childNodesCount, 1); + XCTAssertEqual(fragment.firstChild.nodeType, HTMLNodeText); +} + +- (void)testCloneContents_SameTextNode_Selected +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *node = [document querySelector:@"#P1"].firstChild; + [range selectNode:node]; + HTMLDocumentFragment *fragment = [range cloneContents]; + + XCTAssertEqualObjects(fragment.innerHTML, @"This "); + XCTAssertEqual(fragment.childNodesCount, 1); + XCTAssertEqual(fragment.firstChild.nodeType, HTMLNodeText); +} + +- (void)testCloneContents_SameTextNode_SelectedContents +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *node = [document querySelector:@"#P1"].firstChild; + [range selectNodeContents:node]; + HTMLDocumentFragment *fragment = [range cloneContents]; + + XCTAssertEqualObjects(fragment.innerHTML, @"This "); + XCTAssertEqual(fragment.childNodesCount, 1); + XCTAssertEqual(fragment.firstChild.nodeType, HTMLNodeText); +} + +- (void)testCloneContents_DifferentTextNodesOfSingleParent +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:3]; + HTMLNode *end = [document querySelector:@"#P1"].lastChild; + [range setEndNode:end endOffset:2]; + HTMLDocumentFragment *fragment = [range cloneContents]; + + XCTAssertEqualObjects(fragment.innerHTML, @"s is a t"); +} + +- (void)testCloneContents_DifferentTextNodesOfDifferentParents +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:3]; + HTMLNode *end = [document querySelector:@"#P2"].lastChild; + [range setEndNode:end endOffset:4]; + HTMLDocumentFragment *fragment = [range cloneContents]; + + XCTAssertEqualObjects(fragment.innerHTML, DoubleQuote(@"

      s is a text

      Hell

      ")); +} + +- (void)testCloneContents_DifferentTextNodesOfDifferentParents_HavingContainedNodesInBetween +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:3]; + HTMLNode *end = [document querySelector:@"#P4"].firstChild; + [range setEndNode:end endOffset:2]; + HTMLDocumentFragment *fragment = [range cloneContents]; + + XCTAssertEqualObjects(fragment.innerHTML, DoubleQuote(@"

      s is a text

      Hello

      " + @"

      World

      " + @"

      An

      ")); +} + +- (void)testCloneContents_SameContainerNode +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"]; + [range setStartNode:start startOffset:0]; + HTMLNode *end = [document querySelector:@"#P1"]; + [range setEndNode:end endOffset:2]; + HTMLDocumentFragment *fragment = [range cloneContents]; + + XCTAssertEqualObjects(fragment.innerHTML, @"This is a"); +} + +- (void)testCloneContents_SameContainerNode_Selected +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *node = [document querySelector:@"#P1"]; + [range selectNode:node]; + HTMLDocumentFragment *fragment = [range cloneContents]; + + XCTAssertEqualObjects(fragment.innerHTML, DoubleQuote(@"

      This is a text

      ")); +} + +- (void)testCloneContents_SameContainerNode_SelectedContents +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *node = [document querySelector:@"#P1"]; + [range selectNodeContents:node]; + HTMLDocumentFragment *fragment = [range cloneContents]; + + XCTAssertEqualObjects(fragment.innerHTML, DoubleQuote(@"This is a text")); +} + +- (void)testCloneContents_StartContainerIsCommonRoot +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#D1"]; + [range setStartNode:start startOffset:0]; + HTMLNode *end = [document querySelector:@"#P2"].firstChild; + [range setEndNode:end endOffset:2]; + HTMLDocumentFragment *fragment = [range cloneContents]; + + XCTAssertEqualObjects(fragment.innerHTML, DoubleQuote(@"

      This is a text

      He

      ")); +} + +- (void)testCloneContents_EndContainerIsCommonRoot +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:1]; + HTMLNode *end = [document querySelector:@"#D1"]; + [range setEndNode:end endOffset:1]; + HTMLDocumentFragment *fragment = [range cloneContents]; + + XCTAssertEqualObjects(fragment.innerHTML, DoubleQuote(@"

      his is a text

      ")); +} + +#pragma mark - Extract Contents + +- (void)testExtractContents_SameTextNode +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:1]; + HTMLNode *end = [document querySelector:@"#P1"].firstChild; + [range setEndNode:end endOffset:4]; + HTMLDocumentFragment *fragment = [range extractContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      T is a text

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); + + XCTAssertEqualObjects(fragment.innerHTML, @"his"); + XCTAssertEqual(fragment.childNodesCount, 1); + XCTAssertEqual(fragment.firstChild.nodeType, HTMLNodeText); +} + +- (void)testExtractContents_SameTextNode_Selected +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *node = [document querySelector:@"#P1"].firstChild; + [range selectNode:node]; + HTMLDocumentFragment *fragment = [range extractContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      is a text

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); + + XCTAssertEqual([document querySelector:@"#P1"].childNodesCount, 2); + + XCTAssertEqualObjects(fragment.innerHTML, @"This "); + XCTAssertEqual(fragment.childNodesCount, 1); + XCTAssertEqual(fragment.firstChild.nodeType, HTMLNodeText); +} + +- (void)testExtractContents_SameTextNode_SelectedContents +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *node = [document querySelector:@"#P1"].firstChild; + [range selectNodeContents:node]; + HTMLDocumentFragment *fragment = [range extractContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      is a text

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); + + XCTAssertEqual([document querySelector:@"#P1"].childNodesCount, 3); + XCTAssertEqual([document querySelector:@"#P1"].firstChild.nodeType, HTMLNodeText); + + XCTAssertEqualObjects(fragment.innerHTML, @"This "); + XCTAssertEqual(fragment.childNodesCount, 1); + XCTAssertEqual(fragment.firstChild.nodeType, HTMLNodeText); +} + +- (void)testExtractContents_DifferentTextNodesOfSingleParent +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:3]; + HTMLNode *end = [document querySelector:@"#P1"].lastChild; + [range setEndNode:end endOffset:2]; + HTMLDocumentFragment *fragment = [range extractContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      Thiext

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); + + XCTAssertEqualObjects(fragment.innerHTML, @"s is a t"); +} + +- (void)testExtractContents_DifferentTextNodesOfDifferentParents +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:3]; + HTMLNode *end = [document querySelector:@"#P2"].lastChild; + [range setEndNode:end endOffset:4]; + HTMLDocumentFragment *fragment = [range extractContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      Thi

      o

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); + + XCTAssertEqualObjects(fragment.innerHTML, DoubleQuote(@"

      s is a text

      Hell

      ")); +} + +- (void)testExtractContents_DifferentTextNodesOfDifferentParents_HavingContainedNodesInBetween +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:3]; + HTMLNode *end = [document querySelector:@"#P4"].firstChild; + [range setEndNode:end endOffset:2]; + HTMLDocumentFragment *fragment = [range extractContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      Thi

      " + @"

      other text

      " + @"
      ")); + + XCTAssertEqualObjects(fragment.innerHTML, DoubleQuote(@"

      s is a text

      Hello

      " + @"

      World

      " + @"

      An

      ")); +} + +- (void)testExtractContents_SameContainerNode +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"]; + [range setStartNode:start startOffset:0]; + HTMLNode *end = [document querySelector:@"#P1"]; + [range setEndNode:end endOffset:2]; + HTMLDocumentFragment *fragment = [range extractContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      text

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); + + XCTAssertEqualObjects(fragment.innerHTML, @"This is a"); +} + +- (void)testExtractContents_SameContainerNode_Selected +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *node = [document querySelector:@"#P1"]; + [range selectNode:node]; + HTMLDocumentFragment *fragment = [range extractContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); + + XCTAssertEqualObjects(fragment.innerHTML, DoubleQuote(@"

      This is a text

      ")); +} + +- (void)testExtractContents_SameContainerNode_SelectedContents +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *node = [document querySelector:@"#P1"]; + [range selectNodeContents:node]; + HTMLDocumentFragment *fragment = [range extractContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); + + XCTAssertEqualObjects(fragment.innerHTML, DoubleQuote(@"This is a text")); +} + +- (void)testExtractContents_StartContainerIsCommonRoot +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#D1"]; + [range setStartNode:start startOffset:0]; + HTMLNode *end = [document querySelector:@"#P2"].firstChild; + [range setEndNode:end endOffset:2]; + HTMLDocumentFragment *fragment = [range extractContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      llo

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); + + XCTAssertEqualObjects(fragment.innerHTML, DoubleQuote(@"

      This is a text

      He

      ")); +} + +- (void)testExtractContents_EndContainerIsCommonRoot +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:1]; + HTMLNode *end = [document querySelector:@"#D1"]; + [range setEndNode:end endOffset:1]; + HTMLDocumentFragment *fragment = [range extractContents]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      T

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); + + XCTAssertEqualObjects(fragment.innerHTML, DoubleQuote(@"

      his is a text

      ")); +} + +#pragma mark - Insertion & Surround + +- (void)testInsertNode_InvalidNode +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:2]; + HTMLNode *end = [document querySelector:@"#P1"].lastChild; + [range setEndNode:end endOffset:5]; + + XCTAssertThrows([range insertNode:start]); + + HTMLComment *comment = [[HTMLComment alloc] initWithData:@"data"]; + [[document querySelector:@"#D1"] appendNode: comment]; + [range setStartNode:comment startOffset:2]; + + XCTAssertThrows([range insertNode:end]); +} + +- (void)testInsertNode_TextNodeStart_Begin +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:0]; + HTMLNode *end = [document querySelector:@"#P1"].lastChild; + [range setEndNode:end endOffset:5]; + + HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div"]; + [div appendNodes:@[ + [[HTMLText alloc] initWithData:@"TEXT"], + [[HTMLElement alloc] initWithTagName:@"a"]]]; + + [range insertNode:div]; + + XCTAssertEqualObjects(BodyOf(document), DoubleQuote(@"
      " + @"

      TEXT
      This is a text

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); +} + +- (void)testInsertNode_TextNodeStart_Middle +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:2]; + HTMLNode *end = [document querySelector:@"#P1"].lastChild; + [range setEndNode:end endOffset:5]; + + HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div"]; + [div appendNodes:@[ + [[HTMLText alloc] initWithData:@"TEXT"], + [[HTMLElement alloc] initWithTagName:@"a"]]]; + + [range insertNode:div]; + + XCTAssertEqualObjects(BodyOf(document), DoubleQuote(@"
      " + @"

      Th

      TEXT
      is is a text

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); +} + +- (void)testInsertNode_NonTextNodeStart_Begin +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"]; + [range setStartNode:start startOffset:0]; + HTMLNode *end = [document querySelector:@"#P1"].lastChild; + [range setEndNode:end endOffset:5]; + + HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div"]; + [div appendNodes:@[ + [[HTMLText alloc] initWithData:@"TEXT"], + [[HTMLElement alloc] initWithTagName:@"a"]]]; + + [range insertNode:div]; + + XCTAssertEqualObjects(BodyOf(document), DoubleQuote(@"
      " + @"

      TEXT
      This is a text

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); +} + +- (void)testInsertNode_NonTextNodeStart_Middle +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"]; + [range setStartNode:start startOffset:2]; + HTMLNode *end = [document querySelector:@"#P1"].lastChild; + [range setEndNode:end endOffset:5]; + + HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div"]; + [div appendNodes:@[ + [[HTMLText alloc] initWithData:@"TEXT"], + [[HTMLElement alloc] initWithTagName:@"a"]]]; + + [range insertNode:div]; + + XCTAssertEqualObjects(BodyOf(document), DoubleQuote(@"
      " + @"

      This is a

      TEXT
      text

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); +} + +- (void)testInsertNode_NonTextNodeStart_DifferentParents +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#D1"]; + [range setStartNode:start startOffset:1]; + HTMLNode *end = [document querySelector:@"#P3"].firstChild; + [range setEndNode:end endOffset:4]; + + HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div"]; + [div appendNodes:@[ + [[HTMLText alloc] initWithData:@"TEXT"], + [[HTMLElement alloc] initWithTagName:@"a"]]]; + + [range insertNode:div]; + + XCTAssertEqualObjects(BodyOf(document), DoubleQuote(@"
      " + @"

      This is a text

      TEXT

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); +} + +- (void)testSurroundContents_InvalidNode +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:2]; + HTMLNode *end = [document querySelector:@"#P1"].lastChild; + [range setEndNode:end endOffset:5]; + + XCTAssertThrows([range surroundContents:[HTMLDocumentType new]]); + XCTAssertThrows([range surroundContents:[HTMLDocument new]]); + XCTAssertThrows([range surroundContents:[[HTMLDocumentFragment alloc] initWithDocument:document]]); +} + +- (void)testSurroundContents_PartiallySelectedAncestors +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:2]; + HTMLNode *end = [document querySelector:@"#P3"].lastChild; + [range setEndNode:end endOffset:3]; + + HTMLElement *span = [[HTMLElement alloc] initWithTagName:@"span"]; + XCTAssertThrows([range surroundContents:span]); + + start = [document querySelector:@"#D1"]; + [range setStartNode:start startOffset:0]; + end = [document querySelector:@"#D2"]; + [range setEndNode:end endOffset:1]; + + XCTAssertThrows([range surroundContents:span]); +} + +- (void)testSurroundContents_TextNodes +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:2]; + HTMLNode *end = [document querySelector:@"#P1"].lastChild; + [range setEndNode:end endOffset:3]; + + HTMLElement *span = [[HTMLElement alloc] initWithTagName:@"span"]; + [range surroundContents:span]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      This is a text

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); +} + +- (void)testSurroundContents_NonTextNodes +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"]; + [range setStartNode:start startOffset:0]; + HTMLNode *end = [document querySelector:@"#P1"]; + [range setEndNode:end endOffset:2]; + + HTMLElement *span = [[HTMLElement alloc] initWithTagName:@"span"]; + [range surroundContents:span]; + + XCTAssertEqualObjects(BodyOf(document), InnerHTML( + @"
      " + @"

      This is a text

      Hello

      " + @"

      World

      " + @"

      Another text

      " + @"
      ")); +} + +#pragma mark - Stringifier + +- (void)testRangeStringifier +{ + HTMLDocument *document = self.editingDocument; + HTMLRange *range = [[HTMLRange alloc] initWithDowcument:document]; + + HTMLNode *start = [document querySelector:@"#P1"].firstChild; + [range setStartNode:start startOffset:2]; + HTMLNode *end = [document querySelector:@"#P1"].lastChild; + [range setEndNode:end endOffset:3]; + XCTAssertEqualObjects([range textContent], @"is is a te"); + + start = [document querySelector:@"#P1"]; + [range setStartNode:start startOffset:0]; + end = [document querySelector:@"#P1"]; + [range setEndNode:end endOffset:2]; + XCTAssertEqualObjects([range textContent], @"This is a"); + + start = [document querySelector:@"#D1"]; + [range setStartNode:start startOffset:0]; + end = [document querySelector:@"#D1"]; + [range setEndNode:end endOffset:1]; + XCTAssertEqualObjects([range textContent], @"This is a text"); + + start = [document querySelector:@"#D1"]; + [range setStartNode:start startOffset:0]; + end = [document querySelector:@"#D1"]; + [range setEndNode:end endOffset:2]; + XCTAssertEqualObjects([range textContent], @"This is a textHello"); + + start = [document querySelector:@"#D1"]; + [range setStartNode:start startOffset:0]; + end = [document querySelector:@"#D2"]; + [range setEndNode:end endOffset:0]; + XCTAssertEqualObjects([range textContent], @"This is a textHelloWorld"); + + start = [document querySelector:@"#D1"]; + [range setStartNode:start startOffset:0]; + end = [document querySelector:@"#D2"]; + [range setEndNode:end endOffset:1]; + XCTAssertEqualObjects([range textContent], @"This is a textHelloWorldAnother text"); +} + +@end diff --git a/Tests/HTMLKitStringCategoryTests.m b/Tests/HTMLStringCategoryTests.m similarity index 100% rename from Tests/HTMLKitStringCategoryTests.m rename to Tests/HTMLStringCategoryTests.m diff --git a/Tests/html5lib-tests b/Tests/html5lib-tests index c305da7..50568b5 160000 --- a/Tests/html5lib-tests +++ b/Tests/html5lib-tests @@ -1 +1 @@ -Subproject commit c305da74fae50fb018870de7a042da36c1a93b65 +Subproject commit 50568b5664d6b034b752310603b2b664d045ad0f