120 Commits

Author SHA1 Message Date
iska daf71a01f3 Merge branch 'release/1.1.0' 2017-01-15 00:01:28 +01:00
iska 049d7b7148 Bump HTMLKit version to 1.1.0 2017-01-15 00:00:14 +01:00
iska c727ce6643 Update README.md 2017-01-15 00:00:14 +01:00
iska 1a5238863a Add Changelog entry for HTMLKit 1.1.0 2017-01-14 23:54:36 +01:00
iska 46e36e9c24 Update jazzy.yaml for 1.1.0 2017-01-14 23:54:36 +01:00
iska 1919992b0e Update podspec for 1.1.0 2017-01-14 23:54:36 +01:00
iska 28ecfb0278 Update headers in module map 2017-01-14 23:54:36 +01:00
iska 65666a13c0 Remove redundant header file from private group 2017-01-14 23:54:36 +01:00
iska e5237a68c4 Update project headers and targets 2017-01-14 23:54:36 +01:00
iska ea63b38379 Update html5lib-tests to latest commit as of 2017-01-13
Commit:
50568b5664d6b034b752310603b2b664d045ad0f

This commit references a PR adding <plaintext> tests
2017-01-13 19:35:09 +01:00
iska 542bc26ff9 Use AssertEqualObjects instead of AssertEqual in range tests
Better error message on failures.
2017-01-13 18:28:29 +01:00
iska 718e43ff9c Add HTML Range tests for Text split callbacks 2017-01-13 18:23:56 +01:00
iska 472d59546a Add HTML Range stringifier and description
https://dom.spec.whatwg.org/#dom-range-stringifier
2017-01-11 22:48:51 +01:00
iska bdaddc0938 Add travis_retry in travis.yml to fix build issues
So this happened:
https://github.com/travis-ci/travis-ci/issues/6422
2017-01-11 22:38:53 +01:00
iska a38d1e6484 Add HTML CharacterData method for substring data 2017-01-11 19:17:54 +01:00
iska f53ce3464d Add HTML Range tests for node surround and insertion 2017-01-11 18:29:30 +01:00
iska bc94f91c6e Add HTML Character Data & Text tests 2017-01-10 17:55:39 +01:00
iska 1e5517f403 Fix return type of split-text method in HTML Text 2017-01-10 17:55:26 +01:00
iska 925848404b Add implementation for HTML Range surround and insert node 2017-01-08 22:38:41 +01:00
iska fe46b106b2 Add implementation for HTML Text splitting 2017-01-08 22:36:47 +01:00
iska c7edbd3e14 Add HTML Document and Range methods for Text splitting callbacks 2017-01-08 22:35:53 +01:00
iska f1b1f523b8 Add implementation for HTML Range’s extract contents method 2017-01-08 09:44:03 +01:00
iska 51c94d49bd Add implementation for HTML Range’s clone contents method 2017-01-08 09:33:15 +01:00
iska 395ce5e981 Add HTML Range initializer that accepts start and end boundaries 2017-01-08 09:32:15 +01:00
iska e48ccf42ca Add HTML Node deep clone implementation 2017-01-08 09:31:37 +01:00
iska 0ac49f2a0f Add implementation for HTML Range’s delete contents method 2017-01-08 08:09:26 +01:00
iska 07c72f65d9 Add HTML Range methods for contained nodes 2017-01-08 08:07:47 +01:00
iska 8602ad5a7b Add extended documentation for Node’s containsNode method
This will be deprecated and replaced with isInclusiveAncestor in a later
release.
2017-01-05 02:44:05 +01:00
iska 12fc972602 Add HTML Range handling for mutation callback on node removal
See:
https://dom.spec.whatwg.org/#concept-node-remove
2017-01-05 02:43:07 +01:00
iska b66f5264ba Rename some test classes
- Drop the “Kit” from class names
2017-01-04 03:49:05 +01:00
iska 1042571c57 Add Range tests for character data callbacks with different boundaries 2017-01-02 01:04:00 +01:00
iska c294802199 Add HTML Range tests for character data mutation callbacks 2017-01-02 00:39:55 +01:00
iska ea17eeef9d Fix copy methods for CharacterData and DocumentType nodes 2016-12-31 16:26:06 +01:00
iska 9795172c9c Add implementation and tests for Range comparison and intersection methods 2016-12-31 03:11:49 +01:00
iska 0319536a6c Add HTML Range tests for boundary comparison 2016-12-31 00:34:54 +01:00
iska 3f74fcaa7b Use the “iPhone 7 Plus 10.1” destination in travis.yml
It seems that the Xcode8.2 image in travis contains multiple (2)
“iPhone 7 Plus 10.2” destinations
See build log:
https://travis-ci.org/iabudiab/HTMLKit/jobs/187795560
2016-12-30 23:35:00 +01:00
iska 78b3069a25 Fix watchOS and iOS destinations in travis.yml 2016-12-30 22:54:05 +01:00
iska dfd01b1908 Update Xcode image and SDK versions in travis.yml 2016-12-30 22:11:14 +01:00
iska bdacc629eb Add HTML Range tests for node selecting 2016-12-30 22:08:23 +01:00
iska 734c185137 Add HTML Range tests for boundaries logic 2016-12-30 21:55:36 +01:00
iska de6c215550 Fix check in range’s compare-boundaries method 2016-12-30 21:54:58 +01:00
iska b7b29d2af5 Fix HTML Range boundary validations 2016-12-30 03:21:17 +01:00
iska 15acfa06f6 Fix HTML Node’s method to compare document positions 2016-12-30 03:07:59 +01:00
iska 7506ed8cae Refactor method to get a node’s ancestor chain into the utils class 2016-12-30 03:07:33 +01:00
iska 3898ae243c Add HTML Range tests class and setup 2016-12-29 22:54:59 +01:00
iska 6da3f7bd49 Mark HTML Range’s default initializer as unavailable 2016-12-29 22:54:39 +01:00
iska fb04d49693 Add DOM method for computing a common ancestor of two nodes 2016-12-29 22:54:14 +01:00
iska 4d132af99f Update and reorganize private headers and modulemap 2016-12-29 22:53:34 +01:00
iska e4a0669cf2 Fix travis badge on develop branch 2016-11-28 01:32:25 +01:00
iska e60fe36e25 Fix append-string method in HTML Text node 2016-11-27 20:22:21 +01:00
iska 1f6f9d843e Add implementation for range’s update methods on character data mutations 2016-11-27 20:21:16 +01:00
iska 5c2f6527a7 Add implementation for HTML Range’s contains-node method 2016-11-27 20:19:54 +01:00
iska e4b57f3333 Update deprecation message in HTML Text node 2016-11-27 20:19:19 +01:00
iska 6be1bd702f Add implementation for HTML Character Data methods
The data property is longer exposed as a mutable string. Instead all
manipulations must be performed via the provided API in order to ensure
correct updates for the Document’s attached ranges.

See:
https://dom.spec.whatwg.org/#characterdata
2016-11-27 20:18:12 +01:00
iska ef672e1dbb Add range attaching/detaching to the range’s lifecycle 2016-11-27 15:00:13 +01:00
iska 0d95ba33d1 Add HTML Document methods to attach and detach a range to the document 2016-11-27 03:13:21 +01:00
iska 9df89043d3 Refactor all private extensions into own separate categories 2016-11-27 03:12:58 +01:00
iska 6fdc7ff85b Refactor private HTML Document extensions into one separate category 2016-11-27 02:43:10 +01:00
iska c3c7c34310 Deprecate appendString: method in HTML Text node
The data property is mutable and should be used instead
2016-11-27 02:10:10 +01:00
iska fb2952062d Add Character Data implementation as a base for text and comment nodes
https://dom.spec.whatwg.org/#characterdata
2016-11-27 02:09:06 +01:00
iska 49799f8bcd Add methods to select a node and node contents in a HTML Range 2016-11-22 21:42:45 +01:00
iska 9bd9dfbf6b Add methods to collapse a HTML Range 2016-11-22 21:42:15 +01:00
iska 8f0a6d133e Finalize HTML Range methods for setting boundaries 2016-11-22 21:41:41 +01:00
iska cfb1707fc4 Implement collapsed, common ancestor & root properties for HTML Range 2016-11-22 01:25:04 +01:00
iska 733780091d Add method declaration for comparing HTML Ranges 2016-11-22 01:24:02 +01:00
iska 8391a01147 Change HTML Range’s initializer to accept one HTML Document argument
According to spec:
“The Range() constructor, when invoked, must return a new range with
(current global object’s associated Document, 0) as its start and end.”

We don’t have a global object (window) with an associated document, thus
we have to pass one instead.
2016-11-22 01:23:17 +01:00
iska 6ed0238a89 Add implementation for setting HTML Range’s boundaries 2016-11-22 01:20:20 +01:00
iska 80299ad9e0 Change HTMLDocumentPosition to NS_OPTIONS instead of NS_ENUM
Better interop with Swift
2016-11-22 01:17:32 +01:00
iska ffc9e629f2 Add index property for HTML Node 2016-11-22 01:15:23 +01:00
iska 394a1a53c4 Add root-node property for HTML Node 2016-11-22 01:15:09 +01:00
iska 14289f96e5 Add method declarations to collapse and select nodes in the HTML Range
See:
https://dom.spec.whatwg.org/#range
2016-11-21 01:24:26 +01:00
iska c57117670f Add method declarations to initialize and define a HTML Range
See:
https://dom.spec.whatwg.org/#range
2016-11-21 01:24:26 +01:00
iska a08fb84be9 Add properties to HTML Range interface
See:
https://dom.spec.whatwg.org/#range
2016-11-21 01:19:31 +01:00
iska 9fd5d34244 Add HTML Range class stub 2016-11-21 01:17:48 +01:00
iska 2fb871a7ff Add implementation for the length property for HTML Nodes
See:
https://dom.spec.whatwg.org/#concept-node-length
https://heycam.github.io/webidl/#dfn-code-unit
2016-11-20 18:36:55 +01:00
iska 46072a09cc Update html5lib-tests to latest commit as of 2016.11.07
Commit: 27724be8d27a1232a8809a55f9c37b69706cb03e
2016-11-07 21:51:22 +01:00
iska de2d35fbe6 Merge branch 'release/1.0.0' 2016-09-28 02:09:00 +02:00
iska 6ae00f40e1 Merge branch 'release/1.0.0' into develop 2016-09-28 02:09:00 +02:00
iska 76a7930e7d Bump HTMLKit version to 1.0.0 2016-09-28 02:02:51 +02:00
iska 8dc5d0ea50 Update jazzy.yaml for 1.0.0 2016-09-28 02:02:31 +02:00
iska 0e9956efc0 Update podspec for 1.0.0 2016-09-28 02:02:18 +02:00
iska d5888e68a7 Add Changelog entry for HTMLKit 1.0.0 2016-09-28 02:01:10 +02:00
iska 02177e7fac Fix macOS scheme and sdk in travis.yml 2016-09-28 01:55:10 +02:00
iska af76666f3c Add watchOS2.0 and tvOS9.0 destinations to travis.yml 2016-09-28 01:44:09 +02:00
iska 73dc32aae9 Set deployment targets to macOS 10.9, iOS 9.0, tvOS 9.0 and watchOS 2.0 2016-09-28 01:39:38 +02:00
iska 64cba55677 Replace OSX with macOS throughout project 2016-09-28 01:38:59 +02:00
iska 214722c0bf Share example project's scheme 2016-09-28 01:36:05 +02:00
iska b8592630d0 Fix simulator name in travis.yml 2016-09-28 01:10:04 +02:00
iska a641c64f74 Fix typo in travis.yml 2016-09-27 23:34:24 +02:00
iska e20531925a Update travis.yaml for Xcode8 and iOS10 2016-09-27 23:00:45 +02:00
iska 024ab12242 Add docs directory to gitignores 2016-09-27 22:50:02 +02:00
iska c2a11e6f97 Add .jazzy.yaml configuration file 2016-09-27 22:49:49 +02:00
iska 800336d317 Add nullability annotation for CSSSelectorParser's parse method 2016-09-27 22:32:30 +02:00
iska a751cec215 Add example project 2016-09-27 22:30:45 +02:00
iska 87a476e523 Change html5lib-tests submodule name 2016-09-20 19:30:10 +02:00
iska 7e42fd1e08 Add missing lightweight generics in Parser, Element & Node classes
To play nicely with Swift 3
2016-09-20 19:28:08 +02:00
iska 7d1b94219d Add missing deployment target settings in project file 2016-09-20 19:27:21 +02:00
iska 29bd89b808 Update playground to Swift 3 2016-09-20 19:27:05 +02:00
iska 45a8e25028 Update project and schemes for Xcode 8 2016-09-13 22:47:33 +02:00
iska 33bb1e39a4 Merge branch 'release/0.9.4' into develop 2016-09-03 17:23:09 +02:00
iska 44c987ec13 Merge branch 'release/0.9.4'
# Conflicts:
#	Tests/html5lib-tests
2016-09-03 17:21:13 +02:00
iska 3bd5b2d4a2 Fix source code and header paths in podspec file
Source code resides in "Sources" and "Sources/include" now after spm
refactor
2016-09-03 17:08:17 +02:00
iska 8c312e1508 Bump HTMLKit version to 0.9.4 2016-09-03 16:53:23 +02:00
iska 20d8449a94 Update podspec for 0.9.4 2016-09-03 16:51:58 +02:00
iska 16b85af5af Update README.md 2016-09-03 16:51:46 +02:00
iska 3c56a1d694 Add Changelog entry for HTMLKit 0.9.4 2016-09-03 16:51:33 +02:00
iska ca20e6d03f Add HTMLKit modulemap file 2016-09-03 16:18:28 +02:00
iska f58c2e3f94 Remove unused strings plist from tests code 2016-08-28 01:17:53 +02:00
iska 9698919d35 Fix import in private HTMLNode header 2016-08-27 02:07:54 +02:00
iska 9a1a12c788 Remove prefix header and necessary foundation imports 2016-08-27 02:02:43 +02:00
iska e56a980e80 Move header files into include directory 2016-08-27 01:59:53 +02:00
iska a440fd56b2 Add Package.swift 2016-08-24 20:27:50 +02:00
iska 1a3c1dd3d2 Update gitignore 2016-08-24 20:23:20 +02:00
iska 170c298571 Remove unused strings plist 2016-08-24 20:04:13 +02:00
iska f51c76d257 Update project paths for info.plist and prefix header 2016-08-24 20:02:09 +02:00
iska 1df63b8e00 Move test source code to Tests directory 2016-08-24 19:55:59 +02:00
iska 37444d5899 Move source code to Sources directory 2016-08-24 19:54:41 +02:00
iska 85e4941057 Move css-tests to Tests directory 2016-08-24 19:51:41 +02:00
iska c7a339fbed Move html5lib-tests submodule to Tests directory 2016-08-24 19:50:23 +02:00
iska b1a9b7ecee Merge branch 'release/0.9.3' into develop 2016-07-16 14:50:07 +02:00
285 changed files with 5197 additions and 547 deletions
+4 -1
View File
@@ -26,6 +26,7 @@ xcuserdata
## Obj-C/Swift specific
*.hmap
*.ipa
.build/
# CocoaPods
#
@@ -39,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/
+2 -2
View File
@@ -1,3 +1,3 @@
[submodule "HTMLKitTests/html5lib-tests"]
path = HTMLKitTests/html5lib-tests
[submodule "html5lib-tests"]
path = Tests/html5lib-tests
url = https://github.com/html5lib/html5lib-tests.git
+123
View File
@@ -0,0 +1,123 @@
module: HTMLKit
module_version: 1.1.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)
+18 -15
View File
@@ -1,5 +1,5 @@
language: objective-c
osx_image: xcode7.3
osx_image: xcode8.2
branches:
except:
@@ -14,21 +14,24 @@ 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=iphonesimulator10.2
- MACOS_SDK=macosx10.12
- WATCHOS_SDK=watchsimulator3.1
- TVOS_SDK=appletvsimulator10.1
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" SIMULATOR="" SCHEME="$MACOS_FRAMEWORK_SCHEME" SDK="$MACOS_SDK"
- DESTINATION="OS=9.0,name=iPhone 6" SIMULATOR="iPhone 6 (9.0)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
- DESTINATION="OS=9.1,name=iPhone 6 Plus" SIMULATOR="iPhone 6 Plus (9.1)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
- DESTINATION="OS=9.2,name=iPhone 6S" SIMULATOR="iPhone 6S (9.2)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
- DESTINATION="OS=9.3,name=iPhone 6S Plus" SIMULATOR="iPhone 6S Plus (9.3)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
- DESTINATION="OS=10.1,name=iPhone 7 Plus" SIMULATOR="iPhone 7 Plus (10.1)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
- DESTINATION="OS=2.2,name=Apple Watch - 42mm" SIMULATOR="Apple Watch - 42mm (2.2)" SCHEME="$WATCHOS_FRAMEWORK_SCHEME" SDK="$WATCHOS_SDK"
- DESTINATION="OS=3.1,name=Apple Watch Series 2 - 42mm" SIMULATOR="Apple Watch Series 2 - 42mm (3.1)" SCHEME="$WATCHOS_FRAMEWORK_SCHEME" SDK="$WATCHOS_SDK"
- DESTINATION="OS=9.0,name=Apple TV 1080p" SIMULATOR="Apple TV 1080p (9.2)" SCHEME="$TVOS_FRAMEWORK_SCHEME" SDK="$TVOS_SDK"
- DESTINATION="OS=10.0,name=Apple TV 1080p" SIMULATOR="Apple TV 1080p (10.0)" SCHEME="$TVOS_FRAMEWORK_SCHEME" SDK="$TVOS_SDK"
script:
- set -o pipefail
@@ -36,7 +39,7 @@ script:
- xcodebuild -showsdks
- SIMULATOR_ID=$(xcrun instruments -s devices | grep -io "$SIMULATOR \[.*\]" | grep -o "\[.*\]" | sed "s/^\[\(.*\)\]$/\1/")
- open -b com.apple.iphonesimulator --args -CurrentDeviceUDID $SIMULATOR_ID
- xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO clean build | xcpretty -c
- travis_retry xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO clean build | xcpretty -c
- if [ "$SDK" != "$WATCHOS_SDK" ]; then
xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO test | xcpretty -c;
travis_retry xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO test | xcpretty -c;
fi
+49 -6
View File
@@ -1,10 +1,57 @@
# Change Log
## [1.1.0](https://github.com/iabudiab/HTMLKit/releases/tag/1.1.0)
Released on 2017.01.14
### Added
- `DOM Ranges` implementation ([spec](https://dom.spec.whatwg.org/#ranges))
- `HTMLChatacterData` as base class for `HTMLText` & `HTMLComment`
- `HTMLText` and `HTMLComment` no longer extend `HTMLNode` directly
- `splitText` implementation for `HTMLText` nodes
- `index` property for `HTMLNode`
- `cloneNodeDeep` method for `HTMLNode`
### Deprecated
- `appendString` method in `HTMLText` in favor of `appendData` from the supperclass `HTMLCharacterData`
## [1.0.0](https://github.com/iabudiab/HTMLKit/releases/tag/1.0.0)
Released on 2016.09.28
### 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
### Added
- `Swift Package Manager` support
## [0.9.3](https://github.com/iabudiab/HTMLKit/releases/tag/0.9.3)
Released on 2016.07.16
This release passes all html5lib-tests as of 2016.07.16
This release passes all tokenizer and tree construction html5lib-tests as of 2016.07.16
### Added
@@ -15,7 +62,7 @@ This release passes all html5lib-tests as of 2016.07.16
Released on 2016.05.18
This release passes all html5lib-tests as of 2016.05.18
This release passes all tokenizer and tree construction html5lib-tests as of 2016.05.18
### Added
@@ -32,7 +79,6 @@ This release passes all html5lib-tests as of 2016.05.18
- `<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
@@ -41,7 +87,6 @@ This release passes all html5lib-tests as of 2016.05.18
- 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
@@ -51,7 +96,6 @@ Released on 2016.01.29
- Travis-CI integration.
- CocoaPods spec.
### Changed
- Warnings are treated as errors.
@@ -83,7 +127,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,257 @@
// !$*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 = 0800;
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_BOOL_CONVERSION = 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_OBJC_ROOT_CLASS = YES_ERROR;
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_BOOL_CONVERSION = 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_OBJC_ROOT_CLASS = YES_ERROR;
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;
};
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;
};
/* End XCConfigurationList section */
};
rootObject = 629A63C11D9AFE0E0089679F /* Project object */;
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:HTMLKitExample.xcodeproj">
</FileRef>
</Workspace>
@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0800"
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"
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"
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&amp;CharacterRangeLoc=318&amp;EndingColumnNumber=26&amp;EndingLineNumber=13&amp;StartingColumnNumber=1&amp;StartingLineNumber=13&amp;Timestamp=472578634.909266"
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=581&amp;EndingColumnNumber=26&amp;EndingLineNumber=11&amp;StartingColumnNumber=1&amp;StartingLineNumber=11&amp;Timestamp=496084829.845787"
lockedSize = "{800, 186}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=27&amp;CharacterRangeLoc=393&amp;EndingColumnNumber=28&amp;EndingLineNumber=20&amp;StartingColumnNumber=1&amp;StartingLineNumber=20&amp;Timestamp=496084834.772773"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=18&amp;CharacterRangeLoc=544&amp;EndingColumnNumber=19&amp;EndingLineNumber=27&amp;StartingColumnNumber=1&amp;StartingLineNumber=27&amp;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&amp;CharacterRangeLoc=453&amp;EndingColumnNumber=26&amp;EndingLineNumber=23&amp;StartingColumnNumber=2&amp;StartingLineNumber=23&amp;Timestamp=472578641.006933"
documentLocation = "#CharacterRangeLen=24&amp;CharacterRangeLoc=455&amp;EndingColumnNumber=26&amp;EndingLineNumber=23&amp;StartingColumnNumber=2&amp;StartingLineNumber=23&amp;Timestamp=495492881.371878"
lockedSize = "{775, 83}"
selectedRepresentationIndex = "1"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=24&amp;CharacterRangeLoc=668&amp;EndingColumnNumber=26&amp;EndingLineNumber=34&amp;StartingColumnNumber=2&amp;StartingLineNumber=34&amp;Timestamp=472578641.007156"
documentLocation = "#CharacterRangeLen=24&amp;CharacterRangeLoc=672&amp;EndingColumnNumber=26&amp;EndingLineNumber=34&amp;StartingColumnNumber=2&amp;StartingLineNumber=34&amp;Timestamp=495493308.359844"
lockedSize = "{775, 76}"
selectedRepresentationIndex = "1"
shouldTrackSuperviewWidth = "YES">
@@ -39,7 +39,7 @@
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=24&amp;CharacterRangeLoc=448&amp;EndingColumnNumber=26&amp;EndingLineNumber=21&amp;StartingColumnNumber=2&amp;StartingLineNumber=21&amp;Timestamp=472579861.71569"
documentLocation = "#CharacterRangeLen=24&amp;CharacterRangeLoc=450&amp;EndingColumnNumber=26&amp;EndingLineNumber=21&amp;StartingColumnNumber=2&amp;StartingLineNumber=21&amp;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&amp;CharacterRangeLoc=575&amp;EndingColumnNumber=26&amp;EndingLineNumber=24&amp;StartingColumnNumber=1&amp;StartingLineNumber=24&amp;Timestamp=472578662.196737"
documentLocation = "#CharacterRangeLen=19&amp;CharacterRangeLoc=571&amp;EndingColumnNumber=26&amp;EndingLineNumber=24&amp;StartingColumnNumber=1&amp;StartingLineNumber=24&amp;Timestamp=495493395.434491"
lockedSize = "{763, 104}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "YES">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=15&amp;CharacterRangeLoc=843&amp;EndingColumnNumber=22&amp;EndingLineNumber=33&amp;StartingColumnNumber=1&amp;StartingLineNumber=33&amp;Timestamp=472578662.197012"
documentLocation = "#CharacterRangeLen=15&amp;CharacterRangeLoc=834&amp;EndingColumnNumber=22&amp;EndingLineNumber=33&amp;StartingColumnNumber=1&amp;StartingLineNumber=33&amp;Timestamp=495493408.111041"
lockedSize = "{763, 75}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=1&amp;CharacterRangeLoc=1757&amp;EndingColumnNumber=44&amp;EndingLineNumber=75&amp;StartingColumnNumber=2&amp;StartingLineNumber=75&amp;Timestamp=472578674.159974"
documentLocation = "#CharacterRangeLen=1&amp;CharacterRangeLoc=1774&amp;EndingColumnNumber=44&amp;EndingLineNumber=77&amp;StartingColumnNumber=2&amp;StartingLineNumber=77&amp;Timestamp=496085091.150842"
lockedSize = "{763, 176}"
selectedRepresentationIndex = "1"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=14&amp;CharacterRangeLoc=844&amp;EndingColumnNumber=15&amp;EndingLineNumber=33&amp;StartingColumnNumber=1&amp;StartingLineNumber=33&amp;Timestamp=472578662.197451"
documentLocation = "#CharacterRangeLen=14&amp;CharacterRangeLoc=835&amp;EndingColumnNumber=15&amp;EndingLineNumber=33&amp;StartingColumnNumber=1&amp;StartingLineNumber=33&amp;Timestamp=495493408.111552"
lockedSize = "{762, 70}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=18&amp;CharacterRangeLoc=576&amp;EndingColumnNumber=19&amp;EndingLineNumber=24&amp;StartingColumnNumber=1&amp;StartingLineNumber=24&amp;Timestamp=472578662.197662"
documentLocation = "#CharacterRangeLen=18&amp;CharacterRangeLoc=572&amp;EndingColumnNumber=19&amp;EndingLineNumber=24&amp;StartingColumnNumber=1&amp;StartingLineNumber=24&amp;Timestamp=495493395.435449"
lockedSize = "{752, 99}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=1&amp;CharacterRangeLoc=1757&amp;EndingColumnNumber=16&amp;EndingLineNumber=75&amp;StartingColumnNumber=2&amp;StartingLineNumber=75&amp;Timestamp=472578674.160606"
documentLocation = "#CharacterRangeLen=1&amp;CharacterRangeLoc=1774&amp;EndingColumnNumber=16&amp;EndingLineNumber=77&amp;StartingColumnNumber=2&amp;StartingLineNumber=77&amp;Timestamp=496085091.151491"
lockedSize = "{763, 102}"
selectedRepresentationIndex = "1"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=18&amp;CharacterRangeLoc=1740&amp;EndingColumnNumber=39&amp;EndingLineNumber=75&amp;StartingColumnNumber=2&amp;StartingLineNumber=75&amp;Timestamp=472578674.160818"
documentLocation = "#CharacterRangeLen=33&amp;CharacterRangeLoc=1742&amp;EndingColumnNumber=39&amp;EndingLineNumber=77&amp;StartingColumnNumber=2&amp;StartingLineNumber=77&amp;Timestamp=496085091.151711"
lockedSize = "{759, 149}"
selectedRepresentationIndex = "1"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=17&amp;CharacterRangeLoc=1741&amp;EndingColumnNumber=19&amp;EndingLineNumber=75&amp;StartingColumnNumber=2&amp;StartingLineNumber=75&amp;Timestamp=472578674.161034"
documentLocation = "#CharacterRangeLen=32&amp;CharacterRangeLoc=1743&amp;EndingColumnNumber=19&amp;EndingLineNumber=77&amp;StartingColumnNumber=2&amp;StartingLineNumber=77&amp;Timestamp=496085091.151931"
lockedSize = "{763, 120}"
selectedRepresentationIndex = "1"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=14&amp;CharacterRangeLoc=1230&amp;EndingColumnNumber=15&amp;EndingLineNumber=51&amp;StartingColumnNumber=1&amp;StartingLineNumber=51&amp;Timestamp=472578674.161242"
documentLocation = "#CharacterRangeLen=14&amp;CharacterRangeLoc=1204&amp;EndingColumnNumber=15&amp;EndingLineNumber=51&amp;StartingColumnNumber=1&amp;StartingLineNumber=51&amp;Timestamp=495493441.437517"
lockedSize = "{763, 94}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=14&amp;CharacterRangeLoc=1145&amp;EndingColumnNumber=15&amp;EndingLineNumber=45&amp;StartingColumnNumber=1&amp;StartingLineNumber=45&amp;Timestamp=472578674.161449"
documentLocation = "#CharacterRangeLen=14&amp;CharacterRangeLoc=1122&amp;EndingColumnNumber=15&amp;EndingLineNumber=45&amp;StartingColumnNumber=1&amp;StartingLineNumber=45&amp;Timestamp=495493427.982381"
lockedSize = "{760, 97}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=8&amp;CharacterRangeLoc=1332&amp;EndingColumnNumber=13&amp;EndingLineNumber=57&amp;StartingColumnNumber=5&amp;StartingLineNumber=57&amp;Timestamp=472578674.161666"
lockedSize = "{763, 74}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=18&amp;CharacterRangeLoc=397&amp;EndingColumnNumber=19&amp;EndingLineNumber=14&amp;StartingColumnNumber=1&amp;StartingLineNumber=14&amp;Timestamp=472578662.199111"
lockedSize = "{762, 90}"
@@ -75,76 +69,82 @@
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=14&amp;CharacterRangeLoc=701&amp;EndingColumnNumber=15&amp;EndingLineNumber=26&amp;StartingColumnNumber=1&amp;StartingLineNumber=26&amp;Timestamp=472578662.199312"
documentLocation = "#CharacterRangeLen=14&amp;CharacterRangeLoc=697&amp;EndingColumnNumber=15&amp;EndingLineNumber=26&amp;StartingColumnNumber=1&amp;StartingLineNumber=26&amp;Timestamp=495493395.437213"
lockedSize = "{763, 76}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=14&amp;CharacterRangeLoc=1002&amp;EndingColumnNumber=15&amp;EndingLineNumber=38&amp;StartingColumnNumber=1&amp;StartingLineNumber=38&amp;Timestamp=472578674.162269"
documentLocation = "#CharacterRangeLen=14&amp;CharacterRangeLoc=983&amp;EndingColumnNumber=15&amp;EndingLineNumber=38&amp;StartingColumnNumber=1&amp;StartingLineNumber=38&amp;Timestamp=495493427.983236"
lockedSize = "{763, 92}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=14&amp;CharacterRangeLoc=1049&amp;EndingColumnNumber=15&amp;EndingLineNumber=41&amp;StartingColumnNumber=1&amp;StartingLineNumber=41&amp;Timestamp=472578674.162475"
documentLocation = "#CharacterRangeLen=14&amp;CharacterRangeLoc=1030&amp;EndingColumnNumber=15&amp;EndingLineNumber=41&amp;StartingColumnNumber=1&amp;StartingLineNumber=41&amp;Timestamp=495493427.983458"
lockedSize = "{763, 83}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=8&amp;CharacterRangeLoc=1108&amp;EndingColumnNumber=13&amp;EndingLineNumber=44&amp;StartingColumnNumber=5&amp;StartingLineNumber=44&amp;Timestamp=472578674.162693"
documentLocation = "#CharacterRangeLen=8&amp;CharacterRangeLoc=1089&amp;EndingColumnNumber=13&amp;EndingLineNumber=44&amp;StartingColumnNumber=5&amp;StartingLineNumber=44&amp;Timestamp=495493427.983688"
lockedSize = "{763, 73}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=17&amp;CharacterRangeLoc=1598&amp;EndingColumnNumber=19&amp;EndingLineNumber=68&amp;StartingColumnNumber=2&amp;StartingLineNumber=68&amp;Timestamp=472578674.162898"
documentLocation = "#CharacterRangeLen=17&amp;CharacterRangeLoc=1601&amp;EndingColumnNumber=19&amp;EndingLineNumber=70&amp;StartingColumnNumber=2&amp;StartingLineNumber=70&amp;Timestamp=496085091.153628"
lockedSize = "{763, 92}"
selectedRepresentationIndex = "1"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=8&amp;CharacterRangeLoc=1410&amp;EndingColumnNumber=9&amp;EndingLineNumber=61&amp;StartingColumnNumber=1&amp;StartingLineNumber=59&amp;Timestamp=472578674.163102"
lockedSize = "{763, 60}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=14&amp;CharacterRangeLoc=590&amp;EndingColumnNumber=15&amp;EndingLineNumber=21&amp;StartingColumnNumber=1&amp;StartingLineNumber=21&amp;Timestamp=472579886.937978"
documentLocation = "#CharacterRangeLen=14&amp;CharacterRangeLoc=586&amp;EndingColumnNumber=15&amp;EndingLineNumber=21&amp;StartingColumnNumber=1&amp;StartingLineNumber=21&amp;Timestamp=495493395.438545"
lockedSize = "{763, 50}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=14&amp;CharacterRangeLoc=830&amp;EndingColumnNumber=15&amp;EndingLineNumber=30&amp;StartingColumnNumber=1&amp;StartingLineNumber=30&amp;Timestamp=472579886.937978"
documentLocation = "#CharacterRangeLen=9&amp;CharacterRangeLoc=826&amp;EndingColumnNumber=15&amp;EndingLineNumber=30&amp;StartingColumnNumber=1&amp;StartingLineNumber=30&amp;Timestamp=495493408.115325"
lockedSize = "{763, 50}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "YES">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=14&amp;CharacterRangeLoc=877&amp;EndingColumnNumber=15&amp;EndingLineNumber=33&amp;StartingColumnNumber=1&amp;StartingLineNumber=33&amp;Timestamp=472579886.937978"
documentLocation = "#CharacterRangeLen=14&amp;CharacterRangeLoc=868&amp;EndingColumnNumber=15&amp;EndingLineNumber=33&amp;StartingColumnNumber=1&amp;StartingLineNumber=33&amp;Timestamp=495493408.115547"
lockedSize = "{763, 50}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "YES">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=8&amp;CharacterRangeLoc=936&amp;EndingColumnNumber=13&amp;EndingLineNumber=36&amp;StartingColumnNumber=5&amp;StartingLineNumber=36&amp;Timestamp=472579886.937978"
documentLocation = "#CharacterRangeLen=8&amp;CharacterRangeLoc=927&amp;EndingColumnNumber=13&amp;EndingLineNumber=36&amp;StartingColumnNumber=5&amp;StartingLineNumber=36&amp;Timestamp=495493408.115762"
lockedSize = "{763, 50}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=8&amp;CharacterRangeLoc=978&amp;EndingColumnNumber=9&amp;EndingLineNumber=38&amp;StartingColumnNumber=1&amp;StartingLineNumber=38&amp;Timestamp=472579886.937978"
documentLocation = "#CharacterRangeLen=8&amp;CharacterRangeLoc=959&amp;EndingColumnNumber=9&amp;EndingLineNumber=38&amp;StartingColumnNumber=1&amp;StartingLineNumber=38&amp;Timestamp=495493427.985192"
lockedSize = "{763, 50}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "YES">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=17&amp;CharacterRangeLoc=1261&amp;EndingColumnNumber=19&amp;EndingLineNumber=48&amp;StartingColumnNumber=2&amp;StartingLineNumber=48&amp;Timestamp=472579886.937978"
documentLocation = "#CharacterRangeLen=17&amp;CharacterRangeLoc=1235&amp;EndingColumnNumber=19&amp;EndingLineNumber=48&amp;StartingColumnNumber=2&amp;StartingLineNumber=48&amp;Timestamp=495493441.440785"
lockedSize = "{763, 92}"
selectedRepresentationIndex = "1"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=18&amp;CharacterRangeLoc=1451&amp;EndingColumnNumber=19&amp;EndingLineNumber=64&amp;StartingColumnNumber=1&amp;StartingLineNumber=64&amp;Timestamp=496085091.155099"
lockedSize = "{740, 65}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "YES">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=18&amp;CharacterRangeLoc=1347&amp;EndingColumnNumber=19&amp;EndingLineNumber=58&amp;StartingColumnNumber=1&amp;StartingLineNumber=58&amp;Timestamp=496085092.425909"
lockedSize = "{740, 69}"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
</TimelineItems>
</Timeline>
-11
View File
@@ -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 -1
View File
@@ -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'/>
+3 -3
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "HTMLKit"
s.version = "0.9.3"
s.version = "1.1.0"
s.summary = "HTMLKit, an Objective-C framework for your everyday HTML needs."
s.license = "MIT"
s.homepage = "https://github.com/iabudiab/HTMLKit"
@@ -14,9 +14,9 @@ Pod::Spec.new do |s|
s.source = { :git => "https://github.com/iabudiab/HTMLKit.git", :tag => s.version }
s.source_files = "HTMLKit", "HTMLKit/**/*.{h,m}"
s.source_files = "Sources", "Sources/**/*.{h,m}"
s.private_header_files = [
'HTMLKit/**/*{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
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 = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
LastUpgradeVersion = "0800"
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,16 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
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 +59,7 @@
BuildableIdentifier = "primary"
BlueprintIdentifier = "625A14AB19C7829400AD0C32"
BuildableName = "HTMLKit.framework"
BlueprintName = "HTMLKit-OSX"
BlueprintName = "HTMLKit-macOS"
ReferencedContainer = "container:HTMLKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
@@ -80,7 +81,7 @@
BuildableIdentifier = "primary"
BlueprintIdentifier = "625A14AB19C7829400AD0C32"
BuildableName = "HTMLKit.framework"
BlueprintName = "HTMLKit-OSX"
BlueprintName = "HTMLKit-macOS"
ReferencedContainer = "container:HTMLKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
@@ -98,7 +99,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 = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -26,7 +26,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -26,7 +26,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
</Testables>
<AdditionalOptions>
+3
View File
@@ -7,4 +7,7 @@
<FileRef
location = "group:HTMLKit.playground">
</FileRef>
<FileRef
location = "group:Example/HTMLKitExample/HTMLKitExample.xcodeproj">
</FileRef>
</Workspace>
-9
View File
@@ -1,9 +0,0 @@
//
// Prefix header
//
// The contents of this file are implicitly included at the beginning of every source file.
//
#ifdef __OBJC__
#import <Foundation/Foundation.h>
#endif
-79
View File
@@ -1,79 +0,0 @@
//
// HTMLText.m
// HTMLKit
//
// Created by Iska on 26/02/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import "HTMLText.h"
#import "HTMLElement.h"
#import "NSString+HTMLKit.h"
#import "HTMLNode+Private.h"
@implementation HTMLText
- (instancetype)init
{
return [self initWithData:@""];
}
- (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 ?: @""];
}
- (void)appendString:(NSString *)string
{
[self.data appendString:string];
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
{
HTMLText *copy = [super copyWithZone:zone];
copy.data = self.data;
return copy;
}
#pragma mark - Serialization
- (NSString *)outerHTML
{
if ([self.parentElement.tagName isEqualToAny:@"style", @"script", @"xmp", @"iframe", @"noembed", @"noframes",
@"plaintext", @"noscript", nil]) {
return self.data;
} else {
NSRange range = NSMakeRange(0, self.data.length);
NSMutableString *escaped = [self.data mutableCopy];
[escaped replaceOccurrencesOfString:@"&" withString:@"&amp;" options:0 range:range];
[escaped replaceOccurrencesOfString:@"\00A0" withString:@"&nbsp;" options:0 range:range];
[escaped replaceOccurrencesOfString:@"<" withString:@"&lt;" options:0 range:range];
[escaped replaceOccurrencesOfString:@">" withString:@"&gt;" options:0 range:range];
return escaped;
}
}
#pragma mark - Description
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p \"%@\">", self.class, self, self.data];
}
@end
-2
View File
@@ -1,2 +0,0 @@
/* Localized versions of Info.plist keys */
-2
View File
@@ -1,2 +0,0 @@
/* Localized versions of Info.plist keys */
+5
View File
@@ -0,0 +1,5 @@
import PackageDescription
let package = Package(
name: "HTMLKit"
)
+51 -16
View File
@@ -4,13 +4,19 @@
An Objective-C framework for your everyday HTML needs.
[![Build Status](https://img.shields.io/travis/iabudiab/HTMLKit/master.svg?style=flat)](https://travis-ci.org/iabudiab/HTMLKit)
[![Build Status](https://img.shields.io/travis/iabudiab/HTMLKit/develop.svg?style=flat)](https://travis-ci.org/iabudiab/HTMLKit)
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/HTMLKit.svg?style=flat)](https://cocoapods.org/pods/HTMLKit)
[![CocoaDocs](https://img.shields.io/cocoapods/metrics/doc-percent/HTMLKit.svg?style=flat)](http://cocoadocs.org/docsets/HTMLKit)
[![Platform](https://img.shields.io/cocoapods/p/HTMLKit.svg?style=flat)](http://cocoadocs.org/docsets/HTMLKit)
[![License MIT](https://img.shields.io/badge/license-MIT-4481C7.svg?style=flat)](https://opensource.org/licenses/MIT)
- [Quick Overview](#overview)
- [Installation](#installation)
- [Parsing](#parsing)
- [The DOM](#the-dom)
- [CSS3 Selectors](#css3-selectors)
# Quick Overview
HTMLKit is a [WHATWG specification](https://html.spec.whatwg.org/multipage/)-compliant framework for parsing and serializing HTML documents and document fragments for iOS and OSX. HTMLKit parses real-world HTML the same way modern web browsers would.
@@ -33,7 +39,8 @@ 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.
If you don't have Carthage yet, you can install it with Homebrew using the following command:
@@ -55,7 +62,8 @@ 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.
If you don't have CocoaPods yet, you can install it with the following command:
@@ -70,7 +78,7 @@ To add `HTMLKit` as a dependency into your project using CocoaPods just add the
use_frameworks!
target 'MyTarget' do
pod 'HTMLKit', '~> 0.9'
pod 'HTMLKit', '~> 1.1'
end
```
@@ -80,7 +88,23 @@ Then, run the following command:
$ pod install
```
### Manually
## 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: 1)
```
Then run:
```bash
$ swift build
```
## Manually
1- Add `HTMLKit` as git submodule
@@ -92,9 +116,9 @@ $ git submodule add https://github.com/iabudiab/HTMLKit.git
3- In the General panel of your target add `HTMLKit.framework` under the `Embedded Binaries`
# Features
# Parsing
# Parsing Documents
## Parsing Documents
Given some HTML content, you can parse it either via the `HTMLParser` or instatiate a `HTMLDocument` directly:
@@ -109,7 +133,7 @@ HTMLDocument *document = [parser parseDocument];
HTMLDocument *document = [HTMLDocument documentWithString:htmlString];
```
# Parsing Fragments
## Parsing Fragments
You can also prase HTML content as a document fragment with a specified context element:
@@ -132,7 +156,7 @@ nodes = [parser parseFragmentWithContextElement:bodyContext];
# The DOM
Here are some of the things you can do:
The DOM tree can be manipulated in several ways, here are just a few:
* Create new elements and assign attributes
@@ -156,7 +180,7 @@ NSArray *nodes = @[
[body appendNodes:nodes];
```
* Enumerate child elements and perform DOM manipulation
* Enumerate child elements and perform DOM editing
```objective-c
[body enumerateChildElementsUsingBlock:^(HTMLElement *element, NSUInteger idx, BOOL *stop) {
@@ -176,6 +200,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
@@ -183,12 +213,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
@@ -204,6 +228,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:
+103
View File
@@ -0,0 +1,103 @@
//
// HTMLCharacterData.m
// HTMLKit
//
// Created by Iska on 26/11/16.
// Copyright © 2016 BrainCookie. All rights reserved.
//
#import "HTMLCharacterData.h"
#import "HTMLNode+Private.h"
#import "HTMLDocument+Private.h"
#import "HTMLKitDOMExceptions.h"
@interface HTMLCharacterData ()
{
NSMutableString *_data;
}
@end
@implementation HTMLCharacterData
@synthesize data = _data;
- (instancetype)initWithName:(NSString *)name type:(HTMLNodeType)type data:(NSString *)data
{
self = [super initWithName:name type:type];
if (self) {
_data = [[NSMutableString alloc] initWithString:data ?: @""];
}
return self;
}
- (NSString *)textContent
{
return [_data copy];
}
- (void)setTextContent:(NSString *)textContent
{
[self setData:textContent];
}
- (NSUInteger)length
{
return _data.length;
}
#pragma mark - Data
NS_INLINE void CheckValidOffset(HTMLCharacterData *node, NSUInteger offset, NSString *cmd)
{
if (offset > node.length) {
[NSException raise:HTMLKitIndexSizeError
format:@"%@: Index Size Error, invalid index %lu for character data node %@.",
cmd, (unsigned long)offset, node];
}
}
- (void)setData:(NSString *)data
{
[self replaceDataInRange:NSMakeRange(0, self.length) withData:data];
}
- (void)appendData:(NSString *)data
{
[self replaceDataInRange:NSMakeRange(self.length, 0) withData:data];
}
- (void)insertData:(NSString *)data atOffset:(NSUInteger)offset
{
[self replaceDataInRange:NSMakeRange(offset, 0) withData:data];
}
- (void)deleteDataInRange:(NSRange)range
{
[self replaceDataInRange:range withData:@""];
}
- (void)replaceDataInRange:(NSRange)range withData:(NSString *)data
{
CheckValidOffset(self, range.location, NSStringFromSelector(_cmd));
range.length = MIN(range.length, self.length - range.location);
[_data replaceCharactersInRange:range withString:data];
[self.ownerDocument didRemoveCharacterDataInNode:self atOffset:range.location withLength:range.length];
[self.ownerDocument didAddCharacterDataToNode:self atOffset:range.location withLength:data.length];
}
- (NSString *)substringDataWithRange:(NSRange)range
{
return [_data substringWithRange:range];
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
{
HTMLCharacterData *copy = [super copyWithZone:zone];
copy->_data = [_data mutableCopy];
return copy;
}
@end
@@ -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
+32
View File
@@ -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;
}
@@ -9,19 +9,19 @@
#import "HTMLDocument.h"
#import "HTMLParser.h"
#import "HTMLNodeIterator.h"
#import "HTMLRange.h"
#import "HTMLCharacterData.h"
#import "HTMLText.h"
#import "HTMLKitDOMExceptions.h"
#import "HTMLNode+Private.h"
@interface HTMLNodeIterator (Private)
- (void)runRemovingStepsForNode:(HTMLNode *)oldNode
withOldParent:(HTMLNode *)oldParent
andOldPreviousSibling:(HTMLNode *)oldPreviousSibling;
@end
#import "HTMLNodeIterator+Private.h"
#import "HTMLRange+Private.h"
@interface HTMLDocument ()
{
HTMLDocument *_inertTemplateDocument;
NSMutableArray *_nodeIterators;
NSMutableArray *_ranges;
}
@property (nonatomic, assign) HTMLDocumentReadyState readyState;
@end
@@ -42,6 +42,7 @@
if (self) {
_readyState = HTMLDocumentLoading;
_nodeIterators = [NSMutableArray new];
_ranges = [NSMutableArray new];
}
return self;
}
@@ -143,14 +144,62 @@
[_nodeIterators removeObject:iterator];
}
#pragma mark - Ranges
- (void)attachRange:(HTMLRange *)range
{
[_ranges addObject:range];
}
- (void)detachRange:(HTMLRange *)range
{
[_ranges removeObject:range];
}
- (void)didRemoveCharacterDataInNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length
{
for (HTMLRange *range in _ranges) {
[range didRemoveCharacterDataInNode:node atOffset:offset withLength:length];
}
}
- (void)didAddCharacterDataToNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length
{
for (HTMLRange *range in _ranges) {
[range didAddCharacterDataToNode:node atOffset:offset withLength:length];
}
}
- (void)didInsertNewTextNode:(HTMLText *)newNode intoParent:(HTMLNode *)parent afterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset
{
for (HTMLRange *range in _ranges) {
[range didInsertNewTextNode:newNode intoParent:parent afterSplittingTextNode:node atOffset:offset];
}
}
- (void)clampRangesAfterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset
{
for (HTMLRange *range in _ranges) {
[range clampRangesAfterSplittingTextNode:node atOffset:offset];
}
}
#pragma mark - Mutation Callback
- (void)runRemovingStepsForNode:(HTMLNode *)oldNode
withOldParent:(HTMLNode *)oldParent
andOldPreviousSibling:(HTMLNode *)oldPreviousSibling
{
for (HTMLRange *range in _ranges) {
[range runRemovingStepsForNode:oldNode
withOldParent:oldParent
andOldPreviousSibling:oldPreviousSibling];
}
for (HTMLNodeIterator *iterator in _nodeIterators) {
[iterator runRemovingStepsForNode:oldNode
withOldParent:oldParent
andOldPreviousSibling:oldPreviousSibling];
withOldParent:oldParent
andOldPreviousSibling:oldPreviousSibling];
}
}
@@ -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;
}
@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.9.3</string>
<string>1.1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
@@ -6,6 +6,7 @@
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "HTMLKitDOMExceptions.h"
NSString * const HTMLKitHierarchyRequestError = @"HierarchyRequestError";
@@ -13,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";
+61 -28
View File
@@ -16,12 +16,8 @@
#import "HTMLKitDOMExceptions.h"
#import "HTMLNodeFilter.h"
#import "CSSSelector.h"
@interface HTMLDocument (Private)
- (void)runRemovingStepsForNode:(HTMLNode *)oldNode
withOldParent:(HTMLNode *)oldParent
andOldPreviousSibling:(HTMLNode *)oldPreviousSibling;
@end
#import "HTMLDocument+Private.h"
#import "HTMLDOMUtils.h"
@interface HTMLNode ()
{
@@ -62,6 +58,11 @@
[self.childNodes.array makeObjectsPerformSelector:@selector(setOwnerDocument:) withObject:ownerDocument];
}
- (HTMLNode *)rootNode
{
return _parentNode == nil ? self : _parentNode.rootNode;
}
- (void)setParentNode:(HTMLNode *)parentNode
{
_parentNode = parentNode;
@@ -118,11 +119,21 @@
return node.asElement;
}
- (NSUInteger)index
{
return [self.parentNode indexOfChildNode:self];
}
- (NSString *)textContent
{
return nil;
}
- (NSUInteger)length
{
return self.childNodesCount;
}
#pragma mark - Cast
- (HTMLElement *)asElement
@@ -155,6 +166,11 @@
return self.childNodes.count;
}
- (BOOL)isEmpty
{
return self.length == 0;
}
- (HTMLNode *)childNodeAtIndex:(NSUInteger)index
{
return [self.childNodes objectAtIndex:index];
@@ -328,41 +344,43 @@
return HTMLDocumentPositionEquivalent;
}
NSArray * (^ ancestorNodes) (HTMLNode *) = ^ NSArray * (HTMLNode *node) {
NSMutableArray *ancestors = [NSMutableArray array];
for (HTMLNode *node = self; node; node = node.parentNode) {
[ancestors addObject:node];
}
return ancestors;
};
NSArray *ancestors1 = ancestorNodes(self);
NSArray *ancestors2 = ancestorNodes(otherNode);
if (ancestors1.lastObject != ancestors2.lastObject) {
return HTMLDocumentPositionDisconnected |
HTMLDocumentPositionImplementationSpecific |
HTMLDocumentPositionFollowing;
if (self.ownerDocument != otherNode.ownerDocument) {
return (HTMLDocumentPositionDisconnected | HTMLDocumentPositionImplementationSpecific |
self.hash < otherNode.hash ? HTMLDocumentPositionPreceding : HTMLDocumentPositionFollowing);
}
for (NSUInteger i = MIN(ancestors1.count - 1, ancestors2.count - 1); i; --i) {
HTMLNode *child1 = ancestors1[i];
HTMLNode *child2 = ancestors2[i];
NSArray *ancestors1 = GetAncestorNodes(self);
NSArray *ancestors2 = GetAncestorNodes(otherNode);
if (ancestors1.lastObject != ancestors2.lastObject) {
return (HTMLDocumentPositionDisconnected | HTMLDocumentPositionImplementationSpecific |
self.hash < otherNode.hash ? HTMLDocumentPositionPreceding : HTMLDocumentPositionFollowing);
}
NSUInteger index1 = ancestors1.count;
NSUInteger index2 = ancestors2.count;
for (NSUInteger i = MIN(index1, index2); i; --i) {
index1 -= 1;
index2 -= 1;
HTMLNode *child1 = ancestors1[index1];
HTMLNode *child2 = ancestors2[index2];
if (child1 != child2) {
for (HTMLNode *sibling = child1.nextSibling; sibling; sibling = sibling.nextSibling) {
if (sibling == child2) {
return HTMLDocumentPositionFollowing;
return HTMLDocumentPositionPreceding;
}
}
return HTMLDocumentPositionPreceding;
return HTMLDocumentPositionFollowing;
}
}
if (ancestors1.count < ancestors2.count) {
return HTMLDocumentPositionContainedBy | HTMLDocumentPositionFollowing;
} else {
return HTMLDocumentPositionContains | HTMLDocumentPositionPreceding;
} else {
return HTMLDocumentPositionContainedBy | HTMLDocumentPositionFollowing;
}
}
@@ -650,6 +668,21 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin
#endif
#pragma mark - Clone
- (instancetype)cloneNodeDeep:(BOOL)deep
{
HTMLNode *copy = [self copy];
if (deep) {
for (HTMLNode *child in self.childNodes) {
[copy appendNode:[child cloneNodeDeep:YES]];
}
}
return copy;
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
@@ -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;
+5 -10
View File
@@ -18,14 +18,7 @@
#import "HTMLMarker.h"
#import "NSString+HTMLKit.h"
#import "CSSSelectors.h"
@interface HTMLTokenizer (Private)
@property (nonatomic, weak) HTMLParser *parser;
@end
@interface HTMLDocument (Private)
@property (nonatomic, assign) HTMLDocumentReadyState readyState;
@end
#import "HTMLDocument+Private.h"
@interface HTMLParser ()
{
@@ -457,9 +450,11 @@
beforeChildNode:&child];
if (adjustedInsertionLocation.nodeType != HTMLNodeDocument) {
if (child != nil && child.previousSibling.nodeType == HTMLNodeText) {
[(HTMLText *)child.previousSibling appendString:data];
HTMLText *textNode = (HTMLText *)child.previousSibling;
[textNode appendData:data];
} else if (adjustedInsertionLocation.lastChild.nodeType == HTMLNodeText) {
[(HTMLText *)adjustedInsertionLocation.lastChild appendString:data];
HTMLText *textNode = (HTMLText *)adjustedInsertionLocation.lastChild;
[textNode appendData:data];
} else {
HTMLText *text = [[HTMLText alloc] initWithData:data];
[adjustedInsertionLocation insertNode:text beforeChildNode:child];
+753
View File
@@ -0,0 +1,753 @@
//
// HTMLRange.m
// HTMLKit
//
// Created by Iska on 20/11/16.
// Copyright © 2016 BrainCookie. All rights reserved.
//
#import "HTMLRange.h"
#import "HTMLDocument.h"
#import "HTMLKitDOMExceptions.h"
#import "HTMLDocument+Private.h"
#import "HTMLDOMUtils.h"
#import "HTMLNodeTraversal.h"
@interface HTMLRange ()
{
HTMLDocument *_ownerDocument;
}
@end
@implementation HTMLRange
#pragma mark - Lifecycle
- (instancetype)initWithDowcument:(HTMLDocument *)document
{
return [self initWithDowcument:document startContainer:document startOffset:0 endContainer:document endOffset:0];
}
- (instancetype)initWithDowcument:(HTMLDocument *)document
startContainer:(HTMLNode *)startContainer startOffset:(NSUInteger)startOffset
endContainer:(HTMLNode *)endContainer endOffset:(NSUInteger)endOffset
{
self = [super init];
if (self) {
_ownerDocument = document;
[_ownerDocument attachRange:self];
[self setStartNode:startContainer startOffset:startOffset];
[self setEndNode:endContainer endOffset:endOffset];
}
return self;
}
- (void)dealloc
{
[_ownerDocument detachRange:self];
}
#pragma mark - Properties
- (BOOL)isCollapsed
{
return _startContainer == _endContainer && _startOffset == _endOffset;
}
- (HTMLNode *)commonAncestorContainer
{
return GetCommonAncestorContainer(_startContainer, _endContainer);
}
- (HTMLNode *)rootNode
{
return _startContainer.rootNode;
}
#pragma mark - Boundaries
NS_INLINE void CheckValidBoundaryNode(HTMLDocument *document, HTMLNode *node, NSString *cmd)
{
if (node.ownerDocument != document) {
[NSException raise:HTMLKitWrongDocumentError
format:@"%@: Invalid Node Error, %@ is not in the same document.",
cmd, node];
}
}
NS_INLINE void CheckValidBoundaryNodeType(HTMLNode *node, NSString *cmd)
{
if (node == nil || node.nodeType == HTMLNodeDocumentType) {
[NSException raise:HTMLKitInvalidNodeTypeError
format:@"%@: Invalid Node Type Error, %@ is not a valid range boundary node.",
cmd, node];
}
}
NS_INLINE void CheckValidBoundaryOffset(HTMLNode *node, NSUInteger offset, NSString *cmd)
{
if (node.length < offset) {
[NSException raise:HTMLKitIndexSizeError
format:@"%@: Index Size Error, invalid index %lu for range boundary node %@.",
cmd, (unsigned long)offset, node];
}
}
NS_INLINE void CheckValidDocument(HTMLRange *lhs, HTMLRange *rhs, NSString *cmd)
{
if (lhs.rootNode != rhs.rootNode) {
[NSException raise:HTMLKitWrongDocumentError
format:@"%@: Wrong Document Error, ranges %@ and %@ are not in the same document.",
cmd, lhs, rhs];
}
}
NS_INLINE NSComparisonResult CompareBoundaries(HTMLNode *startNode, NSUInteger startOffset, HTMLNode *endNode, NSUInteger endOffset)
{
if (startNode == endNode) {
if (startOffset == endOffset) {
return NSOrderedSame;
} else if (startOffset < endOffset) {
return NSOrderedAscending;
} else {
return NSOrderedDescending;
}
}
HTMLDocumentPosition position = [startNode compareDocumentPositionWithNode:endNode];
if ((position & HTMLDocumentPositionFollowing) == HTMLDocumentPositionFollowing) {
if (CompareBoundaries(endNode, endOffset, startNode, startOffset) == NSOrderedAscending) {
return NSOrderedDescending;
} else {
return NSOrderedAscending;
}
}
if ((position & HTMLDocumentPositionContains) == HTMLDocumentPositionContains) {
HTMLNode *child = endNode;
while (child.parentNode != startNode) {
child = child.parentNode;
}
if (child.index < startOffset) {
return NSOrderedDescending;
}
}
return NSOrderedAscending;
}
- (void)setStartNode:(HTMLNode *)node startOffset:(NSUInteger)offset
{
CheckValidBoundaryNode(_ownerDocument, node, NSStringFromSelector(_cmd));
CheckValidBoundaryNodeType(node, NSStringFromSelector(_cmd));
CheckValidBoundaryOffset(node, offset, NSStringFromSelector(_cmd));
if (self.rootNode != node.rootNode ||
CompareBoundaries(node, offset, _endContainer, _endOffset) == NSOrderedDescending) {
_endContainer = node;
_endOffset = offset;
}
_startContainer = node;
_startOffset = offset;
}
- (void)setEndNode:(HTMLNode *)node endOffset:(NSUInteger)offset
{
CheckValidBoundaryNode(_ownerDocument, node, NSStringFromSelector(_cmd));
CheckValidBoundaryNodeType(node, NSStringFromSelector(_cmd));
CheckValidBoundaryOffset(node, offset, NSStringFromSelector(_cmd));
if (self.rootNode != node.rootNode ||
CompareBoundaries(node, offset, _startContainer, _startOffset) == NSOrderedAscending) {
_startContainer = node;
_startOffset = offset;
}
_endContainer = node;
_endOffset = offset;
}
- (void)setStartBeforeNode:(HTMLNode *)node
{
HTMLNode *parent = node.parentNode;
[self setStartNode:parent startOffset:node.index];
}
- (void)setStartAfterNode:(HTMLNode *)node
{
HTMLNode *parent = node.parentNode;
[self setStartNode:parent startOffset:node.index + 1];
}
- (void)setEndBeforeNode:(HTMLNode *)node
{
HTMLNode *parent = node.parentNode;
[self setEndNode:parent endOffset:node.index];
}
- (void)setEndAfterNode:(HTMLNode *)node
{
HTMLNode *parent = node.parentNode;
[self setEndNode:parent endOffset:node.index + 1];
}
- (void)collapseToStart
{
[self setEndNode:_startContainer endOffset:_startOffset];
}
- (void)collapseToEnd
{
[self setStartNode:_endContainer startOffset:_endOffset];
}
- (void)selectNode:(HTMLNode *)node
{
HTMLNode *parent = node.parentNode;
[self setStartNode:parent startOffset:node.index];
[self setEndNode:parent endOffset:node.index + 1];
}
- (void)selectNodeContents:(HTMLNode *)node
{
[self setStartNode:node startOffset:0];
[self setEndNode:node endOffset:node.length];
}
- (NSComparisonResult)compareBoundaryPoints:(HTMLRangeComparisonMethod)method sourceRange:(HTMLRange *)sourceRange
{
CheckValidDocument(self, sourceRange, NSStringFromSelector(_cmd));
switch (method) {
case HTMLRangeComparisonMethodStartToStart:
return CompareBoundaries(_startContainer, _startOffset, sourceRange.startContainer, sourceRange.startOffset);
case HTMLRangeComparisonMethodStartToEnd:
return CompareBoundaries(_endContainer, _endOffset, sourceRange.startContainer, sourceRange.startOffset);
case HTMLRangeComparisonMethodEndToEnd:
return CompareBoundaries(_endContainer, _endOffset, sourceRange.endContainer, sourceRange.endOffset);
case HTMLRangeComparisonMethodEndToStart:
return CompareBoundaries(_startContainer, _startOffset, sourceRange.endContainer, sourceRange.endOffset);
}
}
#pragma mark - Containment
- (NSComparisonResult)comparePoint:(HTMLNode *)node offset:(NSUInteger)offset
{
CheckValidBoundaryNode(_ownerDocument, node, NSStringFromSelector(_cmd));
CheckValidBoundaryNodeType(node, NSStringFromSelector(_cmd));
CheckValidBoundaryOffset(node, offset, NSStringFromSelector(_cmd));
if (CompareBoundaries(node, offset, _startContainer, _startOffset) == NSOrderedAscending) {
return NSOrderedAscending;
}
if (CompareBoundaries(node, offset, _endContainer, _endOffset) == NSOrderedDescending) {
return NSOrderedDescending;
}
return NSOrderedSame;
}
- (BOOL)containsPoint:(HTMLNode *)node offset:(NSUInteger)offset
{
return [self comparePoint:node offset:offset] == NSOrderedSame;
}
- (BOOL)intersectsNode:(HTMLNode *)node
{
if (self.rootNode != node.rootNode) {
return NO;
}
HTMLNode *parent = node.parentNode;
if (parent == nil) {
return YES;
}
NSUInteger offset = node.index;
if (CompareBoundaries(parent, offset, _endContainer, _endOffset) == NSOrderedAscending &&
CompareBoundaries(parent, offset + 1, _startContainer, _startOffset) == NSOrderedDescending) {
return YES;
}
return NO;
}
- (BOOL)containsNode:(HTMLNode *)node
{
return CompareBoundaries(_startContainer, _startOffset, node, 0) == NSOrderedAscending &&
CompareBoundaries(_endContainer, _endOffset, node, node.length) == NSOrderedDescending;
}
- (BOOL)partiallyContainsNode:(HTMLNode *)node
{
return [GetAncestorNodes(_startContainer) containsObject:node] || [GetAncestorNodes(_endContainer) containsObject:node];
}
- (NSArray *)containedNodes:(HTMLNode *)commonAncestor
{
NSMutableArray *containedNodes = [NSMutableArray array];
[commonAncestor.childNodes enumerateObjectsUsingBlock:^(HTMLNode * _Nonnull node, NSUInteger idx, BOOL * _Nonnull stop) {
if (node.nodeType == HTMLNodeDocumentType) {
[NSException raise:HTMLKitHierarchyRequestError format:@"Hierarchy Request Error, encountered a DOCTYPE contained in range: %@", self];
}
if ([self containsNode:node]) {
[containedNodes addObject:node];
}
}];
return containedNodes;
}
#pragma mark - Update Callbacks
- (void)didRemoveCharacterDataInNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length
{
if (_startContainer == node && _startOffset > offset) {
if (_startOffset <= offset + length) {
_startOffset = offset;
} else {
_startOffset = _startOffset - length;
}
}
if (_endContainer == node && _endOffset > offset) {
if (_endOffset <= offset + length) {
_endOffset = offset;
} else {
_endOffset = _endOffset - length;
}
}
}
- (void)didAddCharacterDataToNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length
{
if (_startContainer == node && _startOffset > offset) {
_startOffset = _startOffset + length;
}
if (_endContainer == node && _endOffset > offset) {
_endOffset = _endOffset + length;
}
}
- (void)didInsertNewTextNode:(HTMLText *)newNode intoParent:(HTMLNode *)parent afterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset
{
if (_startContainer == node && _startOffset > offset) {
_startContainer = newNode;
_startOffset -= offset;
}
if (_endContainer == node && _endOffset > offset) {
_endContainer = newNode;
_endOffset -= offset;
}
if (_startContainer == parent && _startOffset == node.index + 1) {
_startOffset += 1;
}
if (_endContainer == parent && _endOffset == node.index + 1) {
_endOffset += 1;
}
}
- (void)clampRangesAfterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset
{
if (_startContainer == node && _startOffset > offset) {
_startOffset = offset;
}
if (_endContainer == node && _endOffset > offset) {
_endOffset = offset;
}
}
- (void)runRemovingStepsForNode:(HTMLNode *)oldNode withOldParent:(HTMLNode *)oldParent andOldPreviousSibling:(HTMLNode *)oldPreviousSibling
{
NSUInteger oldIndex = oldPreviousSibling.index + 1;
if ([_startContainer containsNode:oldNode]) {
[self setStartNode:oldNode startOffset:oldIndex];
}
if ([_endContainer containsNode:oldNode]) {
[self setEndNode:oldNode endOffset:oldIndex];
}
if (_startContainer == oldParent && _startOffset > oldIndex) {
_startOffset -= 1;
}
if (_endContainer == oldParent && _endOffset > oldIndex) {
_endOffset -= 1;
}
}
#pragma mark - Mutations
NS_INLINE HTMLNode * GetHighestPartiallyContainedChild(HTMLNode *node, HTMLNode *root)
{
if (node == root) {
return nil;
}
while (node.parentNode != root) {
node = node.parentNode;
}
return node;
}
NS_INLINE HTMLCharacterData * CloneCharachterData(HTMLNode *node, NSUInteger start, NSUInteger length, BOOL delete)
{
HTMLCharacterData *clone = (HTMLCharacterData *)[node copy];
NSRange range = NSMakeRange(start, length);
[clone setData:[clone.data substringWithRange:range]];
if (delete) {
[(HTMLCharacterData *)node deleteDataInRange:range];
}
return clone;
}
- (void)deleteContents
{
if (self.isCollapsed) {
return;
}
if (_startContainer == _endContainer && [_startContainer isKindOfClass:[HTMLCharacterData class]]) {
[(HTMLCharacterData *)_startContainer deleteDataInRange:NSMakeRange(_startOffset, _endOffset - _startOffset)];
return;
}
HTMLNode *commonAncestor = self.commonAncestorContainer;
NSMutableArray *containedNodes = [NSMutableArray array];
HTMLNode *node = FollowingNode(_startContainer, commonAncestor);
while (node) {
if ([self containsNode:node]) {
[containedNodes addObject:node];
node = FollowingNodeSkippingChildren(node, commonAncestor);
} else {
node = FollowingNode(node, commonAncestor);
}
}
HTMLNode *newNode = _startContainer;
NSUInteger newOffset = _startOffset;
if (![_startContainer containsNode:_endContainer]) {
HTMLNode *referenceNode = _startContainer;
while (referenceNode.parentNode) {
if ([referenceNode.parentNode containsNode:_endContainer]) {
newNode = referenceNode.parentNode;
newOffset = referenceNode.index + 1;
break;
}
referenceNode = referenceNode.parentNode;
}
}
if ([_startContainer isKindOfClass:[HTMLCharacterData class]]) {
[(HTMLCharacterData *)_startContainer deleteDataInRange:NSMakeRange(_startOffset, _startContainer.length - _startOffset)];
}
for (HTMLNode *node in containedNodes) {
[node removeFromParentNode];
}
if ([_endContainer isKindOfClass:[HTMLCharacterData class]]) {
[(HTMLCharacterData *)_endContainer deleteDataInRange:NSMakeRange(0, _endOffset)];
}
[self setStartNode:newNode startOffset:newOffset];
[self setEndNode:newNode endOffset:newOffset];
}
- (HTMLDocumentFragment *)extractContents
{
HTMLDocumentFragment *fragment = [[HTMLDocumentFragment alloc] initWithDocument:_ownerDocument];
// Nothing todo
if (self.isCollapsed) {
return fragment;
}
// Same character data container, handle that and return
if (_startContainer == _endContainer && [_startContainer isKindOfClass:[HTMLCharacterData class]]) {
HTMLCharacterData *clone = CloneCharachterData(_startContainer, _startOffset, _endOffset - _startOffset, YES);
[fragment appendNode:clone];
return fragment;
}
HTMLNode *commonAncestor = self.commonAncestorContainer;
HTMLNode *firstPartiallyContainedChild = GetHighestPartiallyContainedChild(_startContainer, commonAncestor);
HTMLNode *lastPartiallyContainedChild = GetHighestPartiallyContainedChild(_endContainer, commonAncestor);
NSArray *containedNodes = [self containedNodes:commonAncestor];
HTMLNode *newNode = _startContainer;
NSUInteger newOffset = _startOffset;
if (![_startContainer containsNode:_endContainer]) {
HTMLNode *referenceNode = _startContainer;
while (referenceNode.parentNode) {
if ([referenceNode.parentNode containsNode:_endContainer]) {
newNode = referenceNode.parentNode;
newOffset = referenceNode.index + 1;
break;
}
referenceNode = referenceNode.parentNode;
}
}
if ([firstPartiallyContainedChild isKindOfClass:[HTMLCharacterData class]]) {
HTMLCharacterData *clone = CloneCharachterData(_startContainer, _startOffset, _startContainer.length - _startOffset, YES);
[fragment appendNode:clone];
} else if (firstPartiallyContainedChild != nil) {
HTMLNode *clone = [firstPartiallyContainedChild copy];
[fragment appendNode:clone];
HTMLRange *subRange = [[HTMLRange alloc] initWithDowcument:_ownerDocument
startContainer:_startContainer
startOffset:_startOffset
endContainer:firstPartiallyContainedChild
endOffset:firstPartiallyContainedChild.length];
HTMLDocumentFragment *subFragment = [subRange extractContents];
[clone appendNode:subFragment];
}
for (HTMLNode *node in containedNodes) {
[fragment appendNode:node];
}
if ([lastPartiallyContainedChild isKindOfClass:[HTMLCharacterData class]]) {
HTMLCharacterData *clone = CloneCharachterData(_endContainer, 0, _endOffset, YES);
[fragment appendNode:clone];
} else if (lastPartiallyContainedChild != nil) {
HTMLNode *clone = [lastPartiallyContainedChild copy];
[fragment appendNode:clone];
HTMLRange *subRange = [[HTMLRange alloc] initWithDowcument:_ownerDocument
startContainer:lastPartiallyContainedChild
startOffset:0
endContainer:_endContainer
endOffset:_endOffset];
HTMLDocumentFragment *subFragment = [subRange extractContents];
[clone appendNode:subFragment];
}
[self setStartNode:newNode startOffset:newOffset];
[self setEndNode:newNode endOffset:newOffset];
return fragment;
}
- (HTMLDocumentFragment *)cloneContents
{
HTMLDocumentFragment *fragment = [[HTMLDocumentFragment alloc] initWithDocument:_ownerDocument];
// Nothing todo
if (self.isCollapsed) {
return fragment;
}
// Same character data container, handle that and return
if (_startContainer == _endContainer && [_startContainer isKindOfClass:[HTMLCharacterData class]]) {
HTMLCharacterData *clone = CloneCharachterData(_startContainer, _startOffset, _endOffset - _startOffset, NO);
[fragment appendNode:clone];
return fragment;
}
HTMLNode *commonAncestor = self.commonAncestorContainer;
HTMLNode *firstPartiallyContainedChild = GetHighestPartiallyContainedChild(_startContainer, commonAncestor);
HTMLNode *lastPartiallyContainedChild = GetHighestPartiallyContainedChild(_endContainer, commonAncestor);
NSArray *containedNodes = [self containedNodes:commonAncestor];
if ([firstPartiallyContainedChild isKindOfClass:[HTMLCharacterData class]]) {
HTMLCharacterData *clone = CloneCharachterData(_startContainer, _startOffset, _startContainer.length - _startOffset, NO);
[fragment appendNode:clone];
} else if (firstPartiallyContainedChild != nil) {
HTMLNode *clone = [firstPartiallyContainedChild copy];
[fragment appendNode:clone];
HTMLRange *subRange = [[HTMLRange alloc] initWithDowcument:_ownerDocument
startContainer:_startContainer
startOffset:_startOffset
endContainer:firstPartiallyContainedChild
endOffset:firstPartiallyContainedChild.length];
HTMLDocumentFragment *subFragment = [subRange cloneContents];
[clone appendNode:subFragment];
}
for (HTMLNode *node in containedNodes) {
HTMLNode *clone = [node cloneNodeDeep:YES];
[fragment appendNode:clone];
}
if ([lastPartiallyContainedChild isKindOfClass:[HTMLCharacterData class]]) {
HTMLCharacterData *clone = CloneCharachterData(_endContainer, 0, _endOffset, NO);
[fragment appendNode:clone];
} else if (lastPartiallyContainedChild != nil) {
HTMLNode *clone = [lastPartiallyContainedChild copy];
[fragment appendNode:clone];
HTMLRange *subRange = [[HTMLRange alloc] initWithDowcument:_ownerDocument
startContainer:lastPartiallyContainedChild
startOffset:0
endContainer:_endContainer
endOffset:_endOffset];
HTMLDocumentFragment *subFragment = [subRange cloneContents];
[clone appendNode:subFragment];
}
return fragment;
}
#pragma mark - Insertion & Surround
NS_INLINE void CheckValidInsertionNode(HTMLNode *startContainer, HTMLNode *node, NSString *cmd)
{
if (startContainer == node || startContainer.nodeType == HTMLNodeComment ||
(startContainer.nodeType == HTMLNodeText && startContainer.parentNode == nil)) {
[NSException raise:HTMLKitHierarchyRequestError
format:@"%@: Hierarchy Request Error, cannot insert node into range: %@", cmd, node];
}
}
- (void)insertNode:(HTMLNode *)node
{
CheckValidInsertionNode(_startContainer, node, NSStringFromSelector(_cmd));
HTMLNode *referenceNode = nil;
if (_startContainer.nodeType == HTMLNodeText) {
referenceNode = _startContainer;
} else {
referenceNode = [_startContainer childNodeAtIndex:_startOffset];
}
HTMLNode *parent = _startContainer;
if (referenceNode != nil) {
parent = referenceNode.parentNode;
}
if (_startContainer.nodeType == HTMLNodeText) {
referenceNode = [(HTMLText *)_startContainer splitTextAtOffset:_startOffset];
}
if (node == referenceNode) {
referenceNode = referenceNode.nextSibling;
}
[node removeFromParentNode];
NSUInteger newOffset = referenceNode ? referenceNode.index : parent.length;
newOffset += (node.nodeType == HTMLNodeDocumentFragment) ? node.length : 1;
[parent insertNode:node beforeChildNode:referenceNode];
if (self.isCollapsed) {
[self setEndNode:parent endOffset:newOffset];
}
}
NS_INLINE void CheckValidSurroundState(HTMLRange *range, NSString *cmd)
{
for (HTMLNode *node in GetAncestorNodes(range.startContainer)) {
if ([node containsNode:range.endContainer]) {
return;
}
if (node.nodeType != HTMLNodeText) {
[NSException raise:HTMLKitInvalidStateError
format:@"%@: Invalid State Error, cannot surround range with a partially-contaied non-text node.", cmd];
}
};
for (HTMLNode *node in GetAncestorNodes(range.endContainer)) {
if ([node containsNode:range.startContainer]) {
return;
}
if (node.nodeType != HTMLNodeText) {
[NSException raise:HTMLKitInvalidNodeTypeError
format:@"%@: Invalid State Error, cannot surround range with a partially-contaied non-text node.", cmd];
}
};
}
NS_INLINE void CheckValidSurroundNodeType(HTMLNode *node, NSString *cmd)
{
if (node == nil || node.nodeType == HTMLNodeDocumentType || node.nodeType == HTMLNodeDocument ||
node.nodeType == HTMLNodeDocumentFragment) {
[NSException raise:HTMLKitInvalidNodeTypeError
format:@"%@: Invalid Node Type Error, %@ is not a valid new parent for a range.",
cmd, node];
}
}
- (void)surroundContents:(HTMLNode *)newParent
{
CheckValidSurroundState(self, NSStringFromSelector(_cmd));
CheckValidSurroundNodeType(newParent, NSStringFromSelector(_cmd));
HTMLDocumentFragment *fragment = [self extractContents];
[newParent removeAllChildNodes];
[self insertNode:newParent];
[newParent appendNode:fragment];
[self selectNode:newParent];
}
#pragma mark - Description & Stringifier
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p (%@, %lu), (%@, %lu)>", self.class, self,
_startContainer, (unsigned long)_startOffset,
_endContainer, (unsigned long)_endOffset];
}
- (NSString *)textContent
{
HTMLNode *lastNode = nil;
if ([_endContainer isKindOfClass:[HTMLCharacterData class]]) {
lastNode = FollowingNodeSkippingChildren(_endContainer, _ownerDocument);
} else if (_endContainer.childNodesCount > _endOffset) {
lastNode = [_endContainer childNodeAtIndex:_endOffset];
} else {
lastNode = FollowingNodeSkippingChildren(_endContainer, _ownerDocument);
}
NSMutableString *content = [NSMutableString string];
for (HTMLNode *node = _startContainer; node != lastNode; node = FollowingNode(node, _ownerDocument)) {
if (node.nodeType == HTMLNodeText) {
HTMLText *text = (HTMLText *)node;
if (node == _startContainer) {
NSString *string = [text substringDataWithRange:NSMakeRange(_startOffset, _startContainer.length - _startOffset)];
[content appendString:string];
} else if (node == _endContainer) {
NSString *string = [text substringDataWithRange:NSMakeRange(0, _endOffset)];
[content appendString:string];
} else {
[content appendString:text.data];
}
}
}
return content;
}
@end
@@ -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
+94
View File
@@ -0,0 +1,94 @@
//
// HTMLText.m
// HTMLKit
//
// Created by Iska on 26/02/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import "HTMLText.h"
#import "HTMLElement.h"
#import "NSString+HTMLKit.h"
#import "HTMLCharacterData+Private.h"
#import "HTMLKitDOMExceptions.h"
#import "HTMLDocument+Private.h"
@implementation HTMLText
- (instancetype)init
{
return [self initWithData:@""];
}
- (instancetype)initWithData:(NSString *)data
{
return [super initWithName:@"#text" type:HTMLNodeText data:data];
}
- (void)appendString:(NSString *)string
{
[self appendData:string];
}
NS_INLINE void CheckValidOffset(HTMLNode *node, NSUInteger offset, NSString *cmd)
{
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
- (NSString *)outerHTML
{
if ([self.parentElement.tagName isEqualToAny:@"style", @"script", @"xmp", @"iframe", @"noembed", @"noframes",
@"plaintext", @"noscript", nil]) {
return self.data;
} else {
NSRange range = NSMakeRange(0, self.data.length);
NSMutableString *escaped = [self.data mutableCopy];
[escaped replaceOccurrencesOfString:@"&" withString:@"&amp;" options:0 range:range];
[escaped replaceOccurrencesOfString:@"\00A0" withString:@"&nbsp;" options:0 range:range];
[escaped replaceOccurrencesOfString:@"<" withString:@"&lt;" options:0 range:range];
[escaped replaceOccurrencesOfString:@">" withString:@"&gt;" options:0 range:range];
return escaped;
}
}
#pragma mark - Description
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p \"%@\">", self.class, self, self.data];
}
@end
@@ -13,10 +13,7 @@
#import "HTMLTokenizerStates.h"
#import "HTMLTokenizerCharacters.h"
#import "HTMLTokenizerEntities.h"
@interface HTMLParser (Private)
@property (nonatomic, strong, readonly) HTMLElement *adjustedCurrentNode;
@end
#import "HTMLParser+Private.h"
@interface HTMLTokenizer ()
{
@@ -45,7 +42,6 @@
BOOL _eof;
}
@property (nonatomic, weak) HTMLParser *parser;
@end
@implementation HTMLTokenizer
@@ -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
@@ -6,6 +6,8 @@
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
@class CSSSelector;
NS_ASSUME_NONNULL_BEGIN
@@ -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
+32
View File
@@ -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

Some files were not shown because too many files have changed in this diff Show More