Compare commits
248 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 70eeecbf67 | |||
| 918c59e5ff | |||
| 2a641ddacd | |||
| 91d1f85c1b | |||
| 85bd39963f | |||
| 7cbfdb9c78 | |||
| d756fc4298 | |||
| 0fe6b7c42d | |||
| 7c2335184e | |||
| 0c6084ba73 | |||
| 5201860073 | |||
| cf21b88eb1 | |||
| 7617fceff1 | |||
| 9dd3c70fc3 | |||
| fe2a441171 | |||
| 7c32b5f219 | |||
| bf0d8560b0 | |||
| ebc6f7a097 | |||
| d29a70dfec | |||
| b8f56ca97d | |||
| 0f954d5d56 | |||
| ac5089d69b | |||
| c9c36397e0 | |||
| 923e271429 | |||
| 8546168324 | |||
| da765169fa | |||
| ec6d03ee57 | |||
| 7944549bd7 | |||
| bb6a97984b | |||
| 0aff9cf787 | |||
| b7bf9d2b04 | |||
| 12af838fa2 | |||
| 14b8b5390f | |||
| a5599c4163 | |||
| c0ac25645d | |||
| fa431550d7 | |||
| 70c6aee814 | |||
| 7309b97b92 | |||
| 7ffb8e5ca0 | |||
| 682b19c242 | |||
| 16ed846e65 | |||
| 820b3ef091 | |||
| fb7ea25b1f | |||
| 5302c16600 | |||
| 2dfdeb22ca | |||
| 135715b606 | |||
| f8e6a39171 | |||
| 20cf2dc4d0 | |||
| 0921ebad8a | |||
| c63e2cb8fd | |||
| 3deeb0138f | |||
| b358a08f30 | |||
| 84960e71ee | |||
| 7b028a2819 | |||
| 5aa77eac9b | |||
| c1cd1c0831 | |||
| 1101556cfe | |||
| 839a724432 | |||
| 300705cb63 | |||
| 1963c010f9 | |||
| 89ef6bc101 | |||
| 9069923e82 | |||
| c16c647394 | |||
| 27b8eaac67 | |||
| 956c46ec45 | |||
| cb7e5d519d | |||
| 4e1959bd81 | |||
| f0d9817c68 | |||
| fee2d14ffc | |||
| 4291840a53 | |||
| 4b7c260738 | |||
| 4a41b89ecf | |||
| d4ff2e3869 | |||
| 6dee6c1e74 | |||
| 20ebd8c603 | |||
| c002299247 | |||
| bb6d7ccc67 | |||
| bf655c7ea2 | |||
| 6cd03b4eed | |||
| cc0734e470 | |||
| eca6098361 | |||
| 2cd5098d8a | |||
| 514d23f7a0 | |||
| 2446da3a6d | |||
| 644c180f81 | |||
| 47ec0867a8 | |||
| 1cd1a915d3 | |||
| 8379cee44f | |||
| 4094f51458 | |||
| 57c931aece | |||
| 76b379448b | |||
| 49bdfa018e | |||
| 746ef2ea3b | |||
| 653f6cdf7e | |||
| d9670cddf4 | |||
| 30389c5010 | |||
| 14dfc0b854 | |||
| b693a60358 | |||
| d35e6c4d91 | |||
| c353512a65 | |||
| aaee85909b | |||
| 8d3f24ef8b | |||
| b652935739 | |||
| 601c83a51f | |||
| 774ac35bbb | |||
| 9651d12fd0 | |||
| 0899a4013d | |||
| 22de41d912 | |||
| 2f47e733bd | |||
| 36f5df015a | |||
| bbc5467b33 | |||
| 7ef19f5e20 | |||
| a102f5cd7d | |||
| 7a40e8bafd | |||
| 40b057b569 | |||
| a7ace9929f | |||
| 32b674f786 | |||
| e9e3f04136 | |||
| 44f1bc6946 | |||
| ef36e674a4 | |||
| b8f1123b86 | |||
| 17a2e375e0 | |||
| b5470a61c3 | |||
| fb60759492 | |||
| f21cbf2a5e | |||
| 50c48e589a | |||
| bb9987636b | |||
| 60b086f72e | |||
| f1fd0f3ef6 | |||
| 5bb7122d7e | |||
| 356709a096 | |||
| 963a2b9c09 | |||
| 9926179a62 | |||
| cb9886cbf7 | |||
| 717f62a5f1 | |||
| f5476e0c4c | |||
| b9099a26ed | |||
| b57e27137a | |||
| dbe9c575a1 | |||
| 17961b94a4 | |||
| d6d1e77471 | |||
| 987d37e042 | |||
| 6d7e9bca1b | |||
| 0b8b18b675 | |||
| 325ca47587 | |||
| 05c6b9d294 | |||
| 76753b1d95 | |||
| daf71a01f3 | |||
| da2344e5cf | |||
| 049d7b7148 | |||
| c727ce6643 | |||
| 1a5238863a | |||
| 46e36e9c24 | |||
| 1919992b0e | |||
| 28ecfb0278 | |||
| 65666a13c0 | |||
| e5237a68c4 | |||
| ea63b38379 | |||
| 542bc26ff9 | |||
| 718e43ff9c | |||
| 472d59546a | |||
| bdaddc0938 | |||
| a38d1e6484 | |||
| f53ce3464d | |||
| bc94f91c6e | |||
| 1e5517f403 | |||
| 925848404b | |||
| fe46b106b2 | |||
| c7edbd3e14 | |||
| f1b1f523b8 | |||
| 51c94d49bd | |||
| 395ce5e981 | |||
| e48ccf42ca | |||
| 0ac49f2a0f | |||
| 07c72f65d9 | |||
| 8602ad5a7b | |||
| 12fc972602 | |||
| b66f5264ba | |||
| 1042571c57 | |||
| c294802199 | |||
| ea17eeef9d | |||
| 9795172c9c | |||
| 0319536a6c | |||
| 3f74fcaa7b | |||
| 78b3069a25 | |||
| dfd01b1908 | |||
| bdacc629eb | |||
| 734c185137 | |||
| de6c215550 | |||
| b7b29d2af5 | |||
| 15acfa06f6 | |||
| 7506ed8cae | |||
| 3898ae243c | |||
| 6da3f7bd49 | |||
| fb04d49693 | |||
| 4d132af99f | |||
| e4a0669cf2 | |||
| e60fe36e25 | |||
| 1f6f9d843e | |||
| 5c2f6527a7 | |||
| e4b57f3333 | |||
| 6be1bd702f | |||
| ef672e1dbb | |||
| 0d95ba33d1 | |||
| 9df89043d3 | |||
| 6fdc7ff85b | |||
| c3c7c34310 | |||
| fb2952062d | |||
| 49799f8bcd | |||
| 9bd9dfbf6b | |||
| 8f0a6d133e | |||
| cfb1707fc4 | |||
| 733780091d | |||
| 8391a01147 | |||
| 6ed0238a89 | |||
| 80299ad9e0 | |||
| ffc9e629f2 | |||
| 394a1a53c4 | |||
| 14289f96e5 | |||
| c57117670f | |||
| a08fb84be9 | |||
| 9fd5d34244 | |||
| 2fb871a7ff | |||
| 46072a09cc | |||
| de2d35fbe6 | |||
| 6ae00f40e1 | |||
| 76a7930e7d | |||
| 8dc5d0ea50 | |||
| 0e9956efc0 | |||
| d5888e68a7 | |||
| 02177e7fac | |||
| af76666f3c | |||
| 73dc32aae9 | |||
| 64cba55677 | |||
| 214722c0bf | |||
| b8592630d0 | |||
| a641c64f74 | |||
| e20531925a | |||
| 024ab12242 | |||
| c2a11e6f97 | |||
| 800336d317 | |||
| a751cec215 | |||
| 87a476e523 | |||
| 7e42fd1e08 | |||
| 7d1b94219d | |||
| 29bd89b808 | |||
| 45a8e25028 | |||
| 33bb1e39a4 |
@@ -0,0 +1,5 @@
|
||||
codecov:
|
||||
branch: develop
|
||||
|
||||
ignore:
|
||||
- "Tests/"
|
||||
+3
-1
@@ -40,5 +40,7 @@ Pods/
|
||||
#
|
||||
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
||||
Carthage/Checkouts
|
||||
Carthage/Build
|
||||
|
||||
Carthage/Build
|
||||
# Jazzy
|
||||
docs/
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
[submodule "HTMLKitTests/html5lib-tests"]
|
||||
[submodule "html5lib-tests"]
|
||||
path = Tests/html5lib-tests
|
||||
url = https://github.com/html5lib/html5lib-tests.git
|
||||
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
module: HTMLKit
|
||||
module_version: 3.0.0
|
||||
author: Iskandar Abudiab
|
||||
author_url: https://twitter.com/iabudiab
|
||||
github_url: https://github.com/iabudiab/HTMLKit
|
||||
github_file_prefix: https://github.com/iabudiab/HTMLKit/tree/master
|
||||
output: docs
|
||||
umbrella_header: Sources/include/HTMLKit.h
|
||||
clean: true
|
||||
objc: true
|
||||
skip_undocumented: true
|
||||
|
||||
custom_categories:
|
||||
- name: Parsing
|
||||
children:
|
||||
- HTMLParser
|
||||
|
||||
- name: DOM
|
||||
children:
|
||||
- HTMLNamespace
|
||||
- HTMLNode
|
||||
- HTMLNodeType
|
||||
- HTMLElement
|
||||
- HTMLDocument
|
||||
- HTMLDocumentReadyState
|
||||
- HTMLQuirksMode
|
||||
- HTMLDocumentType
|
||||
- HTMLDocumentFragment
|
||||
- HTMLDocumentPosition
|
||||
- HTMLCharacterData
|
||||
- HTMLText
|
||||
- HTMLComment
|
||||
- HTMLTemplate
|
||||
- HTMLDOMTokenList
|
||||
- HTMLRange
|
||||
|
||||
- name: Iteration & Filtering
|
||||
children:
|
||||
- HTMLNodeIterator
|
||||
- HTMLNodeFilter
|
||||
- HTMLNodeFilterShowOptions
|
||||
- HTMLNodeFilterValue
|
||||
- HTMLNodeFilterBlock
|
||||
- HTMLSelectorNodeFilter
|
||||
- HTMLTreeWalker
|
||||
|
||||
- name: Structures
|
||||
children:
|
||||
- HTMLOrderedDictionary
|
||||
- CSSNthExpression
|
||||
|
||||
- name: CSS Selectors Implementation
|
||||
children:
|
||||
- CSSSelector
|
||||
- CSSSelectorParser
|
||||
- CSSTypeSelector
|
||||
- CSSAttributeSelector
|
||||
- CSSNthExpressionParser
|
||||
- CSSNthExpressionSelector
|
||||
- CSSPseudoClassSelector
|
||||
- CSSPseudoFunctionSelector
|
||||
- CSSSelectorBlock
|
||||
- CSSCombinatorSelector
|
||||
- CSSCompoundSelector
|
||||
|
||||
- name: Typed Selectors and Extensions
|
||||
children:
|
||||
- adjacentSiblingSelector
|
||||
- allOf
|
||||
- anyOf
|
||||
- attributeSelector
|
||||
- buttonSelector
|
||||
- checkboxSelector
|
||||
- checkedSelector
|
||||
- childOfElementSelector
|
||||
- classSelector
|
||||
- descendantOfElementSelector
|
||||
- disabledSelector
|
||||
- emptySelector
|
||||
- enabledSelector
|
||||
- eqSelector
|
||||
- evenSlector
|
||||
- fileSelector
|
||||
- firstChildSelector
|
||||
- firstOfTypeSelector
|
||||
- generalSiblingSelector
|
||||
- gtSelector
|
||||
- has
|
||||
- hasAttributeSelector
|
||||
- headerSelector
|
||||
- idSelector
|
||||
- imageSelector
|
||||
- inputSelector
|
||||
- lastChildSelector
|
||||
- lastOfTypeSelector
|
||||
- linkSelector
|
||||
- ltSelector
|
||||
- namedBlockSelector
|
||||
- namedPseudoSelector
|
||||
- not
|
||||
- nthChildSelector
|
||||
- nthLastChildSelector
|
||||
- nthLastOfTypeSelector
|
||||
- nthOfTypeSelector
|
||||
- oddSelector
|
||||
- onlyChildSelector
|
||||
- onlyOfTypeSelector
|
||||
- optionalSelector
|
||||
- parentSelector
|
||||
- passwordSelector
|
||||
- radioSelector
|
||||
- requiredSelector
|
||||
- resetSelector
|
||||
- rootSelector
|
||||
- submitSelector
|
||||
- textSelector
|
||||
- typeSelector
|
||||
- universalSelector
|
||||
|
||||
- name: Categories
|
||||
children:
|
||||
- NSCharacterSet(HTMLKit)
|
||||
- NSString(HTMLKit)
|
||||
+20
-17
@@ -1,5 +1,5 @@
|
||||
language: objective-c
|
||||
osx_image: xcode7.3
|
||||
osx_image: xcode9
|
||||
|
||||
branches:
|
||||
except:
|
||||
@@ -14,29 +14,32 @@ env:
|
||||
- LANG=en_US.UTF-8
|
||||
- WORKSPACE=HTMLKit.xcworkspace
|
||||
- IOS_FRAMEWORK_SCHEME=HTMLKit-iOS
|
||||
- OSX_FRAMEWORK_SCHEME=HTMLKit-OSX
|
||||
- MACOS_FRAMEWORK_SCHEME=HTMLKit-macOS
|
||||
- WATCHOS_FRAMEWORK_SCHEME="HTMLKit-watchOS"
|
||||
- TVOS_FRAMEWORK_SCHEME="HTMLKit-tvOS"
|
||||
- IOS_SDK=iphonesimulator9.3
|
||||
- OSX_SDK=macosx10.11
|
||||
- WATCHOS_SDK=watchsimulator2.2
|
||||
- TVOS_SDK=appletvsimulator9.2
|
||||
- IOS_SDK=iphonesimulator11.0
|
||||
- MACOS_SDK=macosx10.13
|
||||
- WATCHOS_SDK=watchsimulator4.0
|
||||
- TVOS_SDK=appletvsimulator11.0
|
||||
matrix:
|
||||
- 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="arch=x86_64" SIMULATOR="" SCHEME="$OSX_FRAMEWORK_SCHEME" SDK="$OSX_SDK"
|
||||
- DESTINATION="OS=2.2,name=Apple Watch - 42mm" SIMULATOR="Apple Watch - 42mm (2.2)" SCHEME="$WATCHOS_FRAMEWORK_SCHEME" SDK="$WATCHOS_SDK"
|
||||
- DESTINATION="OS=9.2,name=Apple TV 1080p" SIMULATOR="Apple TV 1080p (9.2)" SCHEME="$TVOS_FRAMEWORK_SCHEME" SDK="$TVOS_SDK"
|
||||
- DESTINATION="arch=x86_64" SCHEME="$MACOS_FRAMEWORK_SCHEME" SDK="$MACOS_SDK"
|
||||
- DESTINATION="OS=9.3,name=iPhone 6s Plus" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
|
||||
- DESTINATION="OS=10.3.1,name=iPhone 7 Plus" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
|
||||
- DESTINATION="OS=11.0,name=iPhone X" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
|
||||
- DESTINATION="OS=3.2,name=Apple Watch Series 2 - 42mm" SCHEME="$WATCHOS_FRAMEWORK_SCHEME" SDK="$WATCHOS_SDK"
|
||||
- DESTINATION="OS=4.0,name=Apple Watch Series 3 - 42mm" SCHEME="$WATCHOS_FRAMEWORK_SCHEME" SDK="$WATCHOS_SDK"
|
||||
- DESTINATION="OS=10.2,name=Apple TV 1080p" SCHEME="$TVOS_FRAMEWORK_SCHEME" SDK="$TVOS_SDK"
|
||||
- DESTINATION="OS=11.0,name=Apple TV 4K" SCHEME="$TVOS_FRAMEWORK_SCHEME" SDK="$TVOS_SDK"
|
||||
|
||||
script:
|
||||
- set -o pipefail
|
||||
- xcodebuild -version
|
||||
- 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 GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES test | xcpretty -c;
|
||||
fi
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
|
||||
+207
-7
@@ -1,5 +1,211 @@
|
||||
# Change Log
|
||||
|
||||
## [3.0.0](https://github.com/iabudiab/HTMLKit/releases/tag/3.0.0)
|
||||
|
||||
Released on 2019.03.28
|
||||
|
||||
### Breaking Change
|
||||
|
||||
- Introduce prefix for `NSString` and `NSCharacterSet` categories to prevent collision with existing code (issue #35)
|
||||
|
||||
|
||||
## [2.1.5](https://github.com/iabudiab/HTMLKit/releases/tag/2.1.5)
|
||||
|
||||
Released on 2018.07.16
|
||||
|
||||
### Fixes
|
||||
|
||||
- Parser would handle foreign attributes incorrectly (issue #30)
|
||||
|
||||
|
||||
## [2.1.4](https://github.com/iabudiab/HTMLKit/releases/tag/2.1.4)
|
||||
|
||||
Released on 2018.05.01
|
||||
|
||||
### Fixes
|
||||
|
||||
- `gt(n)`, `lt(n)` and `eq(n)` selectors would select wrong elements for the zero-index (issue #25)
|
||||
|
||||
|
||||
## [2.1.3](https://github.com/iabudiab/HTMLKit/releases/tag/2.1.3)
|
||||
|
||||
Released on 2018.03.21
|
||||
|
||||
### Fixes
|
||||
|
||||
- `HTMLElement` clone would return an immutable dictionary for attributes (issue #20)
|
||||
- Fixed by @CRivlaldo in PR #24
|
||||
- `HTMLNodeFilterBlock` would behave differently on simulator and device (issue #22)
|
||||
- Fixed by @CRivlaldo in PR #23
|
||||
|
||||
|
||||
## [2.1.2](https://github.com/iabudiab/HTMLKit/releases/tag/2.1.2)
|
||||
|
||||
Released on 2017.11.6
|
||||
|
||||
### Fixes
|
||||
|
||||
- `HTMLText` serialization (issue #16)
|
||||
- `HTMLElement` attribute value serialization (issue #17)
|
||||
|
||||
|
||||
## [2.1.1](https://github.com/iabudiab/HTMLKit/releases/tag/2.1.1)
|
||||
|
||||
Released on 2017.10.13
|
||||
|
||||
### Hotfix
|
||||
|
||||
- Fixed documentation comments
|
||||
- Should fix CocoaDocs generation and percentage
|
||||
|
||||
|
||||
## [2.1.0](https://github.com/iabudiab/HTMLKit/releases/tag/2.1.0)
|
||||
|
||||
Released on 2017.10.12
|
||||
|
||||
### Added
|
||||
|
||||
- Standarized tokenizer error codes:
|
||||
- [whatwg/html#2701](https://github.com/whatwg/html/pull/2701)
|
||||
- [html5lib/html5lib-tests#92](https://github.com/html5lib/html5lib-tests/pull/92)
|
||||
|
||||
### Updated
|
||||
|
||||
- Project for Xcode 9
|
||||
- Travis config for iOS 11.0, macOS 10.13, tvOS 11.0 and watchOS 4.0
|
||||
- Updated HTML5Lib-Tests submodule (cbafeba)
|
||||
|
||||
## [2.0.6](https://github.com/iabudiab/HTMLKit/releases/tag/2.0.6)
|
||||
|
||||
Released on 2017.05.02
|
||||
|
||||
### Added
|
||||
|
||||
- Memory consumption improvements (issue #10)
|
||||
- Allocate `childNodes` collection in `HTMLNode` only when inserting child nodes
|
||||
- Replace `NSStringFromSelector` calls with constants in `HTMLNode` validations
|
||||
- Improve `reverseObjectEnumerator` usage while parsing HTML
|
||||
- Rewrite internal logic of the `HTMLStackOfOpenElements` to prevent excessive allocations
|
||||
|
||||
|
||||
## [2.0.5](https://github.com/iabudiab/HTMLKit/releases/tag/2.0.5)
|
||||
|
||||
Released on 2017.04.19
|
||||
|
||||
### Fixed
|
||||
|
||||
- Xcode 8.3 issue with modulemaps
|
||||
- Temporary workaround (renamed modulemap file)
|
||||
- Memory Leaks in `CSSInputStream`
|
||||
|
||||
### Added
|
||||
|
||||
- Minor memory consumption improvements
|
||||
- Collections for child nodes or attributes of HTML Nodes or Elements are allocated lazily
|
||||
- Underyling data string of `CharacterData` is allocated on first access
|
||||
- Autorelease pool for the main `HTMLTokenizer` loop
|
||||
|
||||
|
||||
## [2.0.4](https://github.com/iabudiab/HTMLKit/releases/tag/2.0.4)
|
||||
|
||||
Released on 2017.04.2
|
||||
|
||||
### Fixed
|
||||
|
||||
- Testing with Swift 3.1
|
||||
- Fixed by @tali in PR #8
|
||||
|
||||
### Deprecated
|
||||
|
||||
- `HTMLRange` initializers with typo
|
||||
- `initWithDowcument:startContainer:startOffset:endContainer:endOffset:`
|
||||
|
||||
|
||||
## [2.0.3](https://github.com/iabudiab/HTMLKit/releases/tag/2.0.3)
|
||||
|
||||
Released on 2017.03.6
|
||||
|
||||
### Fixed
|
||||
|
||||
- Compilation for Swift 3.1
|
||||
- Fixed by @tali in PR #6
|
||||
|
||||
|
||||
## [2.0.2](https://github.com/iabudiab/HTMLKit/releases/tag/2.0.2)
|
||||
|
||||
Released on 2017.02.26
|
||||
|
||||
### Fixed
|
||||
|
||||
- Retain cycles in `HTMLNodeIterator` (issue #4)
|
||||
- Retain cycles in `HTMLRange` (issue #5)
|
||||
- The layout of `HTMLKit` tests module for Swift Package Manager
|
||||
|
||||
|
||||
## [2.0.1](https://github.com/iabudiab/HTMLKit/releases/tag/2.0.1)
|
||||
|
||||
Released on 2017.02.20
|
||||
|
||||
### Hotifx
|
||||
|
||||
- Set `INSTALL_PATH` and `DYLIB_INSTALL_NAME_BASE` to `@rpath` for macOS target
|
||||
- This fixes embedding `HTMLKit` in a Cocoa application
|
||||
|
||||
|
||||
## [2.0.0](https://github.com/iabudiab/HTMLKit/releases/tag/2.0.0)
|
||||
|
||||
Released on 2017.02.11
|
||||
|
||||
### Spec Change
|
||||
|
||||
- Make `<menuitem>` parse like an unkonwn element. See:
|
||||
- [whatwg/html#2319](https://github.com/whatwg/html/pull/2319)
|
||||
- [html5lib/html5lib-tests#88](https://github.com/html5lib/html5lib-tests/pull/88)
|
||||
|
||||
### Updated
|
||||
|
||||
- Updated HTML5Lib-Tests submodule (13f1805)
|
||||
|
||||
|
||||
## [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
|
||||
|
||||
### Added
|
||||
|
||||
- Jazzy configuration file
|
||||
- Example HTMLKit project
|
||||
|
||||
### Updated
|
||||
|
||||
- Project for Xcode 8
|
||||
- Playground syntax for Swift 3
|
||||
- Travis config for iOS 10.0, macOS 10.12, tvOS 10.0 and watchOS 3.0
|
||||
- Deployment targets to macOS 10.9, iOS 9.0, tvOS 9.0 and watchOS 2.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- Nullability annotation in `CSSSelectorParser` class
|
||||
- Missing lightweight generics in `HTMLParser`, `HTMLNode` & `HTMLElement`
|
||||
|
||||
## [0.9.4](https://github.com/iabudiab/HTMLKit/releases/tag/0.9.4)
|
||||
|
||||
Released on 2016.09.03
|
||||
@@ -40,16 +246,12 @@ This release passes all tokenizer and tree construction html5lib-tests as of 201
|
||||
- `<isindex>` 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
|
||||
|
||||
- `<isindex>` 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
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
- Parser now checks the qualified name instead of the local name when handling elements in the `MathML` and `SVG` namespaces
|
||||
|
||||
|
||||
## [0.9.1](https://github.com/iabudiab/HTMLKit/releases/tag/0.9.1)
|
||||
|
||||
Released on 2016.01.29
|
||||
@@ -59,7 +261,6 @@ Released on 2016.01.29
|
||||
- Travis-CI integration.
|
||||
- CocoaPods spec.
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
- Warnings are treated as errors.
|
||||
@@ -91,7 +292,6 @@ This is the first public release of `HTMLKit`.
|
||||
- Unused namespaces.
|
||||
- Historical node types.
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
- `lt`, `gt` & `eq` CSS Selectors method declarations.
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
629A63CD1D9AFE0E0089679F /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 629A63CC1D9AFE0E0089679F /* main.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
629A63C71D9AFE0E0089679F /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = /usr/share/man/man1/;
|
||||
dstSubfolderSpec = 0;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
629A63C91D9AFE0E0089679F /* HTMLKitExample */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = HTMLKitExample; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
629A63CC1D9AFE0E0089679F /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
629A63C61D9AFE0E0089679F /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
629A63C01D9AFE0E0089679F = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
629A63CB1D9AFE0E0089679F /* HTMLKitExample */,
|
||||
629A63CA1D9AFE0E0089679F /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
629A63CA1D9AFE0E0089679F /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
629A63C91D9AFE0E0089679F /* HTMLKitExample */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
629A63CB1D9AFE0E0089679F /* HTMLKitExample */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
629A63CC1D9AFE0E0089679F /* main.swift */,
|
||||
);
|
||||
path = HTMLKitExample;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
629A63C81D9AFE0E0089679F /* HTMLKitExample */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 629A63D01D9AFE0E0089679F /* Build configuration list for PBXNativeTarget "HTMLKitExample" */;
|
||||
buildPhases = (
|
||||
629A63C51D9AFE0E0089679F /* Sources */,
|
||||
629A63C61D9AFE0E0089679F /* Frameworks */,
|
||||
629A63C71D9AFE0E0089679F /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = HTMLKitExample;
|
||||
productName = HTMLKitExample;
|
||||
productReference = 629A63C91D9AFE0E0089679F /* HTMLKitExample */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
629A63C11D9AFE0E0089679F /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0800;
|
||||
LastUpgradeCheck = 0900;
|
||||
ORGANIZATIONNAME = iabudiab;
|
||||
TargetAttributes = {
|
||||
629A63C81D9AFE0E0089679F = {
|
||||
CreatedOnToolsVersion = 8.0;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 629A63C41D9AFE0E0089679F /* Build configuration list for PBXProject "HTMLKitExample" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = 629A63C01D9AFE0E0089679F;
|
||||
productRefGroup = 629A63CA1D9AFE0E0089679F /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
629A63C81D9AFE0E0089679F /* HTMLKitExample */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
629A63C51D9AFE0E0089679F /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
629A63CD1D9AFE0E0089679F /* main.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
629A63CE1D9AFE0E0089679F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVES = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
629A63CF1D9AFE0E0089679F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVES = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
629A63D11D9AFE0E0089679F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
629A63D21D9AFE0E0089679F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
629A63C41D9AFE0E0089679F /* Build configuration list for PBXProject "HTMLKitExample" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
629A63CE1D9AFE0E0089679F /* Debug */,
|
||||
629A63CF1D9AFE0E0089679F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
629A63D01D9AFE0E0089679F /* Build configuration list for PBXNativeTarget "HTMLKitExample" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
629A63D11D9AFE0E0089679F /* Debug */,
|
||||
629A63D21D9AFE0E0089679F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 629A63C11D9AFE0E0089679F /* Project object */;
|
||||
}
|
||||
Generated
+7
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:HTMLKitExample.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0900"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "629A63C81D9AFE0E0089679F"
|
||||
BuildableName = "HTMLKitExample"
|
||||
BlueprintName = "HTMLKitExample"
|
||||
ReferencedContainer = "container:HTMLKitExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "629A63C81D9AFE0E0089679F"
|
||||
BuildableName = "HTMLKitExample"
|
||||
BlueprintName = "HTMLKitExample"
|
||||
ReferencedContainer = "container:HTMLKitExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "629A63C81D9AFE0E0089679F"
|
||||
BuildableName = "HTMLKitExample"
|
||||
BlueprintName = "HTMLKitExample"
|
||||
ReferencedContainer = "container:HTMLKitExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "629A63C81D9AFE0E0089679F"
|
||||
BuildableName = "HTMLKitExample"
|
||||
BlueprintName = "HTMLKitExample"
|
||||
ReferencedContainer = "container:HTMLKitExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// main.swift
|
||||
// HTMLKitExample
|
||||
//
|
||||
// Created by Iska on 27/09/16.
|
||||
// Copyright © 2016 iabudiab. All rights reserved.
|
||||
//
|
||||
|
||||
import HTMLKit
|
||||
|
||||
// Simple scraper that is able to load a page, query via CSS Selectors, and following links
|
||||
class Scraper {
|
||||
|
||||
enum ScrapingError: Error {
|
||||
case DocumentNotLoaded
|
||||
case ElementNotFound(String)
|
||||
case InvalidAnchorUrl(String)
|
||||
case CouldNotLoadPage(URL)
|
||||
}
|
||||
|
||||
private var url: URL
|
||||
private(set) var document: HTMLDocument?
|
||||
|
||||
init(url: URL) {
|
||||
self.url = url
|
||||
self.document = nil
|
||||
}
|
||||
|
||||
func load() throws {
|
||||
try loadDocument(at: url)
|
||||
}
|
||||
|
||||
func listElements(matching selector: CSSSelector) throws -> [HTMLElement] {
|
||||
guard let document = document else {
|
||||
throw ScrapingError.DocumentNotLoaded
|
||||
}
|
||||
|
||||
return document.elements(matching: selector)
|
||||
}
|
||||
|
||||
func followLink(matchingSelector selector: CSSSelector) throws {
|
||||
guard let document = document else {
|
||||
throw ScrapingError.DocumentNotLoaded
|
||||
}
|
||||
|
||||
guard let link = document.firstElement(matching: selector) else {
|
||||
throw ScrapingError.ElementNotFound(selector.debugDescription)
|
||||
}
|
||||
|
||||
guard let targetUrl = URL(string: link["href"], relativeTo: url) else {
|
||||
throw ScrapingError.InvalidAnchorUrl(link["href"])
|
||||
}
|
||||
|
||||
try loadDocument(at: targetUrl)
|
||||
}
|
||||
|
||||
private func loadDocument(at url: URL) throws {
|
||||
guard let content = try? String(contentsOf: url) else {
|
||||
throw ScrapingError.CouldNotLoadPage(url)
|
||||
}
|
||||
document = HTMLDocument(string: content)
|
||||
}
|
||||
}
|
||||
|
||||
// A custom block-based selector, that matches only elements having the given text content:
|
||||
// i.e. textContentSelector("Hello") will match <p>Hello</p> and <a href='example.com'>Hello</a>
|
||||
// but wont match <div>World</div> or <p>Hello there</p>
|
||||
func textContentSelector(text: String) -> CSSSelector {
|
||||
return namedBlockSelector("[@textContent='\(text)']") { (element) -> Bool in
|
||||
return element.textContent == text
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to create a typed-selector matching an anchor element that has the given
|
||||
// text content.
|
||||
func anchorElement(havingContent: String) -> CSSSelector {
|
||||
return allOf(
|
||||
[
|
||||
typeSelector("a"),
|
||||
textContentSelector(text: havingContent)
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
// Helper function to print the content of a github repository file content
|
||||
func printRepositoryFile(element: HTMLElement) {
|
||||
|
||||
// A node iterator filter that iterates only <td> elements of class "content" i.e. <td class='content'>
|
||||
let contentIterator = element.nodeIterator(showOptions: .element) { (node) -> HTMLNodeFilterValue in
|
||||
guard let element = node as? HTMLElement else { return .reject }
|
||||
|
||||
if element.tagName == "td" && element["class"] == "content" {
|
||||
return .accept
|
||||
}
|
||||
|
||||
return .reject
|
||||
}
|
||||
|
||||
for td in contentIterator {
|
||||
// The cast is necessary because Swift3 wont import the generics info of the NSEnumerator class
|
||||
// i.e. the nextObject() function alwasy has the following signature 'func nextObject() -> Any?'
|
||||
let title = (td as AnyObject).textContent.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
|
||||
print("- \(title)")
|
||||
}
|
||||
}
|
||||
|
||||
let htmlKitUrl = URL(string: "https://github.com/iabudiab/HTMLKit")!
|
||||
let scraper = Scraper(url: htmlKitUrl)
|
||||
|
||||
do {
|
||||
// Load the page
|
||||
try scraper.load()
|
||||
|
||||
// Parse the selector
|
||||
let repositoryContent = try CSSSelectorParser.parseSelector("[role='main'] .repository-content > .file-wrap > .files tr.js-navigation-item")
|
||||
|
||||
// Query matching elements
|
||||
let files = try scraper.listElements(matching: repositoryContent)
|
||||
|
||||
print("HTMLKit repositroy root:")
|
||||
files.forEach(printRepositoryFile)
|
||||
} catch let error {
|
||||
print(error)
|
||||
}
|
||||
|
||||
do {
|
||||
// Follow some links
|
||||
try scraper.followLink(matchingSelector: anchorElement(havingContent: "Sources"))
|
||||
try scraper.followLink(matchingSelector: anchorElement(havingContent: "HTMLEOFToken.m"))
|
||||
|
||||
// The following selector: "[role='main'] div.file table.js-file-line-container td:nth-child(2)"
|
||||
// can be defined in type-safe manner:
|
||||
let selector = allOf([
|
||||
descendantOfElementSelector(
|
||||
attributeSelector(.exactMatch, "role", "main")
|
||||
),
|
||||
descendantOfElementSelector(
|
||||
allOf([
|
||||
typeSelector("div"),
|
||||
classSelector("file")
|
||||
])
|
||||
),
|
||||
descendantOfElementSelector(
|
||||
allOf([
|
||||
typeSelector("table"),
|
||||
classSelector("js-file-line-container")
|
||||
])
|
||||
),
|
||||
typeSelector("td"),
|
||||
nthChildSelector(
|
||||
CSSNthExpressionMake(0, 2)
|
||||
)
|
||||
])
|
||||
|
||||
// Query matching elements
|
||||
let elements = try scraper.listElements(matching: selector)
|
||||
|
||||
// This will print the source code for the "HTMLEOFToken.m" file under this url:
|
||||
// https://github.com/iabudiab/HTMLKit/blob/master/Sources/HTMLEOFToken.m
|
||||
|
||||
print("\nHTMLEOFToken:")
|
||||
elements.forEach {
|
||||
print($0.textContent)
|
||||
}
|
||||
} catch let error {
|
||||
print(error)
|
||||
}
|
||||
@@ -28,22 +28,22 @@ var nonParagraphChildOfDiv = document.querySelectorAll("div :not(p)")
|
||||
/*:
|
||||
HTMLKit also provides API to create selector instances in a type-safe manner without the need to parse them first. The previous examples would like this:
|
||||
*/
|
||||
paragraphs = document.elementsMatchingSelector(typeSelector("p"))
|
||||
paragraphsOrHeaders = document.elementsMatchingSelector(
|
||||
paragraphs = document.elements(matching: typeSelector("p"))
|
||||
paragraphsOrHeaders = document.elements(matching:
|
||||
anyOf([
|
||||
typeSelector("p"), typeSelector("h1")
|
||||
])
|
||||
)
|
||||
|
||||
hasClassAttribute = document.elementsMatchingSelector(hasAttributeSelector("class"))
|
||||
greetings = document.elementsMatchingSelector(classSelector("greeting"))
|
||||
classNameStartsWith_de = document.elementsMatchingSelector(attributeSelector(.Begins, "class", "de"))
|
||||
hasClassAttribute = document.elements(matching: hasAttributeSelector("class"))
|
||||
greetings = document.elements(matching: classSelector("greeting"))
|
||||
classNameStartsWith_de = document.elements(matching: attributeSelector(.begins, "class", "de"))
|
||||
|
||||
hasAdjacentHeader = document.elementsMatchingSelector(adjacentSiblingSelector(typeSelector("h1")))
|
||||
hasAdjacentHeader = document.elementsMatchingSelector(generalSiblingSelector(typeSelector("h1")))
|
||||
hasAdjacentHeader = document.elementsMatchingSelector(generalSiblingSelector(typeSelector("p")))
|
||||
hasAdjacentHeader = document.elements(matching: adjacentSiblingSelector(typeSelector("h1")))
|
||||
hasAdjacentHeader = document.elements(matching: generalSiblingSelector(typeSelector("h1")))
|
||||
hasAdjacentHeader = document.elements(matching: generalSiblingSelector(typeSelector("p")))
|
||||
|
||||
nonParagraphChildOfDiv = document.elementsMatchingSelector(
|
||||
nonParagraphChildOfDiv = document.elements(matching:
|
||||
allOf([
|
||||
childOfElementSelector(typeSelector("div")),
|
||||
not(typeSelector("p"))
|
||||
@@ -54,17 +54,17 @@ nonParagraphChildOfDiv = document.elementsMatchingSelector(
|
||||
Here are more examples
|
||||
*/
|
||||
|
||||
let firstDivElement = document.firstElementMatchingSelector(typeSelector("div"))!
|
||||
let firstDivElement = document.firstElement(matching: typeSelector("div"))!
|
||||
|
||||
var secondChildOfDiv = firstDivElement.querySelectorAll(":nth-child(2)")
|
||||
var secondOfType = firstDivElement.querySelectorAll(":nth-of-type(2n)")
|
||||
|
||||
secondChildOfDiv = firstDivElement.elementsMatchingSelector(nthChildSelector(CSSNthExpression(an: 0, b: 2)))
|
||||
secondOfType = firstDivElement.elementsMatchingSelector(nthOfTypeSelector(CSSNthExpression(an: 2, b: 0)))
|
||||
secondChildOfDiv = firstDivElement.elements(matching: nthChildSelector(CSSNthExpression(an: 0, b: 2)))
|
||||
secondOfType = firstDivElement.elements(matching: nthOfTypeSelector(CSSNthExpression(an: 2, b: 0)))
|
||||
|
||||
|
||||
var notParagraphAndNotDiv = firstDivElement.querySelectorAll(":not(p):not(div)")
|
||||
notParagraphAndNotDiv = firstDivElement.elementsMatchingSelector(
|
||||
notParagraphAndNotDiv = firstDivElement.elements(matching:
|
||||
allOf([
|
||||
not(typeSelector("p")),
|
||||
not(typeSelector("div"))
|
||||
@@ -77,4 +77,4 @@ One more thing! You can also create your own selectors. You either subclass the
|
||||
let myAwesomeSelector = namedBlockSelector("myAwesomeSelector", { (element) -> Bool in
|
||||
return element.tagName != "p" && element.tagName != "div"
|
||||
})
|
||||
firstDivElement.elementsMatchingSelector(myAwesomeSelector)
|
||||
firstDivElement.elements(matching: myAwesomeSelector)
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Timeline
|
||||
version = "3.0">
|
||||
<TimelineItems>
|
||||
</TimelineItems>
|
||||
</Timeline>
|
||||
@@ -9,7 +9,8 @@ import HTMLKit
|
||||
Given some HTML content
|
||||
*/
|
||||
|
||||
let htmlString = try! String(contentsOfURL: [#FileReference(fileReferenceLiteral: "HTMLKit.html")#])
|
||||
let htmlString = "<div><h1>HTMLKit</h1><p class='greeting'>Hello there!</p><p class='description'>This is a demo of HTMLKit</p></div>"
|
||||
htmlString
|
||||
|
||||
/*:
|
||||
You can parse it using the HTMLParser:
|
||||
|
||||
@@ -3,10 +3,20 @@
|
||||
version = "3.0">
|
||||
<TimelineItems>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=1&CharacterRangeLoc=318&EndingColumnNumber=26&EndingLineNumber=13&StartingColumnNumber=1&StartingLineNumber=13&Timestamp=472578634.909266"
|
||||
documentLocation = "#CharacterRangeLen=0&CharacterRangeLoc=581&EndingColumnNumber=26&EndingLineNumber=11&StartingColumnNumber=1&StartingLineNumber=11&Timestamp=496084829.845787"
|
||||
lockedSize = "{800, 186}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=27&CharacterRangeLoc=393&EndingColumnNumber=28&EndingLineNumber=20&StartingColumnNumber=1&StartingLineNumber=20&Timestamp=496084834.772773"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=18&CharacterRangeLoc=544&EndingColumnNumber=19&EndingLineNumber=27&StartingColumnNumber=1&StartingLineNumber=27&Timestamp=496084834.772773"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
</TimelineItems>
|
||||
</Timeline>
|
||||
|
||||
@@ -18,7 +18,7 @@ You can prase it as a document fragment in a specified context element:
|
||||
let parser = HTMLParser(string: htmlString)
|
||||
|
||||
let tableContext = HTMLElement(tagName: "table")
|
||||
var elements = parser.parseFragmentWithContextElement(tableContext)
|
||||
var elements = parser.parseFragment(withContextElement: tableContext)
|
||||
|
||||
for element in elements {
|
||||
print(element.outerHTML)
|
||||
@@ -29,7 +29,7 @@ The same parser instance can be reusued:
|
||||
*/
|
||||
|
||||
let bodyContext = HTMLElement(tagName: "body")
|
||||
elements = parser.parseFragmentWithContextElement(bodyContext)
|
||||
elements = parser.parseFragment(withContextElement: bodyContext)
|
||||
|
||||
for element in elements {
|
||||
print(element.outerHTML)
|
||||
|
||||
@@ -21,13 +21,13 @@
|
||||
shouldTrackSuperviewWidth = "YES">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=24&CharacterRangeLoc=453&EndingColumnNumber=26&EndingLineNumber=23&StartingColumnNumber=2&StartingLineNumber=23&Timestamp=472578641.006933"
|
||||
documentLocation = "#CharacterRangeLen=24&CharacterRangeLoc=455&EndingColumnNumber=26&EndingLineNumber=23&StartingColumnNumber=2&StartingLineNumber=23&Timestamp=495492881.371878"
|
||||
lockedSize = "{775, 83}"
|
||||
selectedRepresentationIndex = "1"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=24&CharacterRangeLoc=668&EndingColumnNumber=26&EndingLineNumber=34&StartingColumnNumber=2&StartingLineNumber=34&Timestamp=472578641.007156"
|
||||
documentLocation = "#CharacterRangeLen=24&CharacterRangeLoc=672&EndingColumnNumber=26&EndingLineNumber=34&StartingColumnNumber=2&StartingLineNumber=34&Timestamp=495493308.359844"
|
||||
lockedSize = "{775, 76}"
|
||||
selectedRepresentationIndex = "1"
|
||||
shouldTrackSuperviewWidth = "YES">
|
||||
@@ -39,7 +39,7 @@
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=24&CharacterRangeLoc=448&EndingColumnNumber=26&EndingLineNumber=21&StartingColumnNumber=2&StartingLineNumber=21&Timestamp=472579861.71569"
|
||||
documentLocation = "#CharacterRangeLen=24&CharacterRangeLoc=450&EndingColumnNumber=26&EndingLineNumber=21&StartingColumnNumber=2&StartingLineNumber=21&Timestamp=495492881.372527"
|
||||
lockedSize = "{775, 68}"
|
||||
selectedRepresentationIndex = "1"
|
||||
shouldTrackSuperviewWidth = "YES">
|
||||
|
||||
@@ -21,7 +21,7 @@ description["content"] = "HTMLKit for iOS & OSX"
|
||||
Append nodes to the document
|
||||
*/
|
||||
let head = document.head!
|
||||
head.appendNode(description)
|
||||
head.append(description)
|
||||
document.innerHTML
|
||||
|
||||
let body = document.body!
|
||||
@@ -30,17 +30,17 @@ let nodes = [
|
||||
HTMLElement(tagName: "div", attributes: ["class": "green"]),
|
||||
HTMLElement(tagName: "div", attributes: ["class": "blue"])
|
||||
]
|
||||
body.appendNodes(nodes)
|
||||
body.append(nodes)
|
||||
body.innerHTML
|
||||
|
||||
/*:
|
||||
Enumerate child elements and perform DOM manipulation
|
||||
*/
|
||||
body.enumerateChildElementsUsingBlock { (element, index, stop) -> Void in
|
||||
body.enumerateChildElements { (element, index, stop) -> Void in
|
||||
if element.tagName == "div" {
|
||||
let lorem = HTMLElement(tagName: "p")
|
||||
lorem.textContent = "Lorem ipsum: \(index)"
|
||||
element.appendNode(lorem)
|
||||
element.append(lorem)
|
||||
}
|
||||
}
|
||||
body.innerHTML
|
||||
@@ -48,7 +48,7 @@ body.innerHTML
|
||||
/*:
|
||||
Remove nodes from the document
|
||||
*/
|
||||
body.removeChildNodeAtIndex(1)
|
||||
body.removeChildNode(at: 1)
|
||||
body.innerHTML
|
||||
|
||||
/*:
|
||||
@@ -56,24 +56,26 @@ Navigate to child and sibling nodes
|
||||
*/
|
||||
body.lastChild!.removeFromParentNode()
|
||||
let greenDiv = body.firstChild!.nextSibling!
|
||||
greenDiv.outerHTML
|
||||
|
||||
/*:
|
||||
Manipulate the HTML directly
|
||||
*/
|
||||
greenDiv.innerHTML = "<ul><li>item 1<li>item 2"
|
||||
greenDiv.outerHTML
|
||||
|
||||
/*:
|
||||
Iterate the DOM tree with custom filters
|
||||
*/
|
||||
let filter = HTMLNodeFilterBlock.filterWithBlock { (node) -> HTMLNodeFilterValue in
|
||||
let filter = HTMLNodeFilterBlock.filter { (node) -> HTMLNodeFilterValue in
|
||||
if node.childNodesCount() != 1 {
|
||||
return .Reject
|
||||
return .reject
|
||||
}
|
||||
return .Accept
|
||||
return .accept
|
||||
}
|
||||
|
||||
for element in body.nodeIteratorWithShowOptions(.Element, filter: filter) {
|
||||
element.outerHTML
|
||||
for element in body.nodeIterator(showOptions: .element, filter: filter) {
|
||||
(element as! AnyObject).outerHTML
|
||||
}
|
||||
|
||||
//: [Next](@next)
|
||||
|
||||
@@ -3,71 +3,65 @@
|
||||
version = "3.0">
|
||||
<TimelineItems>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=19&CharacterRangeLoc=575&EndingColumnNumber=26&EndingLineNumber=24&StartingColumnNumber=1&StartingLineNumber=24&Timestamp=472578662.196737"
|
||||
documentLocation = "#CharacterRangeLen=19&CharacterRangeLoc=571&EndingColumnNumber=26&EndingLineNumber=24&StartingColumnNumber=1&StartingLineNumber=24&Timestamp=495493395.434491"
|
||||
lockedSize = "{763, 104}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "YES">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=15&CharacterRangeLoc=843&EndingColumnNumber=22&EndingLineNumber=33&StartingColumnNumber=1&StartingLineNumber=33&Timestamp=472578662.197012"
|
||||
documentLocation = "#CharacterRangeLen=15&CharacterRangeLoc=834&EndingColumnNumber=22&EndingLineNumber=33&StartingColumnNumber=1&StartingLineNumber=33&Timestamp=495493408.111041"
|
||||
lockedSize = "{763, 75}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=1&CharacterRangeLoc=1757&EndingColumnNumber=44&EndingLineNumber=75&StartingColumnNumber=2&StartingLineNumber=75&Timestamp=472578674.159974"
|
||||
documentLocation = "#CharacterRangeLen=1&CharacterRangeLoc=1774&EndingColumnNumber=44&EndingLineNumber=77&StartingColumnNumber=2&StartingLineNumber=77&Timestamp=496085091.150842"
|
||||
lockedSize = "{763, 176}"
|
||||
selectedRepresentationIndex = "1"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=14&CharacterRangeLoc=844&EndingColumnNumber=15&EndingLineNumber=33&StartingColumnNumber=1&StartingLineNumber=33&Timestamp=472578662.197451"
|
||||
documentLocation = "#CharacterRangeLen=14&CharacterRangeLoc=835&EndingColumnNumber=15&EndingLineNumber=33&StartingColumnNumber=1&StartingLineNumber=33&Timestamp=495493408.111552"
|
||||
lockedSize = "{762, 70}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=18&CharacterRangeLoc=576&EndingColumnNumber=19&EndingLineNumber=24&StartingColumnNumber=1&StartingLineNumber=24&Timestamp=472578662.197662"
|
||||
documentLocation = "#CharacterRangeLen=18&CharacterRangeLoc=572&EndingColumnNumber=19&EndingLineNumber=24&StartingColumnNumber=1&StartingLineNumber=24&Timestamp=495493395.435449"
|
||||
lockedSize = "{752, 99}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=1&CharacterRangeLoc=1757&EndingColumnNumber=16&EndingLineNumber=75&StartingColumnNumber=2&StartingLineNumber=75&Timestamp=472578674.160606"
|
||||
documentLocation = "#CharacterRangeLen=1&CharacterRangeLoc=1774&EndingColumnNumber=16&EndingLineNumber=77&StartingColumnNumber=2&StartingLineNumber=77&Timestamp=496085091.151491"
|
||||
lockedSize = "{763, 102}"
|
||||
selectedRepresentationIndex = "1"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=18&CharacterRangeLoc=1740&EndingColumnNumber=39&EndingLineNumber=75&StartingColumnNumber=2&StartingLineNumber=75&Timestamp=472578674.160818"
|
||||
documentLocation = "#CharacterRangeLen=33&CharacterRangeLoc=1742&EndingColumnNumber=39&EndingLineNumber=77&StartingColumnNumber=2&StartingLineNumber=77&Timestamp=496085091.151711"
|
||||
lockedSize = "{759, 149}"
|
||||
selectedRepresentationIndex = "1"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=17&CharacterRangeLoc=1741&EndingColumnNumber=19&EndingLineNumber=75&StartingColumnNumber=2&StartingLineNumber=75&Timestamp=472578674.161034"
|
||||
documentLocation = "#CharacterRangeLen=32&CharacterRangeLoc=1743&EndingColumnNumber=19&EndingLineNumber=77&StartingColumnNumber=2&StartingLineNumber=77&Timestamp=496085091.151931"
|
||||
lockedSize = "{763, 120}"
|
||||
selectedRepresentationIndex = "1"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=14&CharacterRangeLoc=1230&EndingColumnNumber=15&EndingLineNumber=51&StartingColumnNumber=1&StartingLineNumber=51&Timestamp=472578674.161242"
|
||||
documentLocation = "#CharacterRangeLen=14&CharacterRangeLoc=1204&EndingColumnNumber=15&EndingLineNumber=51&StartingColumnNumber=1&StartingLineNumber=51&Timestamp=495493441.437517"
|
||||
lockedSize = "{763, 94}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=14&CharacterRangeLoc=1145&EndingColumnNumber=15&EndingLineNumber=45&StartingColumnNumber=1&StartingLineNumber=45&Timestamp=472578674.161449"
|
||||
documentLocation = "#CharacterRangeLen=14&CharacterRangeLoc=1122&EndingColumnNumber=15&EndingLineNumber=45&StartingColumnNumber=1&StartingLineNumber=45&Timestamp=495493427.982381"
|
||||
lockedSize = "{760, 97}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=8&CharacterRangeLoc=1332&EndingColumnNumber=13&EndingLineNumber=57&StartingColumnNumber=5&StartingLineNumber=57&Timestamp=472578674.161666"
|
||||
lockedSize = "{763, 74}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=18&CharacterRangeLoc=397&EndingColumnNumber=19&EndingLineNumber=14&StartingColumnNumber=1&StartingLineNumber=14&Timestamp=472578662.199111"
|
||||
lockedSize = "{762, 90}"
|
||||
@@ -75,76 +69,82 @@
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=14&CharacterRangeLoc=701&EndingColumnNumber=15&EndingLineNumber=26&StartingColumnNumber=1&StartingLineNumber=26&Timestamp=472578662.199312"
|
||||
documentLocation = "#CharacterRangeLen=14&CharacterRangeLoc=697&EndingColumnNumber=15&EndingLineNumber=26&StartingColumnNumber=1&StartingLineNumber=26&Timestamp=495493395.437213"
|
||||
lockedSize = "{763, 76}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=14&CharacterRangeLoc=1002&EndingColumnNumber=15&EndingLineNumber=38&StartingColumnNumber=1&StartingLineNumber=38&Timestamp=472578674.162269"
|
||||
documentLocation = "#CharacterRangeLen=14&CharacterRangeLoc=983&EndingColumnNumber=15&EndingLineNumber=38&StartingColumnNumber=1&StartingLineNumber=38&Timestamp=495493427.983236"
|
||||
lockedSize = "{763, 92}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=14&CharacterRangeLoc=1049&EndingColumnNumber=15&EndingLineNumber=41&StartingColumnNumber=1&StartingLineNumber=41&Timestamp=472578674.162475"
|
||||
documentLocation = "#CharacterRangeLen=14&CharacterRangeLoc=1030&EndingColumnNumber=15&EndingLineNumber=41&StartingColumnNumber=1&StartingLineNumber=41&Timestamp=495493427.983458"
|
||||
lockedSize = "{763, 83}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=8&CharacterRangeLoc=1108&EndingColumnNumber=13&EndingLineNumber=44&StartingColumnNumber=5&StartingLineNumber=44&Timestamp=472578674.162693"
|
||||
documentLocation = "#CharacterRangeLen=8&CharacterRangeLoc=1089&EndingColumnNumber=13&EndingLineNumber=44&StartingColumnNumber=5&StartingLineNumber=44&Timestamp=495493427.983688"
|
||||
lockedSize = "{763, 73}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=17&CharacterRangeLoc=1598&EndingColumnNumber=19&EndingLineNumber=68&StartingColumnNumber=2&StartingLineNumber=68&Timestamp=472578674.162898"
|
||||
documentLocation = "#CharacterRangeLen=17&CharacterRangeLoc=1601&EndingColumnNumber=19&EndingLineNumber=70&StartingColumnNumber=2&StartingLineNumber=70&Timestamp=496085091.153628"
|
||||
lockedSize = "{763, 92}"
|
||||
selectedRepresentationIndex = "1"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=8&CharacterRangeLoc=1410&EndingColumnNumber=9&EndingLineNumber=61&StartingColumnNumber=1&StartingLineNumber=59&Timestamp=472578674.163102"
|
||||
lockedSize = "{763, 60}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=14&CharacterRangeLoc=590&EndingColumnNumber=15&EndingLineNumber=21&StartingColumnNumber=1&StartingLineNumber=21&Timestamp=472579886.937978"
|
||||
documentLocation = "#CharacterRangeLen=14&CharacterRangeLoc=586&EndingColumnNumber=15&EndingLineNumber=21&StartingColumnNumber=1&StartingLineNumber=21&Timestamp=495493395.438545"
|
||||
lockedSize = "{763, 50}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=14&CharacterRangeLoc=830&EndingColumnNumber=15&EndingLineNumber=30&StartingColumnNumber=1&StartingLineNumber=30&Timestamp=472579886.937978"
|
||||
documentLocation = "#CharacterRangeLen=9&CharacterRangeLoc=826&EndingColumnNumber=15&EndingLineNumber=30&StartingColumnNumber=1&StartingLineNumber=30&Timestamp=495493408.115325"
|
||||
lockedSize = "{763, 50}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "YES">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=14&CharacterRangeLoc=877&EndingColumnNumber=15&EndingLineNumber=33&StartingColumnNumber=1&StartingLineNumber=33&Timestamp=472579886.937978"
|
||||
documentLocation = "#CharacterRangeLen=14&CharacterRangeLoc=868&EndingColumnNumber=15&EndingLineNumber=33&StartingColumnNumber=1&StartingLineNumber=33&Timestamp=495493408.115547"
|
||||
lockedSize = "{763, 50}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "YES">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=8&CharacterRangeLoc=936&EndingColumnNumber=13&EndingLineNumber=36&StartingColumnNumber=5&StartingLineNumber=36&Timestamp=472579886.937978"
|
||||
documentLocation = "#CharacterRangeLen=8&CharacterRangeLoc=927&EndingColumnNumber=13&EndingLineNumber=36&StartingColumnNumber=5&StartingLineNumber=36&Timestamp=495493408.115762"
|
||||
lockedSize = "{763, 50}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=8&CharacterRangeLoc=978&EndingColumnNumber=9&EndingLineNumber=38&StartingColumnNumber=1&StartingLineNumber=38&Timestamp=472579886.937978"
|
||||
documentLocation = "#CharacterRangeLen=8&CharacterRangeLoc=959&EndingColumnNumber=9&EndingLineNumber=38&StartingColumnNumber=1&StartingLineNumber=38&Timestamp=495493427.985192"
|
||||
lockedSize = "{763, 50}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "YES">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=17&CharacterRangeLoc=1261&EndingColumnNumber=19&EndingLineNumber=48&StartingColumnNumber=2&StartingLineNumber=48&Timestamp=472579886.937978"
|
||||
documentLocation = "#CharacterRangeLen=17&CharacterRangeLoc=1235&EndingColumnNumber=19&EndingLineNumber=48&StartingColumnNumber=2&StartingLineNumber=48&Timestamp=495493441.440785"
|
||||
lockedSize = "{763, 92}"
|
||||
selectedRepresentationIndex = "1"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=18&CharacterRangeLoc=1451&EndingColumnNumber=19&EndingLineNumber=64&StartingColumnNumber=1&StartingLineNumber=64&Timestamp=496085091.155099"
|
||||
lockedSize = "{740, 65}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "YES">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
<LoggerValueHistoryTimelineItem
|
||||
documentLocation = "#CharacterRangeLen=18&CharacterRangeLoc=1347&EndingColumnNumber=19&EndingLineNumber=58&StartingColumnNumber=1&StartingLineNumber=58&Timestamp=496085092.425909"
|
||||
lockedSize = "{740, 69}"
|
||||
selectedRepresentationIndex = "0"
|
||||
shouldTrackSuperviewWidth = "NO">
|
||||
</LoggerValueHistoryTimelineItem>
|
||||
</TimelineItems>
|
||||
</Timeline>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>HTMLKit</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>HTMLKit</h1>
|
||||
<p>HTMLKit is a <a href="https://html.spec.whatwg.org/multipage">WHATWG specification-compliant</a> Objective-C framework for parsing and serializing HTML documents and document fragments for iOS and OSX.</p>
|
||||
<p>HTMLKit parses real-world HTML the same way modern web browsers would.</p>
|
||||
<p>HTMLKit comes armed with a <a href="http://www.w3.org/TR/css3-selectors">CSS3 Selectors</a> engine for querying the DOM.</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<playground version='6.0' target-platform='ios' display-mode='rendered'>
|
||||
<playground version='6.0' target-platform='macos' display-mode='rendered' last-migration='0800'>
|
||||
<pages>
|
||||
<page name='Intro'/>
|
||||
<page name='Parsing Documents'/>
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "HTMLKit"
|
||||
s.version = "0.9.4"
|
||||
s.version = "3.0.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
|
||||
|
||||
+377
-113
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0710"
|
||||
LastUpgradeVersion = "0900"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -26,6 +26,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
codeCoverageEnabled = "YES">
|
||||
<Testables>
|
||||
@@ -70,6 +71,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
|
||||
+11
-8
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0710"
|
||||
LastUpgradeVersion = "0900"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -16,7 +16,7 @@
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "625A14AB19C7829400AD0C32"
|
||||
BuildableName = "HTMLKit.framework"
|
||||
BlueprintName = "HTMLKit-OSX"
|
||||
BlueprintName = "HTMLKit-macOS"
|
||||
ReferencedContainer = "container:HTMLKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
@@ -26,15 +26,17 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
codeCoverageEnabled = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "625A14C219C7829400AD0C32"
|
||||
BuildableName = "HTMLKitTests-OSX.xctest"
|
||||
BlueprintName = "HTMLKitTests-OSX"
|
||||
BuildableName = "HTMLKitTests-macOS.xctest"
|
||||
BlueprintName = "HTMLKitTests-macOS"
|
||||
ReferencedContainer = "container:HTMLKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
<SkippedTests>
|
||||
@@ -58,7 +60,7 @@
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "625A14AB19C7829400AD0C32"
|
||||
BuildableName = "HTMLKit.framework"
|
||||
BlueprintName = "HTMLKit-OSX"
|
||||
BlueprintName = "HTMLKit-macOS"
|
||||
ReferencedContainer = "container:HTMLKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
@@ -69,6 +71,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
@@ -80,7 +83,7 @@
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "625A14AB19C7829400AD0C32"
|
||||
BuildableName = "HTMLKit.framework"
|
||||
BlueprintName = "HTMLKit-OSX"
|
||||
BlueprintName = "HTMLKit-macOS"
|
||||
ReferencedContainer = "container:HTMLKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
@@ -98,7 +101,7 @@
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "625A14AB19C7829400AD0C32"
|
||||
BuildableName = "HTMLKit.framework"
|
||||
BlueprintName = "HTMLKit-OSX"
|
||||
BlueprintName = "HTMLKit-macOS"
|
||||
ReferencedContainer = "container:HTMLKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
LastUpgradeVersion = "0900"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -26,7 +26,9 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
codeCoverageEnabled = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
@@ -63,6 +65,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
LastUpgradeVersion = "0900"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -26,7 +26,9 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
codeCoverageEnabled = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
@@ -36,6 +38,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
|
||||
+3
@@ -7,4 +7,7 @@
|
||||
<FileRef
|
||||
location = "group:HTMLKit.playground">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Example/HTMLKitExample/HTMLKitExample.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "HTMLKit"
|
||||
name: "HTMLKit",
|
||||
exclude: ["Tests/Fixtures", "Tests/css-tests", "Tests/html5lib-tests"]
|
||||
)
|
||||
|
||||
@@ -4,13 +4,20 @@
|
||||
|
||||
An Objective-C framework for your everyday HTML needs.
|
||||
|
||||
[](https://travis-ci.org/iabudiab/HTMLKit)
|
||||
[](https://travis-ci.org/iabudiab/HTMLKit)
|
||||
[](https://codecov.io/gh/iabudiab/HTMLKit)
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
[](https://cocoapods.org/pods/HTMLKit)
|
||||
[](http://cocoadocs.org/docsets/HTMLKit)
|
||||
[](http://cocoadocs.org/docsets/HTMLKit)
|
||||
[](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 +40,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 +63,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.
|
||||
|
||||
@@ -69,10 +76,8 @@ $ gem install cocoapods
|
||||
To add `HTMLKit` as a dependency into your project using CocoaPods just add the following in your `Podfile`:
|
||||
|
||||
```ruby
|
||||
use_frameworks!
|
||||
|
||||
target 'MyTarget' do
|
||||
pod 'HTMLKit', '~> 0.9'
|
||||
pod 'HTMLKit', '~> 2.1'
|
||||
end
|
||||
```
|
||||
|
||||
@@ -82,14 +87,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: 2)
|
||||
```
|
||||
|
||||
Then run:
|
||||
@@ -98,7 +103,7 @@ Then run:
|
||||
$ swift build
|
||||
```
|
||||
|
||||
### Manually
|
||||
## Manually
|
||||
|
||||
1- Add `HTMLKit` as git submodule
|
||||
|
||||
@@ -110,9 +115,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 +132,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 +155,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 +179,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 +199,12 @@ NSArray *nodes = @[
|
||||
[body.lastChild removeFromParentNode];
|
||||
```
|
||||
|
||||
* Manipulate the HTML directly
|
||||
|
||||
```objective-c
|
||||
greenDiv.innerHTML = @"<ul><li>item 1<li>item 2";
|
||||
```
|
||||
|
||||
* Navigate to child and sibling nodes
|
||||
|
||||
```objective-c
|
||||
@@ -201,12 +212,6 @@ HTMLNode *firstChild = body.firstChild;
|
||||
HTMLNode *greenDiv = firstChild.nextSibling;
|
||||
```
|
||||
|
||||
* Manipulate the HTML directly
|
||||
|
||||
```objective-c
|
||||
greenDiv.innerHTML = @"<ul><li>item 1<li>item 2";
|
||||
```
|
||||
|
||||
* Iterate the DOM tree with custom filters
|
||||
|
||||
```objective-c
|
||||
@@ -222,6 +227,17 @@ for (HTMLElement *element in [body nodeIteratorWithShowOptions:HTMLNodeFilterSho
|
||||
}
|
||||
```
|
||||
|
||||
* Create and manipulate DOM Ranges
|
||||
|
||||
```objective-c
|
||||
HTMLDocument *document = [HTMLDocument documentWithString:@"<div><h1>HTMLKit</h1><p id='foo'>Hello there!</p></div>"];
|
||||
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:
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
}
|
||||
case CSSAttributeSelectorIncludes:
|
||||
{
|
||||
NSArray *components = [element[_name] componentsSeparatedByCharactersInSet:[NSCharacterSet HTMLWhitespaceCharacterSet]];
|
||||
NSArray *components = [element[_name] componentsSeparatedByCharactersInSet:[NSCharacterSet htmlkit_HTMLWhitespaceCharacterSet]];
|
||||
return [components containsObject:_value];
|
||||
}
|
||||
case CSSAttributeSelectorBegins:
|
||||
|
||||
@@ -24,14 +24,14 @@
|
||||
|
||||
- (NSString *)consumeIdentifier
|
||||
{
|
||||
CFMutableStringRef value = CFStringCreateMutable(kCFAllocatorDefault, 0);
|
||||
|
||||
if (!isValidIdentifierStart([self inputCharacterPointAtOffset:0],
|
||||
[self inputCharacterPointAtOffset:1],
|
||||
[self inputCharacterPointAtOffset:2])) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
CFMutableStringRef value = CFStringCreateMutable(kCFAllocatorDefault, 0);
|
||||
|
||||
while (YES) {
|
||||
UTF32Char codePoint = [self consumeNextInputCharacter];
|
||||
if (codePoint == EOF) {
|
||||
@@ -47,7 +47,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
return (__bridge NSString *)(CFStringGetLength(value) > 0 ? value : nil);
|
||||
if (CFStringGetLength(value) > 0) {
|
||||
return (__bridge_transfer NSString *)value;
|
||||
}
|
||||
|
||||
CFRelease(value);
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *)consumeStringWithEndingCodePoint:(UTF32Char)endingCodePoint
|
||||
@@ -85,7 +90,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
return (__bridge NSString *)(CFStringGetLength(value) > 0 ? value : nil);
|
||||
if (CFStringGetLength(value) > 0) {
|
||||
return (__bridge_transfer NSString *)value;
|
||||
}
|
||||
|
||||
CFRelease(value);
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (UTF32Char)consumeEscapedCodePoint
|
||||
@@ -105,7 +115,7 @@
|
||||
[self consumeNextInputCharacter];
|
||||
}
|
||||
|
||||
NSScanner *scanner = [NSScanner scannerWithString:(__bridge NSString *)(hexString)];
|
||||
NSScanner *scanner = [NSScanner scannerWithString:(__bridge_transfer NSString *)(hexString)];
|
||||
unsigned int number;
|
||||
[scanner scanHexInt:&number];
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#import "CSSNthExpressionParser.h"
|
||||
#import "CSSCodePoints.h"
|
||||
#import "NSString+HTMLKit.h"
|
||||
#import "NSString+Private.h"
|
||||
#import "NSCharacterSet+HTMLKit.h"
|
||||
|
||||
@implementation CSSNthExpressionParser
|
||||
@@ -26,7 +26,7 @@
|
||||
return CSSNthExpressionEven;
|
||||
}
|
||||
|
||||
NSCharacterSet *set = [[NSCharacterSet CSSNthExpressionCharacterSet] invertedSet];
|
||||
NSCharacterSet *set = [[NSCharacterSet htmlkit_CSSNthExpressionCharacterSet] invertedSet];
|
||||
if ([string rangeOfCharacterFromSet:set].location != NSNotFound) {
|
||||
return CSSNthExpressionMake(0, 0);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ NSString * _Nonnull NSStringFromNthExpression(CSSNthExpression expression)
|
||||
|
||||
#pragma mark - Implementation
|
||||
|
||||
NSInteger computeIndex(NSEnumerator *enumerator, HTMLElement *element)
|
||||
NS_INLINE NSInteger computeIndex(NSEnumerator *enumerator, HTMLElement *element)
|
||||
{
|
||||
NSInteger index = 0;
|
||||
for (HTMLNode *node in enumerator) {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#import "CSSInputStream.h"
|
||||
#import "CSSCodePoints.h"
|
||||
#import "CSSSelectors.h"
|
||||
#import "NSString+HTMLKit.h"
|
||||
#import "NSString+Private.h"
|
||||
#import "NSCharacterSet+HTMLKit.h"
|
||||
#import "CSSNthExpressionParser.h"
|
||||
#import "CSSCompoundSelector.h"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#import "CSSStructuralPseudoSelectors.h"
|
||||
#import "CSSSelectors.h"
|
||||
#import "HTMLElement.h"
|
||||
#import "NSString+HTMLKit.h"
|
||||
#import "NSString+Private.h"
|
||||
|
||||
#pragma mark - Elements
|
||||
|
||||
@@ -276,7 +276,7 @@ CSSSelector * ltSelector(NSInteger index)
|
||||
return namedBlockSelector(name, ^BOOL(HTMLElement * _Nonnull element) {
|
||||
NSUInteger elementIndex = [element.parentElement indexOfChildNode:element];
|
||||
|
||||
if (index > 0) {
|
||||
if (index >= 0) {
|
||||
return elementIndex < index;
|
||||
} else {
|
||||
return elementIndex < element.parentElement.childNodesCount - index - 1;
|
||||
@@ -290,7 +290,7 @@ CSSSelector * gtSelector(NSInteger index)
|
||||
return namedBlockSelector(name, ^BOOL(HTMLElement * _Nonnull element) {
|
||||
NSUInteger elementIndex = [element.parentElement indexOfChildNode:element];
|
||||
|
||||
if (index > 0) {
|
||||
if (index >= 0) {
|
||||
return elementIndex > index;
|
||||
} else {
|
||||
return elementIndex > element.parentElement.childNodesCount - index - 1;
|
||||
@@ -304,7 +304,7 @@ CSSSelector * eqSelector(NSInteger index)
|
||||
return namedBlockSelector(name, ^BOOL(HTMLElement * _Nonnull element) {
|
||||
NSUInteger elementIndex = [element.parentElement indexOfChildNode:element];
|
||||
|
||||
if (index > 0) {
|
||||
if (index >= 0) {
|
||||
return elementIndex == index;
|
||||
} else {
|
||||
return elementIndex == element.parentElement.childNodesCount - index - 1;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#import "CSSTypeSelector.h"
|
||||
#import "HTMLElement.h"
|
||||
#import "NSString+HTMLKit.h"
|
||||
#import "NSString+Private.h"
|
||||
|
||||
@interface CSSTypeSelector ()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
//
|
||||
// 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) {
|
||||
if (data) {
|
||||
_data = [[NSMutableString alloc] initWithString:data];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)data
|
||||
{
|
||||
if (_data == nil) {
|
||||
_data = [[NSMutableString alloc] initWithString:@""];
|
||||
}
|
||||
|
||||
return _data;
|
||||
}
|
||||
|
||||
- (NSString *)textContent
|
||||
{
|
||||
return [self.data copy];
|
||||
}
|
||||
|
||||
- (void)setTextContent:(NSString *)textContent
|
||||
{
|
||||
[self setData:textContent];
|
||||
}
|
||||
|
||||
- (NSUInteger)length
|
||||
{
|
||||
return self.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);
|
||||
|
||||
[(NSMutableString *)self.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
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
- (BOOL)isWhitespaceToken
|
||||
{
|
||||
return [_characters isHTMLWhitespaceString];
|
||||
return [_characters htmlkit_isHTMLWhitespaceString];
|
||||
}
|
||||
|
||||
- (BOOL)isEmpty
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
- (void)retainLeadingWhitespace
|
||||
{
|
||||
NSUInteger index = _characters.leadingHTMLWhitespaceLength;
|
||||
NSUInteger index = _characters.htmlkit_leadingHTMLWhitespaceLength;
|
||||
if (index > 0) {
|
||||
[_characters setString:[_characters substringToIndex:index]];
|
||||
}
|
||||
@@ -54,7 +54,7 @@
|
||||
|
||||
- (void)trimLeadingWhitespace
|
||||
{
|
||||
NSUInteger index = _characters.leadingHTMLWhitespaceLength;
|
||||
NSUInteger index = _characters.htmlkit_leadingHTMLWhitespaceLength;
|
||||
if (index > 0) {
|
||||
[_characters setString:[_characters substringFromIndex:index]];
|
||||
}
|
||||
@@ -67,7 +67,7 @@
|
||||
|
||||
- (HTMLCharacterToken *)tokenBySplitingLeadingWhiteSpace
|
||||
{
|
||||
NSUInteger index = _characters.leadingHTMLWhitespaceLength;
|
||||
NSUInteger index = _characters.htmlkit_leadingHTMLWhitespaceLength;
|
||||
if (index > 0) {
|
||||
NSString *leading = [_characters substringToIndex:index];
|
||||
[_characters setString:[_characters substringFromIndex:index]];
|
||||
|
||||
+2
-25
@@ -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
|
||||
|
||||
@@ -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<HTMLNode *> * GetAncestorNodes(HTMLNode *node)
|
||||
{
|
||||
NSMutableArray *ancestors = [NSMutableArray array];
|
||||
for (HTMLNode *it = node; it; it = it.parentNode) {
|
||||
[ancestors addObject:it];
|
||||
}
|
||||
return ancestors;
|
||||
}
|
||||
+59
-10
@@ -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;
|
||||
NSHashTable *_nodeIterators;
|
||||
NSHashTable *_ranges;
|
||||
}
|
||||
@property (nonatomic, assign) HTMLDocumentReadyState readyState;
|
||||
@end
|
||||
@@ -41,7 +41,8 @@
|
||||
self = [super initWithName:@"#document" type:HTMLNodeDocument];
|
||||
if (self) {
|
||||
_readyState = HTMLDocumentLoading;
|
||||
_nodeIterators = [NSMutableArray new];
|
||||
_nodeIterators = [[NSHashTable alloc] initWithOptions:NSHashTableWeakMemory capacity:10];
|
||||
_ranges = [[NSHashTable alloc] initWithOptions:NSHashTableWeakMemory capacity:10];
|
||||
}
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
#import "HTMLDocumentType.h"
|
||||
#import "NSString+HTMLKit.h"
|
||||
#import "NSString+Private.h"
|
||||
#import "HTMLNode+Private.h"
|
||||
|
||||
NS_INLINE BOOL nilOrEqual(id first, id second) {
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+25
-16
@@ -12,7 +12,7 @@
|
||||
#import "HTMLText.h"
|
||||
#import "HTMLDOMTokenList.h"
|
||||
#import "HTMLOrderedDictionary.h"
|
||||
#import "NSString+HTMLKit.h"
|
||||
#import "NSString+Private.h"
|
||||
#import "HTMLNode+Private.h"
|
||||
|
||||
@interface HTMLElement ()
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
- (instancetype)initWithTagName:(NSString *)tagName
|
||||
{
|
||||
return [self initWithTagName:tagName attributes:@{}];
|
||||
return [self initWithTagName:tagName attributes:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes
|
||||
@@ -45,8 +45,9 @@
|
||||
self = [super initWithName:tagName type:HTMLNodeElement];
|
||||
if (self) {
|
||||
_tagName = [tagName copy];
|
||||
_attributes = [HTMLOrderedDictionary new];
|
||||
_attributes = nil;
|
||||
if (attributes != nil) {
|
||||
_attributes = [HTMLOrderedDictionary new];
|
||||
[_attributes addEntriesFromDictionary:attributes];
|
||||
}
|
||||
_htmlNamespace = htmlNamespace;
|
||||
@@ -56,24 +57,33 @@
|
||||
|
||||
#pragma mark - Special Attributes
|
||||
|
||||
- (NSMutableDictionary<NSString *,NSString *> *)attributes
|
||||
{
|
||||
if (_attributes == nil) {
|
||||
_attributes = [HTMLOrderedDictionary new];
|
||||
}
|
||||
|
||||
return _attributes;
|
||||
}
|
||||
|
||||
- (NSString *)elementId
|
||||
{
|
||||
return _attributes[@"id"] ?: @"";
|
||||
return self.attributes[@"id"] ?: @"";
|
||||
}
|
||||
|
||||
- (void)setElementId:(NSString *)elementId
|
||||
{
|
||||
_attributes[@"id"] = elementId;
|
||||
self.attributes[@"id"] = elementId;
|
||||
}
|
||||
|
||||
- (NSString *)className
|
||||
{
|
||||
return _attributes[@"class"] ?: @"";
|
||||
return self.attributes[@"class"] ?: @"";
|
||||
}
|
||||
|
||||
- (void)setClassName:(NSString *)className
|
||||
{
|
||||
_attributes[@"class"] = className;
|
||||
self.attributes[@"class"] = className;
|
||||
}
|
||||
|
||||
- (HTMLDOMTokenList *)classList
|
||||
@@ -85,22 +95,22 @@
|
||||
|
||||
- (BOOL)hasAttribute:(NSString *)name
|
||||
{
|
||||
return _attributes[name] != nil;
|
||||
return self.attributes[name] != nil;
|
||||
}
|
||||
|
||||
- (NSString *)objectForKeyedSubscript:(NSString *)name;
|
||||
{
|
||||
return _attributes[name];
|
||||
return self.attributes[name];
|
||||
}
|
||||
|
||||
- (void)setObject:(NSString *)value forKeyedSubscript:(NSString *)attribute
|
||||
{
|
||||
_attributes[attribute] = value;
|
||||
self.attributes[attribute] = value;
|
||||
}
|
||||
|
||||
- (void)removeAttribute:(NSString *)name
|
||||
{
|
||||
[_attributes removeObjectForKey:name];
|
||||
[self.attributes removeObjectForKey:name];
|
||||
}
|
||||
|
||||
- (NSString *)textContent
|
||||
@@ -134,7 +144,7 @@
|
||||
{
|
||||
HTMLElement *copy = [super copyWithZone:zone];
|
||||
copy->_tagName = [_tagName copy];
|
||||
copy->_attributes = [_attributes copy];
|
||||
copy->_attributes = [_attributes mutableCopy];
|
||||
copy->_htmlNamespace = _htmlNamespace;
|
||||
return copy;
|
||||
}
|
||||
@@ -147,11 +157,10 @@
|
||||
|
||||
[result appendFormat:@"<%@", self.tagName];
|
||||
[self.attributes enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL *stop) {
|
||||
NSRange range = NSMakeRange(0, value.length);
|
||||
NSMutableString *escaped = [value mutableCopy];
|
||||
[escaped replaceOccurrencesOfString:@"&" withString:@"&" options:0 range:range];
|
||||
[escaped replaceOccurrencesOfString:@"\00A0" withString:@" " options:0 range:range];
|
||||
[escaped replaceOccurrencesOfString:@"\"" withString:@""" options:0 range:range];
|
||||
[escaped replaceOccurrencesOfString:@"&" withString:@"&" options:0 range:NSMakeRange(0, escaped.length)];
|
||||
[escaped replaceOccurrencesOfString:@"0x00A0" withString:@" " options:0 range:NSMakeRange(0, escaped.length)];
|
||||
[escaped replaceOccurrencesOfString:@"\"" withString:@""" options:0 range:NSMakeRange(0, escaped.length)];
|
||||
|
||||
[result appendFormat:@" %@=\"%@\"", key, escaped];
|
||||
}];
|
||||
|
||||
@@ -48,10 +48,10 @@
|
||||
|
||||
#pragma mark - Errors
|
||||
|
||||
- (void)emitParseError:(NSString *)reason
|
||||
- (void)emitParseError:(NSString *)code details:(NSString *)details
|
||||
{
|
||||
if (self.errorCallback) {
|
||||
self.errorCallback(reason);
|
||||
self.errorCallback(code, details);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,16 +82,16 @@
|
||||
return LINE_FEED;
|
||||
}
|
||||
if (CFStringIsSurrogateLowCharacter(nextInputCharacter)) {
|
||||
NSString *reason = [NSString stringWithFormat:@"Non-Unicode character found (an isolated low surrogate: 0x%X)", (unsigned int)nextInputCharacter];
|
||||
[self emitParseError:reason];
|
||||
NSString *details = [NSString stringWithFormat:@"Non-Unicode character found (an isolated low surrogate: 0x%X)", (unsigned int)nextInputCharacter];
|
||||
[self emitParseError:@"surrogate-in-input-stream" details:details];
|
||||
return nextInputCharacter;
|
||||
}
|
||||
|
||||
if (CFStringIsSurrogateHighCharacter(nextInputCharacter)) {
|
||||
UniChar surrogateLow = CFStringGetCharacterFromInlineBuffer(&_buffer, _location + 1);
|
||||
if (CFStringIsSurrogateLowCharacter(surrogateLow) == NO) {
|
||||
NSString *reason = [NSString stringWithFormat:@"Non-Unicode character found (an isolated high surrogate: 0x%X)", (unsigned int)nextInputCharacter];
|
||||
[self emitParseError:reason];
|
||||
NSString *details = [NSString stringWithFormat:@"Non-Unicode character found (an isolated high surrogate: 0x%X)", (unsigned int)nextInputCharacter];
|
||||
[self emitParseError:@"surrogate-in-input-stream" details:details];
|
||||
return nextInputCharacter;
|
||||
}
|
||||
|
||||
@@ -99,9 +99,14 @@
|
||||
nextInputCharacter = CFStringGetLongCharacterForSurrogatePair(nextInputCharacter, surrogateLow);
|
||||
}
|
||||
|
||||
if (isControlOrUndefinedCharacter(nextInputCharacter)) {
|
||||
NSString *reason = [NSString stringWithFormat:@"A control/undefined character found: (0x%X)", (unsigned int)nextInputCharacter];
|
||||
[self emitParseError:reason];
|
||||
if (isControlCharacter(nextInputCharacter)) {
|
||||
NSString *details = [NSString stringWithFormat:@"A control character found: (0x%X)", (unsigned int)nextInputCharacter];
|
||||
[self emitParseError:@"control-character-in-input-stream" details:details];
|
||||
}
|
||||
|
||||
if (isNoncharacter(nextInputCharacter)) {
|
||||
NSString *details = [NSString stringWithFormat:@"A noncharacter found: (0x%X)", (unsigned int)nextInputCharacter];
|
||||
[self emitParseError:@"noncharacter-in-input-stream" details:details];
|
||||
}
|
||||
|
||||
return nextInputCharacter;
|
||||
@@ -165,7 +170,7 @@
|
||||
|
||||
- (BOOL)consumeHexNumber:(unsigned long long *)result
|
||||
{
|
||||
NSCharacterSet *set = [NSCharacterSet HTMLHexNumberCharacterSet];
|
||||
NSCharacterSet *set = [NSCharacterSet htmlkit_HTMLHexNumberCharacterSet];
|
||||
|
||||
NSString *string = nil;
|
||||
BOOL success = [_scanner scanCharactersFromSet:set intoString:&string];
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.9.4</string>
|
||||
<string>3.0.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
- (void)addElement:(HTMLElement *)element
|
||||
{
|
||||
NSUInteger existing = 0;
|
||||
for (HTMLElement *node in _list.reverseObjectEnumerator.allObjects) {
|
||||
for (HTMLElement *node in _list.reverseObjectEnumerator) {
|
||||
if ([node isEqual:[HTMLMarker marker]]) {
|
||||
break;
|
||||
}
|
||||
|
||||
+118
-62
@@ -16,12 +16,12 @@
|
||||
#import "HTMLKitDOMExceptions.h"
|
||||
#import "HTMLNodeFilter.h"
|
||||
#import "CSSSelector.h"
|
||||
#import "HTMLDocument+Private.h"
|
||||
#import "HTMLDOMUtils.h"
|
||||
|
||||
@interface HTMLDocument (Private)
|
||||
- (void)runRemovingStepsForNode:(HTMLNode *)oldNode
|
||||
withOldParent:(HTMLNode *)oldParent
|
||||
andOldPreviousSibling:(HTMLNode *)oldPreviousSibling;
|
||||
@end
|
||||
NSString * const ValidationNodePreInsertion = @"-ensurePreInsertionValidityOfNode:beforeChildNode:";
|
||||
NSString * const ValidationNodeReplacement = @"-ensureReplacementValidityOfChildNode:withNode:";
|
||||
NSString * const RemoveChildNode = @"-removeChildNode:";
|
||||
|
||||
@interface HTMLNode ()
|
||||
{
|
||||
@@ -40,13 +40,22 @@
|
||||
if (self) {
|
||||
_name = name;
|
||||
_nodeType = type;
|
||||
_childNodes = [NSMutableOrderedSet new];
|
||||
_childNodes = nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
- (NSOrderedSet<HTMLNode *> *)childNodes
|
||||
{
|
||||
if (_childNodes == nil) {
|
||||
_childNodes = [NSMutableOrderedSet new];
|
||||
}
|
||||
|
||||
return _childNodes;
|
||||
}
|
||||
|
||||
- (HTMLDocument *)ownerDocument
|
||||
{
|
||||
if (_nodeType == HTMLNodeDocument) {
|
||||
@@ -59,7 +68,14 @@
|
||||
- (void)setOwnerDocument:(HTMLDocument *)ownerDocument
|
||||
{
|
||||
_ownerDocument = ownerDocument;
|
||||
[self.childNodes.array makeObjectsPerformSelector:@selector(setOwnerDocument:) withObject:ownerDocument];
|
||||
for (HTMLNode *child in _childNodes) {
|
||||
[child setOwnerDocument:ownerDocument];
|
||||
}
|
||||
}
|
||||
|
||||
- (HTMLNode *)rootNode
|
||||
{
|
||||
return _parentNode == nil ? self : _parentNode.rootNode;
|
||||
}
|
||||
|
||||
- (void)setParentNode:(HTMLNode *)parentNode
|
||||
@@ -74,12 +90,12 @@
|
||||
|
||||
- (HTMLNode *)firstChild
|
||||
{
|
||||
return self.childNodes.firstObject;
|
||||
return _childNodes.firstObject;
|
||||
}
|
||||
|
||||
- (HTMLNode *)lastChild
|
||||
{
|
||||
return self.childNodes.lastObject;
|
||||
return _childNodes.lastObject;
|
||||
}
|
||||
|
||||
- (HTMLNode *)previousSibling
|
||||
@@ -111,18 +127,28 @@
|
||||
|
||||
- (HTMLElement *)nextSiblingElement
|
||||
{
|
||||
HTMLNode *node = self.previousSibling;
|
||||
HTMLNode *node = self.nextSibling;
|
||||
while (node && node.nodeType != HTMLNodeElement) {
|
||||
node = node.nextSibling;
|
||||
}
|
||||
return node.asElement;
|
||||
}
|
||||
|
||||
- (NSUInteger)index
|
||||
{
|
||||
return [_parentNode indexOfChildNode:self];
|
||||
}
|
||||
|
||||
- (NSString *)textContent
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSUInteger)length
|
||||
{
|
||||
return self.childNodesCount;
|
||||
}
|
||||
|
||||
#pragma mark - Cast
|
||||
|
||||
- (HTMLElement *)asElement
|
||||
@@ -134,12 +160,16 @@
|
||||
|
||||
- (BOOL)hasChildNodes
|
||||
{
|
||||
return self.childNodes.count > 0;
|
||||
return _childNodes.count > 0;
|
||||
}
|
||||
|
||||
- (BOOL)hasChildNodeOfType:(HTMLNodeType)type
|
||||
{
|
||||
NSUInteger index = [self.childNodes indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
|
||||
if (_childNodes == nil) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSUInteger index = [_childNodes indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
|
||||
if ([(HTMLNode *)obj nodeType] == type) {
|
||||
*stop = YES;
|
||||
return YES;
|
||||
@@ -152,30 +182,35 @@
|
||||
|
||||
- (NSUInteger)childNodesCount
|
||||
{
|
||||
return self.childNodes.count;
|
||||
return _childNodes.count;
|
||||
}
|
||||
|
||||
- (BOOL)isEmpty
|
||||
{
|
||||
return self.length == 0;
|
||||
}
|
||||
|
||||
- (HTMLNode *)childNodeAtIndex:(NSUInteger)index
|
||||
{
|
||||
return [self.childNodes objectAtIndex:index];
|
||||
return [_childNodes objectAtIndex:index];
|
||||
}
|
||||
|
||||
- (NSUInteger)childElementsCount
|
||||
{
|
||||
return [self.childNodes indexesOfObjectsPassingTest:^BOOL(HTMLNode * _Nonnull node, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
return [_childNodes indexesOfObjectsPassingTest:^BOOL(HTMLNode * _Nonnull node, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
return node.nodeType == HTMLNodeElement;
|
||||
}].count;
|
||||
}
|
||||
|
||||
- (NSUInteger)indexOfChildNode:(HTMLNode *)node
|
||||
{
|
||||
return [self.childNodes indexOfObject:node];
|
||||
return [_childNodes indexOfObject:node];
|
||||
}
|
||||
|
||||
- (HTMLElement *)childElementAtIndex:(NSUInteger)index
|
||||
{
|
||||
NSUInteger counter = 0;
|
||||
for (HTMLNode *node in self.childNodes) {
|
||||
for (HTMLNode *node in _childNodes) {
|
||||
if (node.nodeType == HTMLNodeElement) {
|
||||
if (counter == index) {
|
||||
return node.asElement;
|
||||
@@ -189,7 +224,7 @@
|
||||
- (NSUInteger)indexOfChildElement:(HTMLElement *)element
|
||||
{
|
||||
NSUInteger counter = 0;
|
||||
for (HTMLNode *node in self.childNodes) {
|
||||
for (HTMLNode *node in _childNodes) {
|
||||
if (node.nodeType == HTMLNodeElement) {
|
||||
if (node == element) {
|
||||
return counter;
|
||||
@@ -246,7 +281,9 @@
|
||||
[node removeAllChildNodes];
|
||||
}
|
||||
|
||||
[nodes makeObjectsPerformSelector:@selector(setParentNode:) withObject:self];
|
||||
for (HTMLNode *node in nodes) {
|
||||
[node setParentNode:self];
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
@@ -274,7 +311,7 @@
|
||||
|
||||
- (void)removeFromParentNode
|
||||
{
|
||||
[self.parentNode removeChildNode:self];
|
||||
[_parentNode removeChildNode:self];
|
||||
}
|
||||
|
||||
- (HTMLNode *)removeChildNode:(HTMLNode *)child
|
||||
@@ -282,7 +319,7 @@
|
||||
if (child.parentNode != self) {
|
||||
[NSException raise:HTMLKitNotFoundError
|
||||
format:@"%@: Not Fount Error, removing non-child node %@. The object can not be found here.",
|
||||
NSStringFromSelector(_cmd), child];
|
||||
RemoveChildNode, child];
|
||||
}
|
||||
|
||||
HTMLNode *oldNode = child;
|
||||
@@ -306,16 +343,18 @@
|
||||
|
||||
- (void)reparentChildNodesIntoNode:(HTMLNode *)node
|
||||
{
|
||||
for (HTMLNode *child in self.childNodes.array) {
|
||||
for (HTMLNode *child in _childNodes) {
|
||||
[node appendNode:child];
|
||||
}
|
||||
[(NSMutableOrderedSet *)self.childNodes removeAllObjects];
|
||||
[(NSMutableOrderedSet *)_childNodes removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)removeAllChildNodes
|
||||
{
|
||||
[self.childNodes.array makeObjectsPerformSelector:@selector(setParentNode:) withObject:nil];
|
||||
[(NSMutableOrderedSet *)self.childNodes removeAllObjects];
|
||||
for (HTMLNode *child in _childNodes) {
|
||||
[child setParentNode:nil];
|
||||
}
|
||||
[(NSMutableOrderedSet *)_childNodes removeAllObjects];
|
||||
}
|
||||
|
||||
- (HTMLDocumentPosition)compareDocumentPositionWithNode:(HTMLNode *)otherNode
|
||||
@@ -328,41 +367,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,7 +425,7 @@
|
||||
return self.nodeType != HTMLNodeDocument && self.ownerDocument == otherNode;
|
||||
}
|
||||
|
||||
for (HTMLNode *parentNode = self.parentNode; parentNode; parentNode = parentNode.parentNode) {
|
||||
for (HTMLNode *parentNode = _parentNode; parentNode; parentNode = parentNode.parentNode) {
|
||||
if (parentNode == otherNode) {
|
||||
return YES;
|
||||
}
|
||||
@@ -406,7 +447,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
[self.childNodes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
|
||||
[_childNodes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
|
||||
block(obj, idx, stop);
|
||||
}];
|
||||
}
|
||||
@@ -417,7 +458,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
[self.childNodes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
|
||||
[_childNodes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
|
||||
if ([obj isKindOfClass:[HTMLElement class]]) {
|
||||
block([obj asElement], idx, stop);
|
||||
}
|
||||
@@ -540,18 +581,18 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin
|
||||
|
||||
- (void)ensurePreInsertionValidityOfNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child
|
||||
{
|
||||
CheckParentValid(self, NSStringFromSelector(_cmd));
|
||||
CheckParentValid(self, ValidationNodePreInsertion);
|
||||
|
||||
CheckChildsParent(self, child, NSStringFromSelector(_cmd));
|
||||
CheckChildsParent(self, child, ValidationNodePreInsertion);
|
||||
|
||||
CheckInsertedNodeValid(node, NSStringFromSelector(_cmd));
|
||||
CheckInsertedNodeValid(node, ValidationNodePreInsertion);
|
||||
|
||||
CheckInvalidCombination(self, node, NSStringFromSelector(_cmd));
|
||||
CheckInvalidCombination(self, node, ValidationNodePreInsertion);
|
||||
|
||||
void (^ hierarchyError)() = ^{
|
||||
void (^ hierarchyError)(void) = ^{
|
||||
[NSException raise:HTMLKitHierarchyRequestError
|
||||
format:@"%@: Hierarchy Request Error, inserting (%@) into (%@). The operation would yield an incorrect node tree.",
|
||||
NSStringFromSelector(_cmd), self, node];
|
||||
ValidationNodePreInsertion, self, node];
|
||||
};
|
||||
|
||||
if (self.nodeType == HTMLNodeDocument) {
|
||||
@@ -590,18 +631,18 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin
|
||||
|
||||
- (void)ensureReplacementValidityOfChildNode:(HTMLNode *)child withNode:(HTMLNode *)node
|
||||
{
|
||||
CheckParentValid(self, NSStringFromSelector(_cmd));
|
||||
CheckParentValid(self, ValidationNodeReplacement);
|
||||
|
||||
CheckChildsParent(self, child, NSStringFromSelector(_cmd));
|
||||
CheckChildsParent(self, child, ValidationNodeReplacement);
|
||||
|
||||
CheckInsertedNodeValid(node, NSStringFromSelector(_cmd));
|
||||
CheckInsertedNodeValid(node, ValidationNodeReplacement);
|
||||
|
||||
CheckInvalidCombination(self, node, NSStringFromSelector(_cmd));
|
||||
CheckInvalidCombination(self, node, ValidationNodeReplacement);
|
||||
|
||||
void (^ hierarchyError)() = ^{
|
||||
void (^ hierarchyError)(void) = ^{
|
||||
[NSException raise:HTMLKitHierarchyRequestError
|
||||
format:@"%@: Hierarchy Request Error. The operation would yield an incorrect node tree.",
|
||||
NSStringFromSelector(_cmd)];
|
||||
ValidationNodeReplacement];
|
||||
};
|
||||
|
||||
void (^ checkParentHasAnotherChildOfType)(HTMLNodeType) = ^ void (HTMLNodeType type) {
|
||||
@@ -650,6 +691,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 _childNodes) {
|
||||
[copy appendNode:[child cloneNodeDeep:YES]];
|
||||
}
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
@interface HTMLNodeFilterBlock ()
|
||||
{
|
||||
BOOL (^ _block)(HTMLNode *);
|
||||
HTMLNodeFilterValue (^ _block)(HTMLNode *);
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -154,4 +154,11 @@
|
||||
return [_keys countByEnumeratingWithState:state objects:buffer count:len];
|
||||
}
|
||||
|
||||
#pragma mark - Copying
|
||||
|
||||
- (id)mutableCopy
|
||||
{
|
||||
return [[HTMLOrderedDictionary alloc] initWithDictionary:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,29 +10,46 @@
|
||||
|
||||
@interface HTMLParseErrorToken ()
|
||||
{
|
||||
NSString *_reason;
|
||||
NSString *_code;
|
||||
NSString *_details;
|
||||
NSUInteger _location;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation HTMLParseErrorToken
|
||||
@synthesize reason = _reason;
|
||||
@synthesize code = _code;
|
||||
@synthesize details = _details;
|
||||
@synthesize location = _location;
|
||||
|
||||
- (instancetype)initWithReasonMessage:(NSString *)reason andStreamLocation:(NSUInteger)location
|
||||
- (instancetype)initWithCode:(NSString *)code details:(NSString *)details location:(NSUInteger)location
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.type = HTMLTokenTypeParseError;
|
||||
_reason = [reason copy];
|
||||
_code = [code copy];
|
||||
_details = [details copy];
|
||||
_location = location;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)other
|
||||
{
|
||||
if ([other isKindOfClass:[self class]]) {
|
||||
HTMLParseErrorToken *token = (HTMLParseErrorToken *)other;
|
||||
return bothNilOrEqual(self.code, token.code);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
return self.code.hash + self.code.hash;
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"<%@: %p Reason='%@' Location='%lu'>", self.class, self, _reason, (unsigned long)_location];
|
||||
return [NSString stringWithFormat:@"<%@: %p Code='%@' Details='%@' Location='%lu'>", self.class, self, _code, _details, (unsigned long)_location];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+30
-52
@@ -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 ()
|
||||
{
|
||||
@@ -80,6 +73,10 @@
|
||||
|
||||
_tokenizer = [[HTMLTokenizer alloc] initWithString:string ?: @""];
|
||||
_tokenizer.parser = self;
|
||||
__weak HTMLParser *weakSelf = self;
|
||||
_tokenizer.parseErrorCallback = ^(HTMLParseErrorToken *token) {
|
||||
[weakSelf emitParseError:@"Tokenization error: %@", token.asParseError];
|
||||
};
|
||||
|
||||
_pendingTableCharacterTokens = [[HTMLCharacterToken alloc] initWithString:@""];
|
||||
|
||||
@@ -137,6 +134,10 @@
|
||||
[self initializeDocument];
|
||||
_tokenizer = [[HTMLTokenizer alloc] initWithString:_tokenizer.string];
|
||||
_tokenizer.parser = self;
|
||||
__weak HTMLParser *weakSelf = self;
|
||||
_tokenizer.parseErrorCallback = ^(HTMLParseErrorToken *token) {
|
||||
[weakSelf emitParseError:@"Tokenization error: %@", token.asParseError];
|
||||
};
|
||||
|
||||
_contextElement = contextElement;
|
||||
_fragmentParsingAlgorithm = YES;
|
||||
@@ -233,11 +234,6 @@
|
||||
return NO;
|
||||
};
|
||||
|
||||
if (token.isParseError) {
|
||||
[self emitParseError:@"Tokenizer Parser Error: %@", token.asParseError];
|
||||
return;
|
||||
}
|
||||
|
||||
if (_ignoreNextLineFeedCharacterToken) {
|
||||
_ignoreNextLineFeedCharacterToken = NO;
|
||||
if (token.isCharacterToken) {
|
||||
@@ -457,9 +453,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];
|
||||
@@ -519,7 +517,7 @@
|
||||
|
||||
- (void)generateImpliedEndTagsExceptForElement:(NSString *)tagName
|
||||
{
|
||||
while ([self.currentNode.tagName isEqualToAny:@"dd", @"dt", @"li", @"menuitem", @"option", @"optgroup", @"p", @"rb", @"rp", @"rt", @"rtc", nil] &&
|
||||
while ([self.currentNode.tagName isEqualToAny:@"dd", @"dt", @"li", @"option", @"optgroup", @"p", @"rb", @"rp", @"rt", @"rtc", nil] &&
|
||||
![self.currentNode.tagName isEqualToString:tagName]) {
|
||||
[_stackOfOpenElements popCurrentNode];
|
||||
}
|
||||
@@ -1144,7 +1142,7 @@
|
||||
if (charactes.length > 0) {
|
||||
[self reconstructActiveFormattingElements];
|
||||
[self insertCharacters:charactes];
|
||||
if (!charactes.isHTMLWhitespaceString) {
|
||||
if (!charactes.htmlkit_isHTMLWhitespaceString) {
|
||||
_framesetOkFlag = NO;
|
||||
}
|
||||
}
|
||||
@@ -1167,8 +1165,8 @@
|
||||
[self HTMLInsertionModeInTemplate:token];
|
||||
} else {
|
||||
for (HTMLElement *node in _stackOfOpenElements) {
|
||||
if ([node.tagName isEqualToAny:@"dd", @"dt", @"li", @"menuitem", @"optgroup", @"option", @"p", @"rb",
|
||||
@"rp", @"rt", @"rtc", @"tbody", @"td", @"tfoot", @"th", @"thead", @"tr", @"body", @"html", nil]) {
|
||||
if ([node.tagName isEqualToAny:@"dd", @"dt", @"li", @"optgroup", @"option", @"p", @"rb", @"rp",
|
||||
@"rt", @"rtc", @"tbody", @"td", @"tfoot", @"th", @"thead", @"tr", @"body", @"html", nil]) {
|
||||
[self emitParseError:@"EOF reached with unclosed element <%@> in <body>", node.tagName];
|
||||
break;
|
||||
}
|
||||
@@ -1231,20 +1229,12 @@
|
||||
[self switchInsertionMode:HTMLInsertionModeInFrameset];
|
||||
} else if ([tagName isEqualToAny:@"address", @"article", @"aside", @"blockquote", @"center",
|
||||
@"details", @"dialog", @"dir", @"div", @"dl", @"fieldset", @"figcaption", @"figure",
|
||||
@"footer", @"header", @"hgroup", @"main", @"nav", @"ol", @"p", @"section",
|
||||
@"footer", @"header", @"hgroup", @"main", @"menu", @"nav", @"ol", @"p", @"section",
|
||||
@"summary", @"ul", nil]) {
|
||||
if ([_stackOfOpenElements hasElementInButtonScopeWithTagName:@"p"]) {
|
||||
[self closePElement];
|
||||
}
|
||||
[self insertElementForToken:token];
|
||||
} else if ([tagName isEqualToString:@"menu"]) {
|
||||
if ([_stackOfOpenElements hasElementInButtonScopeWithTagName:@"p"]) {
|
||||
[self closePElement];
|
||||
}
|
||||
if ([self.currentNode.tagName isEqualToString:@"menuitem"]) {
|
||||
[_stackOfOpenElements popCurrentNode];
|
||||
}
|
||||
[self insertElementForToken:token];
|
||||
} else if ([tagName isEqualToAny:@"h1", @"h2", @"h3", @"h4", @"h5", @"h6", nil]) {
|
||||
if ([_stackOfOpenElements hasElementInButtonScopeWithTagName:@"p"]) {
|
||||
[self closePElement];
|
||||
@@ -1285,7 +1275,7 @@
|
||||
@"dd": @[@"dd", @"dt"],
|
||||
@"dt": @[@"dd", @"dt"]};
|
||||
|
||||
for (HTMLElement *node in _stackOfOpenElements.reverseObjectEnumerator.allObjects) {
|
||||
for (HTMLElement *node in _stackOfOpenElements.reverseObjectEnumerator) {
|
||||
if ([map[tagName] containsObject:node.tagName]) {
|
||||
[self generateImpliedEndTagsExceptForElement:node.tagName];
|
||||
if (![self.currentNode.tagName isEqualToString:node.tagName]) {
|
||||
@@ -1388,9 +1378,6 @@
|
||||
if ([_stackOfOpenElements hasElementInButtonScopeWithTagName:@"p"]) {
|
||||
[self closePElement];
|
||||
}
|
||||
if ([self.currentNode.tagName isEqualToString:@"menuitem"]) {
|
||||
[_stackOfOpenElements popCurrentNode];
|
||||
}
|
||||
[self insertElementForToken:token];
|
||||
[_stackOfOpenElements popCurrentNode];
|
||||
_framesetOkFlag = NO;
|
||||
@@ -1436,12 +1423,6 @@
|
||||
}
|
||||
[self reconstructActiveFormattingElements];
|
||||
[self insertElementForToken:token];
|
||||
} else if ([tagName isEqualToString:@"menuitem"]) {
|
||||
if ([self.currentNode.tagName isEqualToString:@"menuitem"]) {
|
||||
[_stackOfOpenElements popCurrentNode];
|
||||
}
|
||||
[self reconstructActiveFormattingElements];
|
||||
[self insertElementForToken:token];
|
||||
} else if ([tagName isEqualToAny:@"rb", @"rtc", nil]) {
|
||||
if ([_stackOfOpenElements hasElementInScopeWithTagName:@"ruby"]) {
|
||||
[self generateImpliedEndTagsExceptForElement:nil];
|
||||
@@ -1462,7 +1443,6 @@
|
||||
} else if ([tagName isEqualToString:@"math"]) {
|
||||
[self reconstructActiveFormattingElements];
|
||||
AdjustMathMLAttributes(token);
|
||||
AdjustForeignAttributes(token);
|
||||
[self insertForeignElementForToken:token inNamespace:HTMLNamespaceMathML];
|
||||
if (token.isSelfClosing) {
|
||||
[_stackOfOpenElements popCurrentNode];
|
||||
@@ -1470,7 +1450,6 @@
|
||||
} else if ([tagName isEqualToString:@"svg"]) {
|
||||
[self reconstructActiveFormattingElements];
|
||||
AdjustSVGAttributes(token);
|
||||
AdjustForeignAttributes(token);
|
||||
[self insertForeignElementForToken:token inNamespace:HTMLNamespaceSVG];
|
||||
if (token.isSelfClosing) {
|
||||
[_stackOfOpenElements popCurrentNode];
|
||||
@@ -1496,8 +1475,8 @@
|
||||
[self emitParseError:@"Unexpected end tag </body> without body element in scope in <body>"];
|
||||
}
|
||||
for (HTMLElement *node in _stackOfOpenElements) {
|
||||
if ([node.tagName isEqualToAny:@"dd", @"dt", @"li", @"menuitem", @"optgroup", @"option", @"p", @"rb", @"rp",
|
||||
@"rt", @"rtc", @"tbody", @"td", @"tfoot", @"th", @"thead", @"tr", @"body", @"html", nil]) {
|
||||
if ([node.tagName isEqualToAny:@"dd", @"dt", @"li", @"optgroup", @"option", @"p", @"rb", @"rp", @"rt",
|
||||
@"rtc", @"tbody", @"td", @"tfoot", @"th", @"thead", @"tr", @"body", @"html", nil]) {
|
||||
[self emitParseError:@"Misnested end tag </%@> with open element <%@> in <body>", tagName, node.tagName];
|
||||
break;
|
||||
}
|
||||
@@ -1551,7 +1530,7 @@
|
||||
}
|
||||
[self closePElement];
|
||||
} else if ([tagName isEqualToString:@"li"]) {
|
||||
if (![_stackOfOpenElements hasElementInListItemScopeWithTagName:@"li"]) {
|
||||
if (![_stackOfOpenElements hasElementInListItemScopeWithTagName:tagName]) {
|
||||
[self emitParseError:@"Unexpected <li> element in <body>"];
|
||||
return;
|
||||
}
|
||||
@@ -1571,7 +1550,7 @@
|
||||
}
|
||||
[_stackOfOpenElements popElementsUntilElementPoppedWithTagName:tagName];
|
||||
} else if ([tagName isEqualToAny:@"h1", @"h2", @"h3", @"h4", @"h5", @"h6", nil]) {
|
||||
if (![_stackOfOpenElements hasAnyElementInScopeWithAnyOfTagNames:@[@"h1", @"h2", @"h3", @"h4", @"h5", @"h6"]]) {
|
||||
if (![_stackOfOpenElements hasHeaderElementInScope]) {
|
||||
[self emitParseError:@"Unexpected <%@> element in <body>", tagName];
|
||||
return;
|
||||
}
|
||||
@@ -1591,7 +1570,7 @@
|
||||
return;
|
||||
}
|
||||
} else if ([tagName isEqualToAny:@"applet", @"marquee", @"object", nil]) {
|
||||
if (![_stackOfOpenElements hasAnyElementInScopeWithAnyOfTagNames:@[@"applet", @"marquee", @"object"]]) {
|
||||
if (![_stackOfOpenElements hasElementInScopeWithTagName:tagName]) {
|
||||
[self emitParseError:@"Unexpected <%@> element in <body>", tagName];
|
||||
return;
|
||||
}
|
||||
@@ -1612,7 +1591,7 @@
|
||||
|
||||
- (void)processAnyOtherEndTagTokenInBody:(HTMLTagToken *)token
|
||||
{
|
||||
for (HTMLElement *node in _stackOfOpenElements.reverseObjectEnumerator.allObjects) {
|
||||
for (HTMLElement *node in _stackOfOpenElements.reverseObjectEnumerator) {
|
||||
if ([node.tagName isEqualToString:token.tagName]) {
|
||||
[self generateImpliedEndTagsExceptForElement:token.tagName];
|
||||
if (![node.tagName isEqualToString:self.currentNode.tagName]) {
|
||||
@@ -2334,7 +2313,7 @@
|
||||
[characters enumerateSubstringsInRange:NSMakeRange(0, characters.length)
|
||||
options:NSStringEnumerationByComposedCharacterSequences
|
||||
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
|
||||
if (substring.isHTMLWhitespaceString) {
|
||||
if (substring.htmlkit_isHTMLWhitespaceString) {
|
||||
[self insertCharacters:substring];
|
||||
} else {
|
||||
[self emitParseError:@"Unexpected Character (%@) in <frameset>", substring];
|
||||
@@ -2402,7 +2381,7 @@
|
||||
[characters enumerateSubstringsInRange:NSMakeRange(0, characters.length)
|
||||
options:NSStringEnumerationByComposedCharacterSequences
|
||||
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
|
||||
if (substring.isHTMLWhitespaceString) {
|
||||
if (substring.htmlkit_isHTMLWhitespaceString) {
|
||||
[self insertCharacters:substring];
|
||||
} else {
|
||||
[self emitParseError:@"Unexpected Character (%@) after <frameset>", substring];
|
||||
@@ -2536,7 +2515,7 @@
|
||||
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
|
||||
if ([substring isEqualToString:@"\uFFFD"]) {
|
||||
[self emitParseError:@"Unexpected Character (0x0000) in foreign content"];
|
||||
} else if (!substring.isHTMLWhitespaceString) {
|
||||
} else if (!substring.htmlkit_isHTMLWhitespaceString) {
|
||||
_framesetOkFlag = NO;
|
||||
}
|
||||
[self insertCharacters:substring];
|
||||
@@ -2552,7 +2531,7 @@
|
||||
return;
|
||||
case HTMLTokenTypeStartTag:
|
||||
{
|
||||
void (^ anythingElse)() = ^ {
|
||||
void (^ anythingElse)(void) = ^ {
|
||||
if (self.adjustedCurrentNode.htmlNamespace == HTMLNamespaceMathML) {
|
||||
AdjustMathMLAttributes(token.asTagToken);
|
||||
}
|
||||
@@ -2560,14 +2539,13 @@
|
||||
AdjustSVGNameCase(token.asTagToken);
|
||||
AdjustSVGAttributes(token.asTagToken);
|
||||
}
|
||||
AdjustForeignAttributes(token.asTagToken);
|
||||
[self insertForeignElementForToken:token.asTagToken inNamespace:self.adjustedCurrentNode.htmlNamespace];
|
||||
if (token.asTagToken.selfClosing) {
|
||||
[_stackOfOpenElements popCurrentNode];
|
||||
}
|
||||
};
|
||||
|
||||
void (^ matchedCase)() = ^ {
|
||||
void (^ matchedCase)(void) = ^ {
|
||||
[self emitParseError:@"Unexpected start tag <%@> in foreign content", token.asTagToken.tagName];
|
||||
if (_fragmentParsingAlgorithm) {
|
||||
anythingElse();
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// HTMLQuircksMode.m
|
||||
// HTMLKit
|
||||
//
|
||||
// Created by Iska on 26.03.19.
|
||||
// Copyright © 2019 BrainCookie. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "HTMLQuirksMode.h"
|
||||
#import "NSString+Private.h"
|
||||
|
||||
BOOL QuirksModePrefixMatch(NSString *publicIdentifier)
|
||||
{
|
||||
for (int i = 0; i < sizeof(HTMLQuirksModePrefixes) / sizeof(HTMLQuirksModePrefixes[0]); i++) {
|
||||
if ([publicIdentifier hasPrefixIgnoringCase:HTMLQuirksModePrefixes[i]]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
@@ -0,0 +1,767 @@
|
||||
//
|
||||
// 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)initWithDocument:(HTMLDocument *)document
|
||||
{
|
||||
return [self initWithDocument:document startContainer:document startOffset:0 endContainer:document endOffset:0];
|
||||
}
|
||||
|
||||
- (instancetype)initWithDowcument:(HTMLDocument *)document
|
||||
{
|
||||
return [self initWithDocument: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
|
||||
{
|
||||
return [self initWithDocument:document
|
||||
startContainer:startContainer startOffset:startOffset
|
||||
endContainer:endContainer endOffset:endOffset];
|
||||
}
|
||||
|
||||
- (instancetype)initWithDocument:(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] initWithDocument:_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] initWithDocument:_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] initWithDocument:_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] initWithDocument:_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
|
||||
@@ -14,7 +14,6 @@
|
||||
@interface HTMLStackOfOpenElements ()
|
||||
{
|
||||
NSMutableArray *_stack;
|
||||
NSDictionary *_specificScopeElementTypes;
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -27,26 +26,6 @@
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_stack = [NSMutableArray new];
|
||||
_specificScopeElementTypes = @{
|
||||
@"applet": @(HTMLNamespaceHTML),
|
||||
@"caption": @(HTMLNamespaceHTML),
|
||||
@"html": @(HTMLNamespaceHTML),
|
||||
@"table": @(HTMLNamespaceHTML),
|
||||
@"td": @(HTMLNamespaceHTML),
|
||||
@"th": @(HTMLNamespaceHTML),
|
||||
@"marquee": @(HTMLNamespaceHTML),
|
||||
@"object": @(HTMLNamespaceHTML),
|
||||
@"template": @(HTMLNamespaceHTML),
|
||||
@"mi": @(HTMLNamespaceMathML),
|
||||
@"mo": @(HTMLNamespaceMathML),
|
||||
@"mn": @(HTMLNamespaceMathML),
|
||||
@"ms": @(HTMLNamespaceMathML),
|
||||
@"mtext": @(HTMLNamespaceMathML),
|
||||
@"annotation-xml": @(HTMLNamespaceMathML),
|
||||
@"foreignObject": @(HTMLNamespaceSVG),
|
||||
@"desc": @(HTMLNamespaceSVG),
|
||||
@"title": @(HTMLNamespaceSVG)
|
||||
};
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -195,82 +174,148 @@
|
||||
|
||||
#pragma mark - Element Scope
|
||||
|
||||
NS_INLINE BOOL IsSpecificScopeElement(HTMLElement *element)
|
||||
{
|
||||
switch (element.htmlNamespace) {
|
||||
case HTMLNamespaceHTML:
|
||||
return [element.tagName isEqualToAny:@"applet", @"caption", @"html", @"table", @"td", @"th", @"marquee", @"object", @"template", nil];
|
||||
case HTMLNamespaceMathML:
|
||||
return [element.tagName isEqualToAny:@"mi", @"mo", @"mn", @"ms", @"mtext", @"annotation-xml", nil];
|
||||
case HTMLNamespaceSVG:
|
||||
return [element.tagName isEqualToAny:@"foreignObject", @"desc", @"title", nil];
|
||||
}
|
||||
}
|
||||
|
||||
NS_INLINE BOOL IsHeaderElement(HTMLElement *element)
|
||||
{
|
||||
if (element.htmlNamespace != HTMLNamespaceHTML) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return [element.tagName isEqualToAny:@"h1", @"h2", @"h3", @"h4", @"h5", @"h6", nil];
|
||||
}
|
||||
|
||||
NS_INLINE BOOL IsTableScopeElement(HTMLElement *element)
|
||||
{
|
||||
if (element.htmlNamespace != HTMLNamespaceHTML) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return [element.tagName isEqualToAny:@"html", @"table", @"template", nil];
|
||||
}
|
||||
|
||||
NS_INLINE BOOL IsListItemScopeElement(HTMLElement *element)
|
||||
{
|
||||
if (element.htmlNamespace != HTMLNamespaceHTML) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return [element.tagName isEqualToAny:@"ol", @"ul", nil];
|
||||
}
|
||||
|
||||
NS_INLINE BOOL IsSelectScopeElement(HTMLElement *element)
|
||||
{
|
||||
if (element.htmlNamespace != HTMLNamespaceHTML) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return ![element.tagName isEqualToString:@"optgroup"] && ![element.tagName isEqualToString:@"option"];
|
||||
}
|
||||
|
||||
NS_INLINE BOOL IsButtonScopeElement(HTMLElement *element)
|
||||
{
|
||||
if (element.htmlNamespace != HTMLNamespaceHTML) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return [element.tagName isEqualToString:@"button"];
|
||||
}
|
||||
|
||||
- (HTMLElement *)hasElementInScopeWithTagName:(NSString *)tagName;
|
||||
{
|
||||
return [self hasAnyElementInSpecificScopeWithTagNames:@[tagName] andElementTypes:_specificScopeElementTypes];
|
||||
}
|
||||
|
||||
- (HTMLElement *)hasAnyElementInScopeWithAnyOfTagNames:(NSArray *)tagNames
|
||||
{
|
||||
return [self hasAnyElementInSpecificScopeWithTagNames:tagNames andElementTypes:_specificScopeElementTypes];
|
||||
}
|
||||
|
||||
- (HTMLElement *)hasElementInListItemScopeWithTagName:(NSString *)tagName
|
||||
{
|
||||
NSMutableDictionary *elementTypes = [NSMutableDictionary dictionaryWithDictionary:_specificScopeElementTypes];
|
||||
[elementTypes addEntriesFromDictionary:@{@"ol": @(HTMLNamespaceHTML),
|
||||
@"ul": @(HTMLNamespaceHTML)}];
|
||||
|
||||
return [self hasElementInSpecificScopeWithTagName:tagName
|
||||
andElementTypes:elementTypes];
|
||||
}
|
||||
|
||||
- (HTMLElement *)hasElementInButtonScopeWithTagName:(NSString *)tagName
|
||||
{
|
||||
NSMutableDictionary *elementTypes = [NSMutableDictionary dictionaryWithDictionary:_specificScopeElementTypes];
|
||||
[elementTypes addEntriesFromDictionary:@{@"button": @(HTMLNamespaceHTML)}];
|
||||
|
||||
return [self hasElementInSpecificScopeWithTagName:tagName
|
||||
andElementTypes:elementTypes];
|
||||
}
|
||||
|
||||
- (HTMLElement *)hasElementInTableScopeWithTagName:(NSString *)tagName
|
||||
{
|
||||
return [self hasElementInSpecificScopeWithTagName:tagName
|
||||
andElementTypes:@{@"html": @(HTMLNamespaceHTML),
|
||||
@"table": @(HTMLNamespaceHTML),
|
||||
@"template": @(HTMLNamespaceHTML)}];
|
||||
}
|
||||
|
||||
- (HTMLElement *)hasElementInTableScopeWithAnyOfTagNames:(NSArray *)tagNames
|
||||
{
|
||||
return [self hasAnyElementInSpecificScopeWithTagNames:tagNames
|
||||
andElementTypes:@{@"html": @(HTMLNamespaceHTML),
|
||||
@"table": @(HTMLNamespaceHTML),
|
||||
@"template": @(HTMLNamespaceHTML)}];
|
||||
}
|
||||
|
||||
- (HTMLElement *)hasElementInSelectScopeWithTagName:(NSString *)tagName
|
||||
{
|
||||
for (HTMLElement *node in _stack.reverseObjectEnumerator) {
|
||||
if ([node.tagName isEqualToString:tagName]) {
|
||||
if (node.htmlNamespace == HTMLNamespaceHTML && [tagName isEqualToString:node.tagName]) {
|
||||
return node;
|
||||
}
|
||||
if (!(node.htmlNamespace == HTMLNamespaceHTML &&
|
||||
[node.tagName isEqualToAny:@"optgroup", @"option", nil])) {
|
||||
if (IsSpecificScopeElement(node)) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (HTMLElement *)hasElementInSpecificScopeWithTagName:(NSString *)tagName
|
||||
andElementTypes:(NSDictionary *)elementTypes
|
||||
{
|
||||
return [self hasAnyElementInSpecificScopeWithTagNames:@[tagName] andElementTypes:elementTypes];
|
||||
}
|
||||
|
||||
- (HTMLElement *)hasAnyElementInSpecificScopeWithTagNames:(NSArray *)tagNames
|
||||
andElementTypes:(NSDictionary *)elementTypes
|
||||
- (HTMLElement *)hasHeaderElementInScope
|
||||
{
|
||||
for (HTMLElement *node in _stack.reverseObjectEnumerator) {
|
||||
if ([tagNames containsObject:node.tagName]) {
|
||||
NSNumber *namespace = elementTypes[node.tagName] ?: @(HTMLNamespaceHTML);
|
||||
if ([namespace isEqual:@(node.htmlNamespace)]) {
|
||||
return node;
|
||||
}
|
||||
if (IsHeaderElement(node)) {
|
||||
return node;
|
||||
}
|
||||
if ([elementTypes[node.tagName] isEqual:@(node.htmlNamespace)]) {
|
||||
if (IsSpecificScopeElement(node)) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (HTMLElement *)hasElementInTableScopeWithTagName:(NSString *)tagName
|
||||
{
|
||||
for (HTMLElement *node in _stack.reverseObjectEnumerator) {
|
||||
if (node.htmlNamespace == HTMLNamespaceHTML && [tagName isEqualToString:node.tagName]) {
|
||||
return node;
|
||||
}
|
||||
if (IsTableScopeElement(node)) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (HTMLElement *)hasElementInTableScopeWithAnyOfTagNames:(NSArray *)tagNames
|
||||
{
|
||||
for (HTMLElement *node in _stack.reverseObjectEnumerator) {
|
||||
if (node.htmlNamespace == HTMLNamespaceHTML && [tagNames containsObject:node.tagName]) {
|
||||
return node;
|
||||
}
|
||||
if (IsTableScopeElement(node)) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (HTMLElement *)hasElementInListItemScopeWithTagName:(NSString *)tagName
|
||||
{
|
||||
for (HTMLElement *node in _stack.reverseObjectEnumerator) {
|
||||
if (node.htmlNamespace == HTMLNamespaceHTML && [tagName isEqualToString:node.tagName]) {
|
||||
return node;
|
||||
}
|
||||
if (IsSpecificScopeElement(node) || IsListItemScopeElement(node)) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (HTMLElement *)hasElementInButtonScopeWithTagName:(NSString *)tagName
|
||||
{
|
||||
for (HTMLElement *node in _stack.reverseObjectEnumerator) {
|
||||
if (node.htmlNamespace == HTMLNamespaceHTML && [tagName isEqualToString:node.tagName]) {
|
||||
return node;
|
||||
}
|
||||
if (IsSpecificScopeElement(node) || IsButtonScopeElement(node)) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (HTMLElement *)hasElementInSelectScopeWithTagName:(NSString *)tagName
|
||||
{
|
||||
for (HTMLElement *node in _stack.reverseObjectEnumerator) {
|
||||
if (node.htmlNamespace == HTMLNamespaceHTML && [tagName isEqualToString:node.tagName]) {
|
||||
return node;
|
||||
}
|
||||
if (IsSelectScopeElement(node)) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+43
-29
@@ -8,8 +8,10 @@
|
||||
|
||||
#import "HTMLText.h"
|
||||
#import "HTMLElement.h"
|
||||
#import "NSString+HTMLKit.h"
|
||||
#import "HTMLNode+Private.h"
|
||||
#import "NSString+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
|
||||
@@ -59,12 +74,11 @@
|
||||
@"plaintext", @"noscript", nil]) {
|
||||
return self.data;
|
||||
} else {
|
||||
NSRange range = NSMakeRange(0, self.data.length);
|
||||
NSMutableString *escaped = [self.data mutableCopy];
|
||||
[escaped replaceOccurrencesOfString:@"&" withString:@"&" options:0 range:range];
|
||||
[escaped replaceOccurrencesOfString:@"\00A0" withString:@" " options:0 range:range];
|
||||
[escaped replaceOccurrencesOfString:@"<" withString:@"<" options:0 range:range];
|
||||
[escaped replaceOccurrencesOfString:@">" withString:@">" options:0 range:range];
|
||||
[escaped replaceOccurrencesOfString:@"&" withString:@"&" options:0 range:NSMakeRange(0, escaped.length)];
|
||||
[escaped replaceOccurrencesOfString:@"\00A0" withString:@" " options:0 range:NSMakeRange(0, escaped.length)];
|
||||
[escaped replaceOccurrencesOfString:@"<" withString:@"<" options:0 range:NSMakeRange(0, escaped.length)];
|
||||
[escaped replaceOccurrencesOfString:@">" withString:@">" options:0 range:NSMakeRange(0, escaped.length)];
|
||||
return escaped;
|
||||
}
|
||||
}
|
||||
|
||||
+822
-655
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@
|
||||
|
||||
@implementation NSCharacterSet (HTMLKit)
|
||||
|
||||
+ (instancetype)HTMLWhitespaceCharacterSet
|
||||
+ (instancetype)htmlkit_HTMLWhitespaceCharacterSet
|
||||
{
|
||||
static NSCharacterSet *set = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
@@ -20,7 +20,7 @@
|
||||
return set;
|
||||
}
|
||||
|
||||
+ (instancetype)HTMLHexNumberCharacterSet
|
||||
+ (instancetype)htmlkit_HTMLHexNumberCharacterSet
|
||||
{
|
||||
static NSCharacterSet *set = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
@@ -30,7 +30,7 @@
|
||||
return set;
|
||||
}
|
||||
|
||||
+ (instancetype)CSSNthExpressionCharacterSet
|
||||
+ (instancetype)htmlkit_CSSNthExpressionCharacterSet
|
||||
{
|
||||
static NSCharacterSet *set = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
|
||||
@@ -15,37 +15,12 @@ NS_INLINE BOOL isHtmlWhitespaceChar(unichar c)
|
||||
|
||||
@implementation NSString (HTMLKit)
|
||||
|
||||
- (BOOL)isEqualToStringIgnoringCase:(NSString *)aString
|
||||
- (BOOL)htmlkit_isHTMLWhitespaceString
|
||||
{
|
||||
return [self caseInsensitiveCompare:aString] == NSOrderedSame;
|
||||
return self.htmlkit_leadingHTMLWhitespaceLength == self.length;
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToAny:(NSString *)first, ... NS_REQUIRES_NIL_TERMINATION
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, first);
|
||||
for (NSString *next = first; next != nil; next = va_arg(list, NSString *)) {
|
||||
if ([self isEqualToString:next]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
va_end(list);
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)hasPrefixIgnoringCase:(NSString *)aString
|
||||
{
|
||||
NSRange reange = [self rangeOfString:aString
|
||||
options:NSAnchoredSearch|NSCaseInsensitiveSearch];
|
||||
return reange.location != NSNotFound;
|
||||
}
|
||||
|
||||
- (BOOL)isHTMLWhitespaceString
|
||||
{
|
||||
return self.leadingHTMLWhitespaceLength == self.length;
|
||||
}
|
||||
|
||||
- (NSUInteger)leadingHTMLWhitespaceLength
|
||||
- (NSUInteger)htmlkit_leadingHTMLWhitespaceLength
|
||||
{
|
||||
size_t idx = 0;
|
||||
NSUInteger length = self.length;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// NSString+Private.m
|
||||
// HTMLKit
|
||||
//
|
||||
// Created by Iska on 26.03.19.
|
||||
// Copyright © 2019 BrainCookie. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSString+Private.h"
|
||||
|
||||
@implementation NSString (Private)
|
||||
|
||||
- (BOOL)isEqualToStringIgnoringCase:(NSString *)aString
|
||||
{
|
||||
return [self caseInsensitiveCompare:aString] == NSOrderedSame;
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToAny:(NSString *)first, ... NS_REQUIRES_NIL_TERMINATION
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, first);
|
||||
for (NSString *next = first; next != nil; next = va_arg(list, NSString *)) {
|
||||
if ([self isEqualToString:next]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
va_end(list);
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)hasPrefixIgnoringCase:(NSString *)aString
|
||||
{
|
||||
NSRange reange = [self rangeOfString:aString
|
||||
options:NSAnchoredSearch|NSCaseInsensitiveSearch];
|
||||
return reange.location != NSNotFound;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
Initializes and returns a CSS has-descendant selector, e.g. 'div:has(p)'
|
||||
|
||||
@discussion 'div:has(p)' matches all <div> elements which have a descendant <p> element.
|
||||
@discussion 'div:has(p)' matches all <div> elements which have a descendant <p> element.
|
||||
|
||||
@param selector The selector matching a descendant element.
|
||||
@return A new instance of the has-descendant selector.
|
||||
|
||||
@@ -76,7 +76,7 @@ extern NSString * _Nonnull NSStringFromNthExpression(CSSNthExpression expression
|
||||
@param string The selector string which will be parsed.
|
||||
@return A new instance of a parsed CSS Selector, `nil` if the string is not a valid selector string.
|
||||
*/
|
||||
+ (nullable instancetype)selectorWithString:(NSString *)stirng;
|
||||
+ (nullable instancetype)selectorWithString:(NSString *)string;
|
||||
|
||||
/**
|
||||
Implementations should override this method to provide the selector-sprecific logic for matching elements.
|
||||
|
||||
@@ -29,7 +29,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@see CSSelector
|
||||
*/
|
||||
+ (CSSSelector *)parseSelector:(NSString *)string error:(NSError **)error;
|
||||
+ (nullable CSSSelector *)parseSelector:(NSString *)string error:(NSError **)error;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@return The universal CSS selector.
|
||||
*/
|
||||
extern CSSSelector * universalSelector();
|
||||
extern CSSSelector * universalSelector(void);
|
||||
|
||||
/**
|
||||
CSS type selector, e.g. 'div', 'p', ...etc.
|
||||
@@ -125,7 +125,7 @@ extern CSSSelector * nthLastOfTypeSelector(CSSNthExpression expression);
|
||||
|
||||
@return Odd-Child selector.
|
||||
*/
|
||||
extern CSSSelector * oddSelector();
|
||||
extern CSSSelector * oddSelector(void);
|
||||
|
||||
/**
|
||||
CSS even-child selector: ':nth-child(even)'
|
||||
@@ -134,49 +134,49 @@ extern CSSSelector * oddSelector();
|
||||
|
||||
@return Even-Child selector.
|
||||
*/
|
||||
extern CSSSelector * evenSlector();
|
||||
extern CSSSelector * evenSlector(void);
|
||||
|
||||
/**
|
||||
CSS first-child selector: ':nth-child(1)'
|
||||
|
||||
@return First-Child selector.
|
||||
*/
|
||||
extern CSSSelector * firstChildSelector();
|
||||
extern CSSSelector * firstChildSelector(void);
|
||||
|
||||
/**
|
||||
CSS first-child selector: ':nth-last-child(1)'
|
||||
|
||||
@return First-Child selector.
|
||||
*/
|
||||
extern CSSSelector * lastChildSelector();
|
||||
extern CSSSelector * lastChildSelector(void);
|
||||
|
||||
/**
|
||||
CSS first-of-type selector: ':nth-first-of-type(1)'
|
||||
|
||||
@return First-Of-Type selector.
|
||||
*/
|
||||
extern CSSSelector * firstOfTypeSelector();
|
||||
extern CSSSelector * firstOfTypeSelector(void);
|
||||
|
||||
/**
|
||||
CSS last-of-type selector: ':nth-last-of-type(1)'
|
||||
|
||||
@return Last-Of-Type selector.
|
||||
*/
|
||||
extern CSSSelector * lastOfTypeSelector();
|
||||
extern CSSSelector * lastOfTypeSelector(void);
|
||||
|
||||
/**
|
||||
CSS only-child selector: ':first-child:last-child'
|
||||
|
||||
@return Only-Child selector.
|
||||
*/
|
||||
extern CSSSelector * onlyChildSelector();
|
||||
extern CSSSelector * onlyChildSelector(void);
|
||||
|
||||
/**
|
||||
CSS only-of-type selector: ':first-of-type:last-of-type'
|
||||
|
||||
@return Only-Of-Type selector.
|
||||
*/
|
||||
extern CSSSelector * onlyOfTypeSelector();
|
||||
extern CSSSelector * onlyOfTypeSelector(void);
|
||||
|
||||
#pragma mark - Combinators
|
||||
|
||||
@@ -225,7 +225,7 @@ extern CSSSelector * not(CSSSelector *selector);
|
||||
/**
|
||||
CSS has-descendant selector, e.g. 'div:has(p)'
|
||||
|
||||
@discussion 'div:has(p)' matches all <div> elements which have a descendant <p> element.
|
||||
@discussion 'div:has(p)' matches all <div> elements which have a descendant <p> element.
|
||||
|
||||
@param selector The selector matching a descendant element.
|
||||
@return A has-descendant selector.
|
||||
|
||||
@@ -15,102 +15,102 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
@return Root element selector: ':root'
|
||||
*/
|
||||
extern CSSSelector * rootSelector();
|
||||
extern CSSSelector * rootSelector(void);
|
||||
|
||||
/**
|
||||
@return Empy element selector: ':empty'
|
||||
*/
|
||||
extern CSSSelector * emptySelector();
|
||||
extern CSSSelector * emptySelector(void);
|
||||
|
||||
/**
|
||||
@return A parent element selector: ':parent'
|
||||
*/
|
||||
extern CSSSelector * parentSelector();
|
||||
extern CSSSelector * parentSelector(void);
|
||||
|
||||
/**
|
||||
@return A button element selector: ':button'
|
||||
*/
|
||||
extern CSSSelector * buttonSelector();
|
||||
extern CSSSelector * buttonSelector(void);
|
||||
|
||||
/**
|
||||
@return A checkbox element selector: ':checkbox'
|
||||
*/
|
||||
extern CSSSelector * checkboxSelector();
|
||||
extern CSSSelector * checkboxSelector(void);
|
||||
|
||||
/**
|
||||
@return A file element selector: ':file'
|
||||
*/
|
||||
extern CSSSelector * fileSelector();
|
||||
extern CSSSelector * fileSelector(void);
|
||||
|
||||
/**
|
||||
@return A header element selector: ':header'
|
||||
*/
|
||||
extern CSSSelector * headerSelector();
|
||||
extern CSSSelector * headerSelector(void);
|
||||
|
||||
/**
|
||||
@return An image element selector: ':image'
|
||||
*/
|
||||
extern CSSSelector * imageSelector();
|
||||
extern CSSSelector * imageSelector(void);
|
||||
|
||||
/**
|
||||
@return A parent element selector: ':parent'
|
||||
*/
|
||||
extern CSSSelector * inputSelector();
|
||||
extern CSSSelector * inputSelector(void);
|
||||
|
||||
/**
|
||||
@return A link element selector: ':link'
|
||||
*/
|
||||
extern CSSSelector * linkSelector();
|
||||
extern CSSSelector * linkSelector(void);
|
||||
|
||||
/**
|
||||
@return A password element selector: ':password'
|
||||
*/
|
||||
extern CSSSelector * passwordSelector();
|
||||
extern CSSSelector * passwordSelector(void);
|
||||
|
||||
/**
|
||||
@return A radio element selector: ':radio'
|
||||
*/
|
||||
extern CSSSelector * radioSelector();
|
||||
extern CSSSelector * radioSelector(void);
|
||||
|
||||
/**
|
||||
@return A reset element selector: ':reset'
|
||||
*/
|
||||
extern CSSSelector * resetSelector();
|
||||
extern CSSSelector * resetSelector(void);
|
||||
|
||||
/**
|
||||
@return A submit element selector: ':submit'
|
||||
*/
|
||||
extern CSSSelector * submitSelector();
|
||||
extern CSSSelector * submitSelector(void);
|
||||
|
||||
/**
|
||||
@return A text element selector: ':text'
|
||||
*/
|
||||
extern CSSSelector * textSelector();
|
||||
extern CSSSelector * textSelector(void);
|
||||
|
||||
/**
|
||||
@return An enabled element selector: ':enabled'
|
||||
*/
|
||||
extern CSSSelector * enabledSelector();
|
||||
extern CSSSelector * enabledSelector(void);
|
||||
|
||||
/**
|
||||
@return A disabled element selector: ':disabled'
|
||||
*/
|
||||
extern CSSSelector * disabledSelector();
|
||||
extern CSSSelector * disabledSelector(void);
|
||||
|
||||
/**
|
||||
@return A checked element selector: ':checked'
|
||||
*/
|
||||
extern CSSSelector * checkedSelector();
|
||||
extern CSSSelector * checkedSelector(void);
|
||||
|
||||
/**
|
||||
@return An optional element selector: ':optional'
|
||||
*/
|
||||
extern CSSSelector * optionalSelector();
|
||||
extern CSSSelector * optionalSelector(void);
|
||||
|
||||
/**
|
||||
@return A required element selector: ':required'
|
||||
*/
|
||||
extern CSSSelector * requiredSelector();
|
||||
extern CSSSelector * requiredSelector(void);
|
||||
|
||||
/**
|
||||
Less-than selector, e.g. 'lt(2)'
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
/**
|
||||
Initializes a new comment token.
|
||||
|
||||
@param string The string with which to initialize the token.
|
||||
@param data The string with which to initialize the token.
|
||||
@return A new instance of a comment token.
|
||||
*/
|
||||
- (instancetype)initWithData:(NSString *)data;
|
||||
|
||||
@@ -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"
|
||||
@@ -22,3 +24,5 @@
|
||||
#import "HTMLKitDOMExceptions.h"
|
||||
#import "HTMLNamespaces.h"
|
||||
#import "HTMLQuirksMode.h"
|
||||
|
||||
#import "HTMLOrderedDictionary.h"
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// HTMLDOMUtils.h
|
||||
// HTMLKit
|
||||
//
|
||||
// Created by Iska on 03/12/16.
|
||||
// Copyright © 2016 BrainCookie. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "HTMLDOM.h"
|
||||
|
||||
@class HTMLNode;
|
||||
|
||||
extern HTMLNode * GetCommonAncestorContainer(HTMLNode *nodeA, HTMLNode *nodeB);
|
||||
extern NSArray<HTMLNode *> * GetAncestorNodes(HTMLNode *node);
|
||||
@@ -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
|
||||
@@ -53,7 +53,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
The element's attribites.
|
||||
*/
|
||||
@property (nonatomic, strong) NSMutableDictionary *attributes;
|
||||
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSString *> *attributes;
|
||||
|
||||
/**
|
||||
@warning Use one of the initWithTagName: methods instead.
|
||||
@@ -63,7 +63,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
Initializes a new HTML element with the given tag name.
|
||||
|
||||
@param tagname The tag name.
|
||||
@param tagName The tag name.
|
||||
@return A new HTML element.
|
||||
*/
|
||||
- (instancetype)initWithTagName:(NSString *)tagName;
|
||||
@@ -71,21 +71,21 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
Initializes a new HTML element with the given tag name and attributes.
|
||||
|
||||
@param tagname The tag name.
|
||||
@param tagName The tag name.
|
||||
@param attributes The attributes.
|
||||
@return A new HTML element.
|
||||
*/
|
||||
- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes;
|
||||
- (instancetype)initWithTagName:(NSString *)tagName attributes:(nullable NSDictionary<NSString *, NSString *> *)attributes;
|
||||
|
||||
/**
|
||||
Initializes a new HTML element with the given tag name, namespace, and attributes.
|
||||
|
||||
@param tagname The tag name.
|
||||
@param namespace The namespace.
|
||||
@param tagName The tag name.
|
||||
@param htmlNamespace The HTML namespace.
|
||||
@param attributes The attributes.
|
||||
@return A new HTML element.
|
||||
*/
|
||||
- (instancetype)initWithTagName:(NSString *)tagName namespace:(HTMLNamespace)htmlNamespace attributes:(NSDictionary *)attributes;
|
||||
- (instancetype)initWithTagName:(NSString *)tagName namespace:(HTMLNamespace)htmlNamespace attributes:(nullable NSDictionary<NSString *, NSString *> *)attributes;
|
||||
|
||||
/**
|
||||
Checks whether this element has an attribute with the given name.
|
||||
@@ -107,7 +107,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
Set the value of the attribute with the given name.
|
||||
|
||||
@param value The value to set.
|
||||
@param name The attribute's name.
|
||||
@param attribute The attribute's name.
|
||||
*/
|
||||
- (void)setObject:(NSString *)value forKeyedSubscript:(NSString *)attribute;
|
||||
|
||||
|
||||
@@ -140,31 +140,3 @@ NS_INLINE void AdjustSVGNameCase(HTMLTagToken *token)
|
||||
NSString *replacement = replacements[token.tagName] ?: token.tagName;
|
||||
token.tagName = replacement;
|
||||
}
|
||||
|
||||
NS_INLINE void AdjustForeignAttributes(HTMLTagToken *token)
|
||||
{
|
||||
if (token.attributes == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSDictionary *replacements = @{ @"xlink:actuate": @"xlink actuate",
|
||||
@"xlink:arcrole": @"xlink arcrole",
|
||||
@"xlink:href": @"xlink href",
|
||||
@"xlink:role": @"xlink role",
|
||||
@"xlink:show": @"xlink show",
|
||||
@"xlink:title": @"xlink title",
|
||||
@"xlink:type": @"xlink type",
|
||||
@"xml:base": @"xml base",
|
||||
@"xml:lang": @"xml lang",
|
||||
@"xml:space": @"xml space",
|
||||
@"xmlns": @"xmlns",
|
||||
@"xmlns:xlink": @"xmlns xlink"};
|
||||
|
||||
HTMLOrderedDictionary *adjusted = [HTMLOrderedDictionary new];
|
||||
for (id key in token.attributes) {
|
||||
NSString *replacement = replacements[key] ?: key;
|
||||
adjusted[replacement] = token.attributes[key];
|
||||
}
|
||||
token.attributes = adjusted;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#import "HTMLElement.h"
|
||||
#import "HTMLNamespaces.h"
|
||||
#import "NSString+HTMLKit.h"
|
||||
#import "NSString+Private.h"
|
||||
|
||||
NS_INLINE BOOL IsNodeMathMLTextIntegrationPoint(HTMLElement *node)
|
||||
{
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
/**
|
||||
Typedef for the error callback block.
|
||||
|
||||
@param reason The string describing the reason of the reported error.
|
||||
@param code The standarized error-code
|
||||
@param details The string describing the reason of the reported error.
|
||||
*/
|
||||
typedef void (^ HTMLStreamReaderErrorCallback)(NSString *reason);
|
||||
typedef void (^ HTMLStreamReaderErrorCallback)(NSString *code, NSString *details);
|
||||
|
||||
/**
|
||||
* HTML Input Stream Reader processor conforming to the HTML standard
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -86,7 +91,7 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition)
|
||||
/**
|
||||
A read-only ordered set of child nodes.
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) NSOrderedSet *childNodes;
|
||||
@property (nonatomic, strong, readonly) NSOrderedSet<HTMLNode *> *childNodes;
|
||||
|
||||
/**
|
||||
The first child 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.
|
||||
|
||||
@@ -202,7 +232,7 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition)
|
||||
/**
|
||||
Returns the index of the given child element in the set of child nodes.
|
||||
|
||||
@param node The element.
|
||||
@param element The element.
|
||||
@return The index of the given element in the children set.
|
||||
*/
|
||||
- (NSUInteger)indexOfChildElement:(HTMLElement *)element;
|
||||
@@ -218,9 +248,9 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition)
|
||||
/**
|
||||
Prepends the given array of nodes to the set of child nodes.
|
||||
|
||||
@param node The nodes to prepend.
|
||||
@param nodes The nodes to prepend.
|
||||
*/
|
||||
- (void)prependNodes:(NSArray *)nodes;
|
||||
- (void)prependNodes:(NSArray<HTMLNode *> *)nodes;
|
||||
|
||||
/**
|
||||
Appends the given node to the set of child nodes.
|
||||
@@ -235,7 +265,7 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition)
|
||||
|
||||
@param nodes The nodes to append.
|
||||
*/
|
||||
- (void)appendNodes:(NSArray *)nodes;
|
||||
- (void)appendNodes:(NSArray<HTMLNode *> *)nodes;
|
||||
|
||||
/**
|
||||
Inserts a given node before a child node.
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -18,19 +18,22 @@
|
||||
*/
|
||||
@interface HTMLParseErrorToken : HTMLToken
|
||||
|
||||
/** @brief The error's reason message. */
|
||||
@property (nonatomic, copy) NSString *reason;
|
||||
/** @brief The parse error's code as specified at https://html.spec.whatwg.org/multipage/parsing.html#parse-errors. */
|
||||
@property (nonatomic, strong, readonly) NSString *code;
|
||||
|
||||
/** @brief Additional detailed error information. */
|
||||
@property (nonatomic, strong, readonly) NSString *details;
|
||||
|
||||
/** @brief The error's location in the stream. */
|
||||
@property (nonatomic, assign) NSUInteger location;
|
||||
@property (nonatomic, assign, readonly) NSUInteger location;
|
||||
|
||||
/**
|
||||
Initializes a new Parse Error token.
|
||||
|
||||
@param reason The error's reason message.
|
||||
@param code The parse error's as specified at https://html.spec.whatwg.org/multipage/parsing.html#parse-errors.
|
||||
@param location The error's location in the stream.
|
||||
@return A new instance of a parse error token.
|
||||
*/
|
||||
- (instancetype)initWithReasonMessage:(NSString *)reason andStreamLocation:(NSUInteger)location;
|
||||
- (instancetype)initWithCode:(NSString *)code details:(NSString *)details location:(NSUInteger)location;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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
|
||||
@@ -24,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
An array of errors that occurred during document parsing.
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) NSArray *parseErrors;
|
||||
@property (nonatomic, strong, readonly) NSArray<NSString *> *parseErrors;
|
||||
|
||||
/**
|
||||
The parsed HTML Document.
|
||||
@@ -67,7 +67,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@see HTMLElement
|
||||
*/
|
||||
- (NSArray *)parseFragmentWithContextElement:(HTMLElement *)contextElement;
|
||||
- (NSArray<HTMLNode *> *)parseFragmentWithContextElement:(HTMLElement *)contextElement;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
// Copyright (c) 2015 BrainCookie. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSString+HTMLKit.h"
|
||||
|
||||
/**
|
||||
HTML quirks modes
|
||||
https://html.spec.whatwg.org/multipage/infrastructure.html#quirks-mode
|
||||
@@ -82,12 +80,4 @@ static NSString * HTMLQuirksModePrefixes[] = {
|
||||
#undef QUIRKS_ENTRY
|
||||
};
|
||||
|
||||
NS_INLINE BOOL QuirksModePrefixMatch(NSString *publicIdentifier)
|
||||
{
|
||||
for (int i = 0; i < sizeof(HTMLQuirksModePrefixes) / sizeof(HTMLQuirksModePrefixes[0]); i++) {
|
||||
if ([publicIdentifier hasPrefixIgnoringCase:HTMLQuirksModePrefixes[i]]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
extern BOOL QuirksModePrefixMatch(NSString *publicIdentifier);
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,265 @@
|
||||
//
|
||||
// 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)initWithDocument:(HTMLDocument *)document;
|
||||
|
||||
/**
|
||||
Deprecated due to typo.
|
||||
*/
|
||||
- (instancetype)initWithDowcument:(HTMLDocument *)document __attribute__((deprecated("Replaced by -initWithDocument:")));
|
||||
|
||||
/**
|
||||
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)initWithDocument:(HTMLDocument *)document
|
||||
startContainer:(HTMLNode *)startContainer startOffset:(NSUInteger)startOffset
|
||||
endContainer:(HTMLNode *)endContainer endOffset:(NSUInteger)endOffset;
|
||||
|
||||
/**
|
||||
Deprecated due to typo.
|
||||
*/
|
||||
- (instancetype)initWithDowcument:(HTMLDocument *)document
|
||||
startContainer:(HTMLNode *)startContainer startOffset:(NSUInteger)startOffset
|
||||
endContainer:(HTMLNode *)endContainer endOffset:(NSUInteger)endOffset
|
||||
__attribute__((deprecated("Replaced by -initWithDocument:startContainer:startOffset:endContainer:endOffset:")));
|
||||
|
||||
/**
|
||||
Sets the start boundary.
|
||||
|
||||
@param node The new node of the start boundary.
|
||||
@param offset The new offset of the start boundary.
|
||||
*/
|
||||
- (void)setStartNode:(HTMLNode *)node startOffset:(NSUInteger)offset;
|
||||
|
||||
/**
|
||||
Sets the end boundary.
|
||||
|
||||
@param node The new node of the end boundary.
|
||||
@param offset 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
|
||||
@@ -79,7 +79,7 @@
|
||||
/**
|
||||
Checks whether an element with the given tag name is in the stack.
|
||||
|
||||
@param tagname The element's tag name.
|
||||
@param tagName The element's tag name.
|
||||
@return `YES` if such an element is in the stack, `NO` otherwise.
|
||||
*/
|
||||
- (BOOL)containsElementWithTagName:(NSString *)tagName;
|
||||
@@ -163,11 +163,11 @@
|
||||
https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-the-specific-scope
|
||||
*/
|
||||
- (HTMLElement *)hasElementInScopeWithTagName:(NSString *)tagName;
|
||||
- (HTMLElement *)hasAnyElementInScopeWithAnyOfTagNames:(NSArray *)tagNames;
|
||||
- (HTMLElement *)hasElementInListItemScopeWithTagName:(NSString *)tagName;
|
||||
- (HTMLElement *)hasElementInButtonScopeWithTagName:(NSString *)tagName;
|
||||
- (HTMLElement *)hasHeaderElementInScope;
|
||||
- (HTMLElement *)hasElementInTableScopeWithTagName:(NSString *)tagName;
|
||||
- (HTMLElement *)hasElementInTableScopeWithAnyOfTagNames:(NSArray *)tagNames;
|
||||
- (HTMLElement *)hasElementInListItemScopeWithTagName:(NSString *)tagName;
|
||||
- (HTMLElement *)hasElementInButtonScopeWithTagName:(NSString *)tagName;
|
||||
- (HTMLElement *)hasElementInSelectScopeWithTagName:(NSString *)tagName;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -16,6 +16,13 @@
|
||||
|
||||
@class HTMLParser;
|
||||
|
||||
/**
|
||||
Typedef for the parse error callback block.
|
||||
|
||||
@param token The parse error token.
|
||||
*/
|
||||
typedef void (^ HTMLTokenizerParseErrorCallback)(HTMLParseErrorToken *token);
|
||||
|
||||
/**
|
||||
* HTML Tokenizer
|
||||
* https://html.spec.whatwg.org/multipage/syntax.html#tokenization
|
||||
@@ -37,7 +44,14 @@
|
||||
|
||||
@see HTMLParser
|
||||
*/
|
||||
@property (nonatomic, weak, readonly) HTMLParser *parser;
|
||||
@property (nonatomic, weak) HTMLParser *parser;
|
||||
|
||||
/**
|
||||
An error callback block, which gets called when encountering parse errors while tokenizing the stream
|
||||
|
||||
Parse error tokens are dropped if the callback is `nil`.
|
||||
*/
|
||||
@property (nonatomic, copy) HTMLTokenizerParseErrorCallback parseErrorCallback;
|
||||
|
||||
/**
|
||||
Initializes a new Tokenizer with the given string.
|
||||
|
||||
@@ -23,11 +23,16 @@
|
||||
CHAR( DIGIT_NINE, 0x0039 ) \
|
||||
CHAR( LATIN_CAPITAL_LETTER_A, 0x0041 ) \
|
||||
CHAR( LATIN_CAPITAL_LETTER_F, 0x0046 ) \
|
||||
CHAR( LATIN_CAPITAL_LETTER_P, 0x0050 ) \
|
||||
CHAR( LATIN_CAPITAL_LETTER_S, 0x0053 ) \
|
||||
CHAR( LATIN_CAPITAL_LETTER_X, 0x0058 ) \
|
||||
CHAR( LATIN_CAPITAL_LETTER_Z, 0x005A ) \
|
||||
CHAR( RIGHT_SQUARE_BRACKET, 0x005D ) \
|
||||
CHAR( GRAVE_ACCENT, 0x0060 ) \
|
||||
CHAR( LATIN_SMALL_LETTER_A, 0x0061 ) \
|
||||
CHAR( LATIN_SMALL_LETTER_F, 0x0066 ) \
|
||||
CHAR( LATIN_SMALL_LETTER_P, 0x0070 ) \
|
||||
CHAR( LATIN_SMALL_LETTER_S, 0x0073 ) \
|
||||
CHAR( LATIN_SMALL_LETTER_X, 0x0078 ) \
|
||||
CHAR( LATIN_SMALL_LETTER_Z, 0x007A ) \
|
||||
CHAR( HYPHEN_MINUS, 0x002D ) \
|
||||
@@ -82,13 +87,17 @@ NUMERIC_REPLACEMENT_CHARACTERS
|
||||
#undef CHAR
|
||||
};
|
||||
|
||||
NS_INLINE BOOL isControlOrUndefinedCharacter(UTF32Char character)
|
||||
NS_INLINE BOOL isControlCharacter(unsigned long long character)
|
||||
{
|
||||
return ((character >= 0x0001 && character <= 0x0008) ||
|
||||
(character >= 0x000D && character <= 0x001F) ||
|
||||
(character >= 0x007F && character <= 0x009F) ||
|
||||
(character >= 0xFDD0 && character <= 0xFDEF) ||
|
||||
character == 0x000B ||
|
||||
(character >= 0x000E && character <= 0x001F) ||
|
||||
(character >= 0x007F && character <= 0x009F));
|
||||
}
|
||||
|
||||
NS_INLINE BOOL isNoncharacter(unsigned long long character)
|
||||
{
|
||||
return ((character >= 0xFDD0 && character <= 0xFDEF) ||
|
||||
character == 0xFFFE ||
|
||||
character == 0xFFFF ||
|
||||
character == 0x1FFFE ||
|
||||
@@ -137,6 +146,18 @@ NS_INLINE BOOL isHexDigit(UTF32Char character)
|
||||
(character >= LATIN_SMALL_LETTER_A && character <= LATIN_SMALL_LETTER_F));
|
||||
}
|
||||
|
||||
NS_INLINE BOOL isUpperHexDigit(UTF32Char character)
|
||||
{
|
||||
return ((character >= LATIN_CAPITAL_LETTER_A && character <= LATIN_CAPITAL_LETTER_F) ||
|
||||
(character >= DIGIT_ZERO && character <= DIGIT_NINE));
|
||||
}
|
||||
|
||||
NS_INLINE BOOL isLowerHexDigit(UTF32Char character)
|
||||
{
|
||||
return ((character >= LATIN_SMALL_LETTER_A && character <= LATIN_SMALL_LETTER_F) ||
|
||||
(character >= DIGIT_ZERO && character <= DIGIT_NINE));
|
||||
}
|
||||
|
||||
NS_INLINE BOOL isAlphanumeric(UTF32Char character)
|
||||
{
|
||||
return ((character >= DIGIT_ZERO && character <= DIGIT_NINE) ||
|
||||
@@ -151,17 +172,14 @@ NS_INLINE BOOL isStringAlphanumeric(NSString *string)
|
||||
return ([string rangeOfCharacterFromSet:set].location == NSNotFound);
|
||||
}
|
||||
|
||||
NS_INLINE BOOL isInvalidNumericRange(unsigned long long numeric)
|
||||
NS_INLINE BOOL isSurrogate(unsigned long long character)
|
||||
{
|
||||
return ((numeric >= 0xD800 && numeric <= 0xDFFF) ||
|
||||
numeric > 0x10FFFF);
|
||||
return (character >= 0xD800 && character <= 0xDFFF);
|
||||
}
|
||||
|
||||
NS_INLINE unichar NumericReplacementCharacter(UTF32Char character)
|
||||
{
|
||||
if (character == NULL_CHAR) {
|
||||
return REPLACEMENT_CHAR;
|
||||
} else if (character >= 0x0080 && character <= 0x009F) {
|
||||
if (character >= 0x0080 && character <= 0x009F) {
|
||||
return NumericReplacementTable[character - 0x0080];
|
||||
} else {
|
||||
return NULL_CHAR;
|
||||
|
||||
@@ -12,9 +12,7 @@
|
||||
|
||||
#define TOKENIZER_STATES \
|
||||
STATE_ENTRY( HTMLTokenizerStateData, = 0) \
|
||||
STATE_ENTRY( HTMLTokenizerStateCharacterReferenceInData, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateRCDATA, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateCharacterReferenceInRCDATA, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateRAWTEXT, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateScriptData, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStatePLAINTEXT, ) \
|
||||
@@ -59,6 +57,10 @@
|
||||
STATE_ENTRY( HTMLTokenizerStateCommentStart, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateCommentStartDash, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateComment, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateCommentLessThanSign, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateCommentLessThanSignBang, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateCommentLessThanSignBangDash, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateCommentLessThanSignBangDashDash, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateCommentEndDash, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateCommentEnd, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateCommentEndBang, ) \
|
||||
@@ -78,7 +80,18 @@
|
||||
STATE_ENTRY( HTMLTokenizerStateDOCTYPESystemIdentifierSingleQuoted, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateAfterDOCTYPESystemIdentifier, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateBogusDOCTYPE, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateCDATASection, )
|
||||
STATE_ENTRY( HTMLTokenizerStateCDATASection, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateCDATASectionBracket, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateCDATASectionEnd, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateCharacterReference, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateNamedCharacterReference, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateAmbiguousAmpersand, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateNumericCharacterReference, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateHexadecimalCharacterReferenceStart, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateDecimalCharacterReferenceStart, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateHexadecimalCharacterReference, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateDecimalCharacterReference, ) \
|
||||
STATE_ENTRY( HTMLTokenizerStateNumericCharacterReferenceEnd, )
|
||||
|
||||
typedef NS_ENUM(NSUInteger, HTMLTokenizerState)
|
||||
{
|
||||
|
||||
@@ -49,7 +49,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
Initializes a new tree walker with no filter and HTMLNodeFilterShowAll show options.
|
||||
|
||||
@param node The root node.
|
||||
@param filter The node filter to use.
|
||||
@return A new instance of a tree walker.
|
||||
*/
|
||||
- (instancetype)initWithNode:(HTMLNode *)node;
|
||||
|
||||
@@ -19,17 +19,18 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
A character set for HTML whitespace characters: CHARACTER TABULATION U+0009, LINE FEED U+000A, FORM FEED U+000C,
|
||||
CARRIAGE RETURN U+000D, and SPACE U+0020.
|
||||
*/
|
||||
+ (instancetype)HTMLWhitespaceCharacterSet;
|
||||
|
||||
+ (instancetype)htmlkit_HTMLWhitespaceCharacterSet;
|
||||
|
||||
/**
|
||||
A character set for HTML HEX-Number characters: The digits 0-9, latin small letters a-f, and latin capital letters A-F.
|
||||
*/
|
||||
+ (instancetype)HTMLHexNumberCharacterSet;
|
||||
+ (instancetype)htmlkit_HTMLHexNumberCharacterSet;
|
||||
|
||||
/**
|
||||
A character set for CSS Nth-Expression: The digits 0-9, space, latin small n, latin capital N, plus sing and minus sign.
|
||||
*/
|
||||
+ (instancetype)CSSNthExpressionCharacterSet;
|
||||
+ (instancetype)htmlkit_CSSNthExpressionCharacterSet;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -15,38 +15,17 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@interface NSString (HTMLKit)
|
||||
|
||||
/**
|
||||
Checks whether this string is equal to another ignoring the case.
|
||||
|
||||
@return `YES` if the two string are equal ignroing the case, `NO` otherwise.
|
||||
*/
|
||||
- (BOOL)isEqualToStringIgnoringCase:(NSString *)aString;
|
||||
|
||||
/**
|
||||
Checks whether this string is equal to any of the given strings.
|
||||
|
||||
@return `YES` if there is an equal string, `NO` otherwise.
|
||||
*/
|
||||
- (BOOL)isEqualToAny:(NSString *)first, ... NS_REQUIRES_NIL_TERMINATION;
|
||||
|
||||
/**
|
||||
Checks whether this string has a prefix ignoring the case.
|
||||
|
||||
@return `YES` if this string has a given prefix ignroing the case, `NO` otherwise.
|
||||
*/
|
||||
- (BOOL)hasPrefixIgnoringCase:(NSString *)aString;
|
||||
|
||||
/**
|
||||
Checks whether this string is a HTML whitespace string.
|
||||
|
||||
@return `YES` if this string is a HTML whitespace string, `NO` otherwise.
|
||||
*/
|
||||
- (BOOL)isHTMLWhitespaceString;
|
||||
- (BOOL)htmlkit_isHTMLWhitespaceString;
|
||||
|
||||
/**
|
||||
@return The length of the leading HTML whitespace characters in this string.
|
||||
*/
|
||||
- (NSUInteger)leadingHTMLWhitespaceLength;
|
||||
- (NSUInteger)htmlkit_leadingHTMLWhitespaceLength;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// NSString+Private.h
|
||||
// HTMLKit
|
||||
//
|
||||
// Created by Iska on 26.03.19.
|
||||
// Copyright © 2019 BrainCookie. All rights reserved.
|
||||
//
|
||||
|
||||
///------------------------------------------------------
|
||||
/// HTMLKit private header
|
||||
///------------------------------------------------------
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
NSStirng category for common helper methods.
|
||||
*/
|
||||
@interface NSString (Private)
|
||||
|
||||
/**
|
||||
Checks whether this string is equal to another ignoring the case.
|
||||
|
||||
@return `YES` if the two string are equal ignroing the case, `NO` otherwise.
|
||||
*/
|
||||
- (BOOL)isEqualToStringIgnoringCase:(NSString *)aString;
|
||||
|
||||
/**
|
||||
Checks whether this string is equal to any of the given strings.
|
||||
|
||||
@return `YES` if there is an equal string, `NO` otherwise.
|
||||
*/
|
||||
- (BOOL)isEqualToAny:(NSString *)first, ... NS_REQUIRES_NIL_TERMINATION;
|
||||
|
||||
/**
|
||||
Checks whether this string has a prefix ignoring the case.
|
||||
|
||||
@return `YES` if this string has a given prefix ignroing the case, `NO` otherwise.
|
||||
*/
|
||||
- (BOOL)hasPrefixIgnoringCase:(NSString *)aString;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,39 @@
|
||||
module HTMLKit {
|
||||
umbrella header "HTMLKit.h"
|
||||
|
||||
module * { export * }
|
||||
export *
|
||||
|
||||
explicit module Private {
|
||||
textual 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"
|
||||
textual 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"
|
||||
header "NSString+Private.h"
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
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 *
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user