193 Commits

Author SHA1 Message Date
iska c1cd1c0831 Merge branch 'release/2.1.0' 2017-10-12 22:44:49 +02:00
iska 1101556cfe Bump HTMLKit version to 2.1.0 2017-10-12 22:44:39 +02:00
iska 839a724432 Update podspec for 2.1.0 2017-10-12 22:44:39 +02:00
iska 300705cb63 Update jazzy.yaml for 2.1.0 2017-10-12 22:44:39 +02:00
iska 1963c010f9 Add Changelog entry for HTMLKit 2.1.0 2017-10-12 22:44:39 +02:00
iska 89ef6bc101 Update README for master branch 2017-10-12 22:44:39 +02:00
iska 9069923e82 Update handling for ParseErrorTokens in the HTML Parser 2017-10-12 22:25:46 +02:00
iska c16c647394 Add codecove.yml and update badges in README 2017-09-28 00:00:23 +02:00
iska 27b8eaac67 Add codecov after-script to upload coverage reports 2017-09-27 18:58:22 +02:00
iska 956c46ec45 Fix travis destinations for iOS 9.3 and 10.3 2017-09-27 00:11:56 +02:00
iska cb7e5d519d Update travis.yml for Xcode 9 and iOS 11 2017-09-26 22:38:09 +02:00
iska 4e1959bd81 Fix strict-prototypes blocks declarations throughout codebase 2017-09-23 02:31:48 +02:00
iska f0d9817c68 Restore default modulemap file name
Closes #13
2017-09-23 02:27:35 +02:00
iska fee2d14ffc Update project for Xcode 9 2017-09-23 02:26:46 +02:00
iska 4291840a53 Update .travis.yml 2017-09-10 15:56:36 +02:00
iska 4b7c260738 Update HTML Spec fixture to source as of 2017.09.09 2017-09-10 02:26:39 +02:00
iska 4a41b89ecf Update tokenizer implementation as per spec as of 2017.09.09
- Use new initial states in tests according to:
https://github.com/html5lib/html5lib-tests/pull/101

- Implement tokenization errors introduced in:
https://github.com/whatwg/html/pull/2701
https://github.com/html5lib/html5lib-tests/pull/92
2017-09-10 02:23:31 +02:00
iska d4ff2e3869 Add error-code property for ParseError token
In addition to the detailed reason message parse errors have an
error-code property that is specified in the WHATWG HTML spec.
2017-09-07 22:08:28 +02:00
iska 6dee6c1e74 Update input stream reader error handling
Use newly specified parse errors:
https://html.spec.whatwg.org/multipage/parsing.html#parse-errors
2017-09-07 22:05:02 +02:00
iska 20ebd8c603 Update html5lib-tests to latest commit as of 2017.09.06 2017-09-06 22:14:34 +02:00
iska c002299247 Merge branch 'release/2.0.6' into develop 2017-05-02 15:49:16 +02:00
iska bb6d7ccc67 Merge branch 'release/2.0.6' 2017-05-02 15:49:15 +02:00
iska bf655c7ea2 Bump HTMLKit version to 2.0.6 2017-05-02 15:49:08 +02:00
iska 6cd03b4eed Update podspec for 2.0.6 2017-05-02 15:48:54 +02:00
iska cc0734e470 Update jazzy.yaml for 2.0.6 2017-05-02 15:48:45 +02:00
iska eca6098361 Add Changelog entry for HTMLKit 2.0.6 2017-05-02 15:48:36 +02:00
iska 2cd5098d8a Update destinations and simulators in travis.yml 2017-05-02 14:01:48 +02:00
iska 514d23f7a0 Update SDK versions in travis.yml 2017-05-02 13:52:16 +02:00
iska 2446da3a6d Update travis xcode image to 8.3 2017-05-02 13:50:35 +02:00
iska 644c180f81 Improve memory allocation/consumption in the Stack of Open Elements
Instead of allocating new dictionaries for the scope elements, the scope
checks are just unrolled in-place. Now we have 6 almost identical methods
that differ only in the inline-check-method. Not optimal but minimal
memory and performance penalty.

This should reduce memory consumption and increase the performance
while parsing, see issue #10
2017-04-26 21:31:55 +02:00
iska 47ec0867a8 Improve reverseObjectEnumerator usage while parsing HTML
Do not use the `allObjects` call on the reverse enumerators in the Parser
and the List of Active Formatting Elements to prevent allocating a new
array of the unenumerated objects.

This should reduce memory consumption while parsing, see issue #10
2017-04-25 00:12:39 +02:00
iska 1cd1a915d3 Replace NSStringFromSelector calls with constants in HTMLNode
This should reduce allocations during HTML parsing. See Issue #10
2017-04-24 22:58:49 +02:00
iska 8379cee44f Fix lazy allocation of childNodes Set in HTMLNode
The call self.childNodes always allocates the collection even if a nil
value is acceptable, i.e. hasChildNodes should return true even if the
childNodes Set is nil but should not allocate it yet.

Hence self.childNodes should only be used when appending child nodes.
2017-04-24 22:36:02 +02:00
iska 4094f51458 Merge branch 'release/2.0.5' 2017-04-19 00:02:10 +02:00
iska 57c931aece Merge branch 'release/2.0.5' into develop 2017-04-19 00:02:10 +02:00
iska 76b379448b Bump HTMLKit version to 2.0.5 2017-04-18 23:59:57 +02:00
iska 49bdfa018e Update podspec for 2.0.5 2017-04-18 23:59:37 +02:00
iska 746ef2ea3b Update jazzy.yaml for 2.0.5 2017-04-18 23:59:27 +02:00
iska 653f6cdf7e Add Changelog entry for HTMLKit 2.0.5 2017-04-18 23:58:46 +02:00
iska d9670cddf4 Add workaround for Xcode8.3 issue with modulemaps
This should be the fix for #12. However the issue will stay open until
tested with Xcode 8.3.1
2017-04-09 20:58:25 +02:00
iska 30389c5010 Add an autorelease pool in the Tokenizer’s iteration method 2017-04-09 20:54:01 +02:00
iska 14dfc0b854 Replace performSelector with for-loop in HTML Node methods 2017-04-09 20:53:11 +02:00
iska b693a60358 Use lazy allocation for underlying collections in HTML Nodes
Do not allocate empty collections for child nodes or attributes when
initializing new HTML Nodes or Elements. These are initialized the first
time they are accessed.

Analogously, the mutable data string of CharacterData is also allocated
with the empty string on first access.
2017-04-09 20:52:06 +02:00
iska d35e6c4d91 Fix memory leaks in CSS Input Stream
- Release allocated instances when returning nil
- Pass autoreleased instances on valid return value
2017-04-09 20:47:55 +02:00
iska c353512a65 Merge branch 'release/2.0.4' into develop 2017-04-03 22:21:43 +02:00
iska aaee85909b Merge branch 'release/2.0.4' 2017-04-03 22:21:42 +02:00
iska 8d3f24ef8b Bump HTMLKit version to 2.0.4 2017-04-03 22:21:15 +02:00
iska b652935739 Update podspec for 2.0.4 2017-04-03 22:20:51 +02:00
iska 601c83a51f Update jazzy.yaml for 2.0.4 2017-04-03 22:20:42 +02:00
iska 774ac35bbb Add Changelog entry for HTMLKit 2.0.4 2017-04-03 22:20:05 +02:00
iska 9651d12fd0 Deprecate for old HTMLRange’s initializers with typo 2017-03-31 17:23:30 +02:00
Iskandar Abudiab 0899a4013d Merge pull request #11 from tali/develop
Fix typo in HTMLRange's initializer

initWithDowcument: -> initWithDocument
2017-03-31 17:19:16 +02:00
Martin Waitz 22de41d912 Fix typo Dowcument -> Document 2017-03-31 08:53:11 +02:00
Iskandar Abudiab 2f47e733bd Merge pull request #8 from tali/develop
Fix tests for Swift 3.1
2017-03-14 00:13:36 +01:00
Martin Waitz 36f5df015a [Tests] fix includes 2017-03-13 23:11:16 +01:00
Martin Waitz bbc5467b33 [modulemap] use textual header for character definitions
Both CSSCodePoints.h and HTMLTokenizerCharacters.h define the same
symbols.
They are never included by the same compilation unit, but when they are
compiled into the same module, they create a conflict.
Fix this conflict by using `textual header`.
2017-03-13 23:11:16 +01:00
iska 7ef19f5e20 Ignore performance tests
For now these are ignored for faster testing/iterations.
The test fixture should also be replaced with several smaller
and more representative ones, since it is relatively big.
2017-03-13 23:04:58 +01:00
iska a102f5cd7d Fix loading of test resources
Building and testing the project via Xcode copies the resources into
the testing bundle, which is not the case for SwiftPM. Hence the helper
method, that tries loading the resource from the bundle first. If it is
nil then the resource is loaded from the test module path directly.
2017-03-13 23:01:53 +01:00
iska 7a40e8bafd Merge branch 'release/2.0.3' 2017-03-06 00:31:03 +01:00
iska 40b057b569 Merge branch 'release/2.0.3' into develop 2017-03-06 00:31:03 +01:00
iska a7ace9929f Bump HTMLKit version to 2.0.3 2017-03-06 00:30:41 +01:00
iska 32b674f786 Update podspec for 2.0.3 2017-03-06 00:30:37 +01:00
iska e9e3f04136 Update jazzy.yaml for 2.0.3 2017-03-06 00:30:32 +01:00
iska 44f1bc6946 Add Changelog entry for HTMLKit 2.0.3 2017-03-06 00:30:26 +01:00
Iskandar Abudiab ef36e674a4 Merge pull request #6 from tali/develop
Fix compilation for Swift 3.1
2017-03-03 10:32:58 +01:00
Martin Waitz b8f1123b86 Fix compilation for Swift 3.1
Move the `module.modulemap` into `Sources/include` and add an exclude
for `Tests/Fixtures`.
2017-03-03 08:25:14 +01:00
iska 17a2e375e0 Merge branch 'release/2.0.2' into develop 2017-02-26 21:46:00 +01:00
iska b5470a61c3 Merge branch 'release/2.0.2' 2017-02-26 21:45:59 +01:00
iska fb60759492 Bump HTMLKit version to 2.0.2 2017-02-26 21:45:47 +01:00
iska f21cbf2a5e Update podspec for 2.0.2 2017-02-26 21:45:27 +01:00
iska 50c48e589a Update jazzy.yaml for 2.0.2 2017-02-26 21:45:16 +01:00
iska bb9987636b Add Changelog entry for HTMLKit 2.0.2 2017-02-26 21:45:05 +01:00
iska 60b086f72e Exclude tests resources from Swift Package 2017-02-25 02:07:03 +01:00
iska f1fd0f3ef6 Move test files into HTMLKitTests subfolder Swift Package Manager 2017-02-25 02:07:02 +01:00
iska 5bb7122d7e Use a weak-memory NSHashTable for referencing ranges in HTML Document
This breaks the retain cycle between the document and the attached range

Fixes #5
2017-02-22 23:55:35 +01:00
iska 356709a096 Use a weak-memory NSHashTable for referencing iterators in HTML Document
This breaks the retain cycle between the document and the attached node
iterator.

Fixes #4
2017-02-22 23:54:55 +01:00
iska 963a2b9c09 Merge branch 'hotfix/2.0.1' 2017-02-20 23:03:40 +01:00
iska 9926179a62 Merge branch 'hotfix/2.0.1' into develop 2017-02-20 23:03:40 +01:00
iska cb9886cbf7 Bump HTMLKit version to 2.0.1 2017-02-20 23:02:55 +01:00
iska 717f62a5f1 Update jazzy.yaml for 2.0.1 2017-02-20 23:02:37 +01:00
iska f5476e0c4c Update podspec for 2.0.1 2017-02-20 23:02:22 +01:00
iska b9099a26ed Add Changelog entry for HTMLKit 2.0.1 2017-02-20 23:01:05 +01:00
iska b57e27137a Set INSTALL_PATH and DYLIB_INSTALL_NAME_BASE to @rpath for macOS target 2017-02-20 22:55:15 +01:00
iska dbe9c575a1 Merge branch 'release/2.0.0' 2017-02-11 19:23:07 +01:00
iska 17961b94a4 Merge branch 'release/2.0.0' into develop 2017-02-11 19:23:07 +01:00
iska d6d1e77471 Bump HTMLKit version to 2.0.0 2017-02-11 19:22:24 +01:00
iska 987d37e042 Update README 2017-02-11 19:22:14 +01:00
iska 6d7e9bca1b Update jazzy.yaml for 2.0.0 2017-02-11 19:22:05 +01:00
iska 0b8b18b675 Update podspec for 2.0.0 2017-02-11 19:21:55 +01:00
iska 325ca47587 Add Changelog entry for HTMLKit 2.0.0 2017-02-11 19:21:44 +01:00
iska 05c6b9d294 Expect <menuitem> to parse like an unknown element
This reverses the parser change introduced in commit:
b0bad5068f

The list of special elements is not reverted

The change was reverted in the spec:
https://github.com/whatwg/html/pull/2319

and the html5lib-tests:
https://github.com/html5lib/html5lib-tests/pull/88
2017-02-11 18:27:25 +01:00
iska 76753b1d95 Update html5lib-tests to latest commit as of 2017.02.11
Commit: 13f1805136f8a6b3883d1cf7c727ad0a6b97eab1
2017-02-11 17:33:22 +01:00
iska daf71a01f3 Merge branch 'release/1.1.0' 2017-01-15 00:01:28 +01:00
iska da2344e5cf Merge branch 'release/1.1.0' into develop 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
109 changed files with 13035 additions and 86864 deletions
+5
View File
@@ -0,0 +1,5 @@
codecov:
branch: develop
ignore:
- "Tests/"
+3 -1
View File
@@ -40,5 +40,7 @@ Pods/
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
Carthage/Checkouts
Carthage/Build
Carthage/Build
# Jazzy
docs/
+1 -1
View File
@@ -1,3 +1,3 @@
[submodule "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: 2.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)
+20 -17
View File
@@ -1,5 +1,5 @@
language: objective-c
osx_image: xcode7.3
osx_image: xcode9
branches:
except:
@@ -14,29 +14,32 @@ env:
- LANG=en_US.UTF-8
- WORKSPACE=HTMLKit.xcworkspace
- IOS_FRAMEWORK_SCHEME=HTMLKit-iOS
- OSX_FRAMEWORK_SCHEME=HTMLKit-OSX
- MACOS_FRAMEWORK_SCHEME=HTMLKit-macOS
- WATCHOS_FRAMEWORK_SCHEME="HTMLKit-watchOS"
- TVOS_FRAMEWORK_SCHEME="HTMLKit-tvOS"
- IOS_SDK=iphonesimulator9.3
- OSX_SDK=macosx10.11
- WATCHOS_SDK=watchsimulator2.2
- TVOS_SDK=appletvsimulator9.2
- IOS_SDK=iphonesimulator11.0
- MACOS_SDK=macosx10.13
- WATCHOS_SDK=watchsimulator4.0
- TVOS_SDK=appletvsimulator11.0
matrix:
- DESTINATION="OS=9.0,name=iPhone 6" SIMULATOR="iPhone 6 (9.0)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
- DESTINATION="OS=9.1,name=iPhone 6 Plus" SIMULATOR="iPhone 6 Plus (9.1)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
- DESTINATION="OS=9.2,name=iPhone 6S" SIMULATOR="iPhone 6S (9.2)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
- DESTINATION="OS=9.3,name=iPhone 6S Plus" SIMULATOR="iPhone 6S Plus (9.3)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
- DESTINATION="arch=x86_64" SIMULATOR="" SCHEME="$OSX_FRAMEWORK_SCHEME" SDK="$OSX_SDK"
- DESTINATION="OS=2.2,name=Apple Watch - 42mm" SIMULATOR="Apple Watch - 42mm (2.2)" SCHEME="$WATCHOS_FRAMEWORK_SCHEME" SDK="$WATCHOS_SDK"
- DESTINATION="OS=9.2,name=Apple TV 1080p" SIMULATOR="Apple TV 1080p (9.2)" SCHEME="$TVOS_FRAMEWORK_SCHEME" SDK="$TVOS_SDK"
- DESTINATION="arch=x86_64" SCHEME="$MACOS_FRAMEWORK_SCHEME" SDK="$MACOS_SDK"
- DESTINATION="OS=9.3,name=iPhone 6s Plus" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
- DESTINATION="OS=10.3.1,name=iPhone 7 Plus" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
- DESTINATION="OS=11.0,name=iPhone X" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
- DESTINATION="OS=3.2,name=Apple Watch Series 2 - 42mm" SCHEME="$WATCHOS_FRAMEWORK_SCHEME" SDK="$WATCHOS_SDK"
- DESTINATION="OS=4.0,name=Apple Watch Series 3 - 42mm" SCHEME="$WATCHOS_FRAMEWORK_SCHEME" SDK="$WATCHOS_SDK"
- DESTINATION="OS=10.2,name=Apple TV 1080p" SCHEME="$TVOS_FRAMEWORK_SCHEME" SDK="$TVOS_SDK"
- DESTINATION="OS=11.0,name=Apple TV 4K" SCHEME="$TVOS_FRAMEWORK_SCHEME" SDK="$TVOS_SDK"
script:
- set -o pipefail
- xcodebuild -version
- xcodebuild -showsdks
- SIMULATOR_ID=$(xcrun instruments -s devices | grep -io "$SIMULATOR \[.*\]" | grep -o "\[.*\]" | sed "s/^\[\(.*\)\]$/\1/")
- open -b com.apple.iphonesimulator --args -CurrentDeviceUDID $SIMULATOR_ID
- xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO clean build | xcpretty -c
- travis_retry xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO clean build | xcpretty -c
- if [ "$SDK" != "$WATCHOS_SDK" ]; then
xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO test | xcpretty -c;
travis_retry xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES test | xcpretty -c;
fi
after_success:
- bash <(curl -s https://codecov.io/bash)
+148 -7
View File
@@ -1,5 +1,152 @@
# Change Log
## [2.1.0](https://github.com/iabudiab/HTMLKit/releases/tag/2.1.0)
Released on 2017.10.12
### Added
- Standarized tokenizer error codes:
- [whatwg/html#2701](https://github.com/whatwg/html/pull/2701)
- [html5lib/html5lib-tests#92](https://github.com/html5lib/html5lib-tests/pull/92)
### Updated
- Project for Xcode 9
- Travis config for iOS 11.0, macOS 10.13, tvOS 11.0 and watchOS 4.0
- Updated HTML5Lib-Tests submodule (cbafeba)
## [2.0.6](https://github.com/iabudiab/HTMLKit/releases/tag/2.0.6)
Released on 2017.05.02
### Added
- Memory consumption improvements (issue #10)
- Allocate `childNodes` collection in `HTMLNode` only when inserting child nodes
- Replace `NSStringFromSelector` calls with constants in `HTMLNode` validations
- Improve `reverseObjectEnumerator` usage while parsing HTML
- Rewrite internal logic of the `HTMLStackOfOpenElements` to prevent excessive allocations
## [2.0.5](https://github.com/iabudiab/HTMLKit/releases/tag/2.0.5)
Released on 2017.04.19
### Fixed
- Xcode 8.3 issue with modulemaps
- Temporary workaround (renamed modulemap file)
- Memory Leaks in `CSSInputStream`
### Added
- Minor memory consumption improvements
- Collections for child nodes or attributes of HTML Nodes or Elements are allocated lazily
- Underyling data string of `CharacterData` is allocated on first access
- Autorelease pool for the main `HTMLTokenizer` loop
## [2.0.4](https://github.com/iabudiab/HTMLKit/releases/tag/2.0.4)
Released on 2017.04.2
### Fixed
- Testing with Swift 3.1
- Fixed by @tali in PR #8
### Deprecated
- `HTMLRange` initializers with typo
- `initWithDowcument:startContainer:startOffset:endContainer:endOffset:`
## [2.0.3](https://github.com/iabudiab/HTMLKit/releases/tag/2.0.3)
Released on 2017.03.6
### Fixed
- Compilation for Swift 3.1
- Fixed by @tali in PR #6
## [2.0.2](https://github.com/iabudiab/HTMLKit/releases/tag/2.0.2)
Released on 2017.02.26
### Fixed
- Retain cycles in `HTMLNodeIterator` (issue #4)
- Retain cycles in `HTMLRange` (issue #5)
- The layout of `HTMLKit` tests module for Swift Package Manager
## [2.0.1](https://github.com/iabudiab/HTMLKit/releases/tag/2.0.1)
Released on 2017.02.20
### Hotifx
- Set `INSTALL_PATH` and `DYLIB_INSTALL_NAME_BASE` to `@rpath` for macOS target
- This fixes embedding `HTMLKit` in a Cocoa application
## [2.0.0](https://github.com/iabudiab/HTMLKit/releases/tag/2.0.0)
Released on 2017.02.11
### Spec Change
- Make `<menuitem>` parse like an unkonwn element. See:
- [whatwg/html#2319](https://github.com/whatwg/html/pull/2319)
- [html5lib/html5lib-tests#88](https://github.com/html5lib/html5lib-tests/pull/88)
### Updated
- Updated HTML5Lib-Tests submodule (13f1805)
## [1.1.0](https://github.com/iabudiab/HTMLKit/releases/tag/1.1.0)
Released on 2017.01.14
### Added
- `DOM Ranges` implementation ([spec](https://dom.spec.whatwg.org/#ranges))
- `HTMLChatacterData` as base class for `HTMLText` & `HTMLComment`
- `HTMLText` and `HTMLComment` no longer extend `HTMLNode` directly
- `splitText` implementation for `HTMLText` nodes
- `index` property for `HTMLNode`
- `cloneNodeDeep` method for `HTMLNode`
### Deprecated
- `appendString` method in `HTMLText` in favor of `appendData` from the supperclass `HTMLCharacterData`
## [1.0.0](https://github.com/iabudiab/HTMLKit/releases/tag/1.0.0)
Released on 2016.09.28
### Added
- Jazzy configuration file
- Example HTMLKit project
### Updated
- Project for Xcode 8
- Playground syntax for Swift 3
- Travis config for iOS 10.0, macOS 10.12, tvOS 10.0 and watchOS 3.0
- Deployment targets to macOS 10.9, iOS 9.0, tvOS 9.0 and watchOS 2.0
### Fixed
- Nullability annotation in `CSSSelectorParser` class
- Missing lightweight generics in `HTMLParser`, `HTMLNode` & `HTMLElement`
## [0.9.4](https://github.com/iabudiab/HTMLKit/releases/tag/0.9.4)
Released on 2016.09.03
@@ -40,16 +187,12 @@ This release passes all tokenizer and tree construction html5lib-tests as of 201
- `<isindex>` is completely removed from the spec now, therefore it is dropped from the implementation
- `Tokenizer` and `Tree-Construction` tests are now generated dynamically
- Test failures are collected by a `XCTestObservation` for better reporting
- `<isindex>` is completely removed from the spec now, therefore it is dropped from the implementation
- `Tokenizer` and `Tree-Construction` tests are now generated dynamically
- Test failures are collected by a `XCTestObservation` for better reporting
### Fixed
- Parser now checks the qualified name instead of the local name when handling elements in the `MathML` and `SVG` namespaces
## [0.9.1](https://github.com/iabudiab/HTMLKit/releases/tag/0.9.1)
Released on 2016.01.29
@@ -59,7 +202,6 @@ Released on 2016.01.29
- Travis-CI integration.
- CocoaPods spec.
### Changed
- Warnings are treated as errors.
@@ -91,7 +233,6 @@ This is the first public release of `HTMLKit`.
- Unused namespaces.
- Historical node types.
### Fixed
- `lt`, `gt` & `eq` CSS Selectors method declarations.
@@ -0,0 +1,273 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
629A63CD1D9AFE0E0089679F /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 629A63CC1D9AFE0E0089679F /* main.swift */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
629A63C71D9AFE0E0089679F /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
629A63C91D9AFE0E0089679F /* HTMLKitExample */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = HTMLKitExample; sourceTree = BUILT_PRODUCTS_DIR; };
629A63CC1D9AFE0E0089679F /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
629A63C61D9AFE0E0089679F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
629A63C01D9AFE0E0089679F = {
isa = PBXGroup;
children = (
629A63CB1D9AFE0E0089679F /* HTMLKitExample */,
629A63CA1D9AFE0E0089679F /* Products */,
);
sourceTree = "<group>";
};
629A63CA1D9AFE0E0089679F /* Products */ = {
isa = PBXGroup;
children = (
629A63C91D9AFE0E0089679F /* HTMLKitExample */,
);
name = Products;
sourceTree = "<group>";
};
629A63CB1D9AFE0E0089679F /* HTMLKitExample */ = {
isa = PBXGroup;
children = (
629A63CC1D9AFE0E0089679F /* main.swift */,
);
path = HTMLKitExample;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
629A63C81D9AFE0E0089679F /* HTMLKitExample */ = {
isa = PBXNativeTarget;
buildConfigurationList = 629A63D01D9AFE0E0089679F /* Build configuration list for PBXNativeTarget "HTMLKitExample" */;
buildPhases = (
629A63C51D9AFE0E0089679F /* Sources */,
629A63C61D9AFE0E0089679F /* Frameworks */,
629A63C71D9AFE0E0089679F /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = HTMLKitExample;
productName = HTMLKitExample;
productReference = 629A63C91D9AFE0E0089679F /* HTMLKitExample */;
productType = "com.apple.product-type.tool";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
629A63C11D9AFE0E0089679F /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0800;
LastUpgradeCheck = 0900;
ORGANIZATIONNAME = iabudiab;
TargetAttributes = {
629A63C81D9AFE0E0089679F = {
CreatedOnToolsVersion = 8.0;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 629A63C41D9AFE0E0089679F /* Build configuration list for PBXProject "HTMLKitExample" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 629A63C01D9AFE0E0089679F;
productRefGroup = 629A63CA1D9AFE0E0089679F /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
629A63C81D9AFE0E0089679F /* HTMLKitExample */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
629A63C51D9AFE0E0089679F /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
629A63CD1D9AFE0E0089679F /* main.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
629A63CE1D9AFE0E0089679F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
629A63CF1D9AFE0E0089679F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
};
name = Release;
};
629A63D11D9AFE0E0089679F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
629A63D21D9AFE0E0089679F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
629A63C41D9AFE0E0089679F /* Build configuration list for PBXProject "HTMLKitExample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
629A63CE1D9AFE0E0089679F /* Debug */,
629A63CF1D9AFE0E0089679F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
629A63D01D9AFE0E0089679F /* Build configuration list for PBXNativeTarget "HTMLKitExample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
629A63D11D9AFE0E0089679F /* Debug */,
629A63D21D9AFE0E0089679F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 629A63C11D9AFE0E0089679F /* Project object */;
}
@@ -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,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0900"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "629A63C81D9AFE0E0089679F"
BuildableName = "HTMLKitExample"
BlueprintName = "HTMLKitExample"
ReferencedContainer = "container:HTMLKitExample.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "629A63C81D9AFE0E0089679F"
BuildableName = "HTMLKitExample"
BlueprintName = "HTMLKitExample"
ReferencedContainer = "container:HTMLKitExample.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "629A63C81D9AFE0E0089679F"
BuildableName = "HTMLKitExample"
BlueprintName = "HTMLKitExample"
ReferencedContainer = "container:HTMLKitExample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "629A63C81D9AFE0E0089679F"
BuildableName = "HTMLKitExample"
BlueprintName = "HTMLKitExample"
ReferencedContainer = "container:HTMLKitExample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,167 @@
//
// main.swift
// HTMLKitExample
//
// Created by Iska on 27/09/16.
// Copyright © 2016 iabudiab. All rights reserved.
//
import HTMLKit
// Simple scraper that is able to load a page, query via CSS Selectors, and following links
class Scraper {
enum ScrapingError: Error {
case DocumentNotLoaded
case ElementNotFound(String)
case InvalidAnchorUrl(String)
case CouldNotLoadPage(URL)
}
private var url: URL
private(set) var document: HTMLDocument?
init(url: URL) {
self.url = url
self.document = nil
}
func load() throws {
try loadDocument(at: url)
}
func listElements(matching selector: CSSSelector) throws -> [HTMLElement] {
guard let document = document else {
throw ScrapingError.DocumentNotLoaded
}
return document.elements(matching: selector)
}
func followLink(matchingSelector selector: CSSSelector) throws {
guard let document = document else {
throw ScrapingError.DocumentNotLoaded
}
guard let link = document.firstElement(matching: selector) else {
throw ScrapingError.ElementNotFound(selector.debugDescription)
}
guard let targetUrl = URL(string: link["href"], relativeTo: url) else {
throw ScrapingError.InvalidAnchorUrl(link["href"])
}
try loadDocument(at: targetUrl)
}
private func loadDocument(at url: URL) throws {
guard let content = try? String(contentsOf: url) else {
throw ScrapingError.CouldNotLoadPage(url)
}
document = HTMLDocument(string: content)
}
}
// A custom block-based selector, that matches only elements having the given text content:
// i.e. textContentSelector("Hello") will match <p>Hello</p> and <a href='example.com'>Hello</a>
// but wont match <div>World</div> or <p>Hello there</p>
func textContentSelector(text: String) -> CSSSelector {
return namedBlockSelector("[@textContent='\(text)']") { (element) -> Bool in
return element.textContent == text
}
}
// Helper function to create a typed-selector matching an anchor element that has the given
// text content.
func anchorElement(havingContent: String) -> CSSSelector {
return allOf(
[
typeSelector("a"),
textContentSelector(text: havingContent)
]
)
}
// Helper function to print the content of a github repository file content
func printRepositoryFile(element: HTMLElement) {
// A node iterator filter that iterates only <td> elements of class "content" i.e. <td class='content'>
let contentIterator = element.nodeIterator(showOptions: .element) { (node) -> HTMLNodeFilterValue in
guard let element = node as? HTMLElement else { return .reject }
if element.tagName == "td" && element["class"] == "content" {
return .accept
}
return .reject
}
for td in contentIterator {
// The cast is necessary because Swift3 wont import the generics info of the NSEnumerator class
// i.e. the nextObject() function alwasy has the following signature 'func nextObject() -> Any?'
let title = (td as AnyObject).textContent.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
print("- \(title)")
}
}
let htmlKitUrl = URL(string: "https://github.com/iabudiab/HTMLKit")!
let scraper = Scraper(url: htmlKitUrl)
do {
// Load the page
try scraper.load()
// Parse the selector
let repositoryContent = try CSSSelectorParser.parseSelector("[role='main'] .repository-content > .file-wrap > .files tr.js-navigation-item")
// Query matching elements
let files = try scraper.listElements(matching: repositoryContent)
print("HTMLKit repositroy root:")
files.forEach(printRepositoryFile)
} catch let error {
print(error)
}
do {
// Follow some links
try scraper.followLink(matchingSelector: anchorElement(havingContent: "Sources"))
try scraper.followLink(matchingSelector: anchorElement(havingContent: "HTMLEOFToken.m"))
// The following selector: "[role='main'] div.file table.js-file-line-container td:nth-child(2)"
// can be defined in type-safe manner:
let selector = allOf([
descendantOfElementSelector(
attributeSelector(.exactMatch, "role", "main")
),
descendantOfElementSelector(
allOf([
typeSelector("div"),
classSelector("file")
])
),
descendantOfElementSelector(
allOf([
typeSelector("table"),
classSelector("js-file-line-container")
])
),
typeSelector("td"),
nthChildSelector(
CSSNthExpressionMake(0, 2)
)
])
// Query matching elements
let elements = try scraper.listElements(matching: selector)
// This will print the source code for the "HTMLEOFToken.m" file under this url:
// https://github.com/iabudiab/HTMLKit/blob/master/Sources/HTMLEOFToken.m
print("\nHTMLEOFToken:")
elements.forEach {
print($0.textContent)
}
} catch let error {
print(error)
}
@@ -28,22 +28,22 @@ var nonParagraphChildOfDiv = document.querySelectorAll("div :not(p)")
/*:
HTMLKit also provides API to create selector instances in a type-safe manner without the need to parse them first. The previous examples would like this:
*/
paragraphs = document.elementsMatchingSelector(typeSelector("p"))
paragraphsOrHeaders = document.elementsMatchingSelector(
paragraphs = document.elements(matching: typeSelector("p"))
paragraphsOrHeaders = document.elements(matching:
anyOf([
typeSelector("p"), typeSelector("h1")
])
)
hasClassAttribute = document.elementsMatchingSelector(hasAttributeSelector("class"))
greetings = document.elementsMatchingSelector(classSelector("greeting"))
classNameStartsWith_de = document.elementsMatchingSelector(attributeSelector(.Begins, "class", "de"))
hasClassAttribute = document.elements(matching: hasAttributeSelector("class"))
greetings = document.elements(matching: classSelector("greeting"))
classNameStartsWith_de = document.elements(matching: attributeSelector(.begins, "class", "de"))
hasAdjacentHeader = document.elementsMatchingSelector(adjacentSiblingSelector(typeSelector("h1")))
hasAdjacentHeader = document.elementsMatchingSelector(generalSiblingSelector(typeSelector("h1")))
hasAdjacentHeader = document.elementsMatchingSelector(generalSiblingSelector(typeSelector("p")))
hasAdjacentHeader = document.elements(matching: adjacentSiblingSelector(typeSelector("h1")))
hasAdjacentHeader = document.elements(matching: generalSiblingSelector(typeSelector("h1")))
hasAdjacentHeader = document.elements(matching: generalSiblingSelector(typeSelector("p")))
nonParagraphChildOfDiv = document.elementsMatchingSelector(
nonParagraphChildOfDiv = document.elements(matching:
allOf([
childOfElementSelector(typeSelector("div")),
not(typeSelector("p"))
@@ -54,17 +54,17 @@ nonParagraphChildOfDiv = document.elementsMatchingSelector(
Here are more examples
*/
let firstDivElement = document.firstElementMatchingSelector(typeSelector("div"))!
let firstDivElement = document.firstElement(matching: typeSelector("div"))!
var secondChildOfDiv = firstDivElement.querySelectorAll(":nth-child(2)")
var secondOfType = firstDivElement.querySelectorAll(":nth-of-type(2n)")
secondChildOfDiv = firstDivElement.elementsMatchingSelector(nthChildSelector(CSSNthExpression(an: 0, b: 2)))
secondOfType = firstDivElement.elementsMatchingSelector(nthOfTypeSelector(CSSNthExpression(an: 2, b: 0)))
secondChildOfDiv = firstDivElement.elements(matching: nthChildSelector(CSSNthExpression(an: 0, b: 2)))
secondOfType = firstDivElement.elements(matching: nthOfTypeSelector(CSSNthExpression(an: 2, b: 0)))
var notParagraphAndNotDiv = firstDivElement.querySelectorAll(":not(p):not(div)")
notParagraphAndNotDiv = firstDivElement.elementsMatchingSelector(
notParagraphAndNotDiv = firstDivElement.elements(matching:
allOf([
not(typeSelector("p")),
not(typeSelector("div"))
@@ -77,4 +77,4 @@ One more thing! You can also create your own selectors. You either subclass the
let myAwesomeSelector = namedBlockSelector("myAwesomeSelector", { (element) -> Bool in
return element.tagName != "p" && element.tagName != "div"
})
firstDivElement.elementsMatchingSelector(myAwesomeSelector)
firstDivElement.elements(matching: myAwesomeSelector)
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Timeline
version = "3.0">
<TimelineItems>
</TimelineItems>
</Timeline>
@@ -9,7 +9,8 @@ import HTMLKit
Given some HTML content
*/
let htmlString = try! String(contentsOfURL: [#FileReference(fileReferenceLiteral: "HTMLKit.html")#])
let htmlString = "<div><h1>HTMLKit</h1><p class='greeting'>Hello there!</p><p class='description'>This is a demo of HTMLKit</p></div>"
htmlString
/*:
You can parse it using the HTMLParser:
@@ -3,10 +3,20 @@
version = "3.0">
<TimelineItems>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=1&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'/>
+2 -2
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "HTMLKit"
s.version = "0.9.4"
s.version = "2.1.0"
s.summary = "HTMLKit, an Objective-C framework for your everyday HTML needs."
s.license = "MIT"
s.homepage = "https://github.com/iabudiab/HTMLKit"
@@ -16,7 +16,7 @@ Pod::Spec.new do |s|
s.source_files = "Sources", "Sources/**/*.{h,m}"
s.private_header_files = [
'Sources/**/*{HTMLToken,HTMLTokens,HTMLTagToken,HTMLCharacterToken,HTMLCommentToken,HTMLDOCTYPEToken,HTMLEOFToken,HTMLTokenizer,HTMLTokenizerCharacters,HTMLTokenizerEntities,HTMLTokenizerStates,HTMLElementAdjustment,HTMLElementTypes,HTMLInputStreamReader,HTMLListOfActiveFormattingElements,HTMLNodeTraversal,HTMLParseErrorToken,HTMLParserInsertionModes,HTMLStackOfOpenElements,HTMLNode+Private,HTMLMarker,CSSCodePoints,CSSInputStream}.h'
'Sources/**/*{HTMLToken,HTMLTokens,HTMLTagToken,HTMLCharacterToken,HTMLCommentToken,HTMLDOCTYPEToken,HTMLEOFToken,HTMLTokenizer,HTMLTokenizerCharacters,HTMLTokenizerEntities,HTMLTokenizerStates,HTMLElementAdjustment,HTMLElementTypes,HTMLInputStreamReader,HTMLListOfActiveFormattingElements,HTMLParseErrorToken,HTMLParserInsertionModes,HTMLStackOfOpenElements,HTMLMarker,HTMLNode+Private,HTMLDocument+Private,HTMLCharacterData+Private,HTMLRange+Private,HTMLParser+Private,HTMLNodeIterator+Private,HTMLNodeTraversal,HTMLDOMUtils,CSSCodePoints,CSSInputStream}.h'
]
s.requires_arc = true
File diff suppressed because it is too large Load Diff
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
LastUpgradeVersion = "0900"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
@@ -70,6 +71,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
LastUpgradeVersion = "0900"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -16,7 +16,7 @@
BuildableIdentifier = "primary"
BlueprintIdentifier = "625A14AB19C7829400AD0C32"
BuildableName = "HTMLKit.framework"
BlueprintName = "HTMLKit-OSX"
BlueprintName = "HTMLKit-macOS"
ReferencedContainer = "container:HTMLKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
@@ -26,15 +26,17 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "625A14C219C7829400AD0C32"
BuildableName = "HTMLKitTests-OSX.xctest"
BlueprintName = "HTMLKitTests-OSX"
BuildableName = "HTMLKitTests-macOS.xctest"
BlueprintName = "HTMLKitTests-macOS"
ReferencedContainer = "container:HTMLKit.xcodeproj">
</BuildableReference>
<SkippedTests>
@@ -58,7 +60,7 @@
BuildableIdentifier = "primary"
BlueprintIdentifier = "625A14AB19C7829400AD0C32"
BuildableName = "HTMLKit.framework"
BlueprintName = "HTMLKit-OSX"
BlueprintName = "HTMLKit-macOS"
ReferencedContainer = "container:HTMLKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
@@ -69,6 +71,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
@@ -80,7 +83,7 @@
BuildableIdentifier = "primary"
BlueprintIdentifier = "625A14AB19C7829400AD0C32"
BuildableName = "HTMLKit.framework"
BlueprintName = "HTMLKit-OSX"
BlueprintName = "HTMLKit-macOS"
ReferencedContainer = "container:HTMLKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
@@ -98,7 +101,7 @@
BuildableIdentifier = "primary"
BlueprintIdentifier = "625A14AB19C7829400AD0C32"
BuildableName = "HTMLKit.framework"
BlueprintName = "HTMLKit-OSX"
BlueprintName = "HTMLKit-macOS"
ReferencedContainer = "container:HTMLKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
LastUpgradeVersion = "0900"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -26,7 +26,9 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
@@ -63,6 +65,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
LastUpgradeVersion = "0900"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -26,7 +26,9 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
</Testables>
<AdditionalOptions>
@@ -36,6 +38,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
+3
View File
@@ -7,4 +7,7 @@
<FileRef
location = "group:HTMLKit.playground">
</FileRef>
<FileRef
location = "group:Example/HTMLKitExample/HTMLKitExample.xcodeproj">
</FileRef>
</Workspace>
+2 -1
View File
@@ -1,5 +1,6 @@
import PackageDescription
let package = Package(
name: "HTMLKit"
name: "HTMLKit",
exclude: ["Tests/Fixtures", "Tests/css-tests", "Tests/html5lib-tests"]
)
+35 -17
View File
@@ -5,12 +5,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)
[![codecov](https://codecov.io/gh/iabudiab/HTMLKit/branch/master/graph/badge.svg)](https://codecov.io/gh/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 +40,7 @@ Check out the playground!
# Installation
### Carthage
## Carthage
[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
@@ -56,7 +63,7 @@ Then run the following command to build the framework and drag the built `HTMLKi
$ carthage update
```
### CocoaPods
## CocoaPods
[CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects.
@@ -72,7 +79,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', '~> 2.0'
end
```
@@ -82,14 +89,14 @@ Then, run the following command:
$ pod install
```
### Swift Package Manager
## Swift Package Manager
[Swift Package Manager](https://github.com/apple/swift-package-manager) is the package manager for the Swift programming language.
Add `HTMLKit` to your `Package.swift` dependecies:
```swift
.Package(url: "https://github.com/iabudiab/HTMLKit", majorVersion: 0, minor: 9)
.Package(url: "https://github.com/iabudiab/HTMLKit", majorVersion: 2)
```
Then run:
@@ -98,7 +105,7 @@ Then run:
$ swift build
```
### Manually
## Manually
1- Add `HTMLKit` as git submodule
@@ -110,9 +117,9 @@ $ git submodule add https://github.com/iabudiab/HTMLKit.git
3- In the General panel of your target add `HTMLKit.framework` under the `Embedded Binaries`
# Features
# Parsing
# Parsing Documents
## Parsing Documents
Given some HTML content, you can parse it either via the `HTMLParser` or instatiate a `HTMLDocument` directly:
@@ -127,7 +134,7 @@ HTMLDocument *document = [parser parseDocument];
HTMLDocument *document = [HTMLDocument documentWithString:htmlString];
```
# Parsing Fragments
## Parsing Fragments
You can also prase HTML content as a document fragment with a specified context element:
@@ -150,7 +157,7 @@ nodes = [parser parseFragmentWithContextElement:bodyContext];
# The DOM
Here are some of the things you can do:
The DOM tree can be manipulated in several ways, here are just a few:
* Create new elements and assign attributes
@@ -174,7 +181,7 @@ NSArray *nodes = @[
[body appendNodes:nodes];
```
* Enumerate child elements and perform DOM manipulation
* Enumerate child elements and perform DOM editing
```objective-c
[body enumerateChildElementsUsingBlock:^(HTMLElement *element, NSUInteger idx, BOOL *stop) {
@@ -194,6 +201,12 @@ NSArray *nodes = @[
[body.lastChild removeFromParentNode];
```
* Manipulate the HTML directly
```objective-c
greenDiv.innerHTML = @"<ul><li>item 1<li>item 2";
```
* Navigate to child and sibling nodes
```objective-c
@@ -201,12 +214,6 @@ HTMLNode *firstChild = body.firstChild;
HTMLNode *greenDiv = firstChild.nextSibling;
```
* Manipulate the HTML directly
```objective-c
greenDiv.innerHTML = @"<ul><li>item 1<li>item 2";
```
* Iterate the DOM tree with custom filters
```objective-c
@@ -222,6 +229,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:
+15 -5
View File
@@ -24,14 +24,14 @@
- (NSString *)consumeIdentifier
{
CFMutableStringRef value = CFStringCreateMutable(kCFAllocatorDefault, 0);
if (!isValidIdentifierStart([self inputCharacterPointAtOffset:0],
[self inputCharacterPointAtOffset:1],
[self inputCharacterPointAtOffset:2])) {
return nil;
}
CFMutableStringRef value = CFStringCreateMutable(kCFAllocatorDefault, 0);
while (YES) {
UTF32Char codePoint = [self consumeNextInputCharacter];
if (codePoint == EOF) {
@@ -47,7 +47,12 @@
}
}
return (__bridge NSString *)(CFStringGetLength(value) > 0 ? value : nil);
if (CFStringGetLength(value) > 0) {
return (__bridge_transfer NSString *)value;
}
CFRelease(value);
return nil;
}
- (NSString *)consumeStringWithEndingCodePoint:(UTF32Char)endingCodePoint
@@ -85,7 +90,12 @@
}
}
return (__bridge NSString *)(CFStringGetLength(value) > 0 ? value : nil);
if (CFStringGetLength(value) > 0) {
return (__bridge_transfer NSString *)value;
}
CFRelease(value);
return nil;
}
- (UTF32Char)consumeEscapedCodePoint
@@ -105,7 +115,7 @@
[self consumeNextInputCharacter];
}
NSScanner *scanner = [NSScanner scannerWithString:(__bridge NSString *)(hexString)];
NSScanner *scanner = [NSScanner scannerWithString:(__bridge_transfer NSString *)(hexString)];
unsigned int number;
[scanner scanHexInt:&number];
+114
View File
@@ -0,0 +1,114 @@
//
// HTMLCharacterData.m
// HTMLKit
//
// Created by Iska on 26/11/16.
// Copyright © 2016 BrainCookie. All rights reserved.
//
#import "HTMLCharacterData.h"
#import "HTMLNode+Private.h"
#import "HTMLDocument+Private.h"
#import "HTMLKitDOMExceptions.h"
@interface HTMLCharacterData ()
{
NSMutableString *_data;
}
@end
@implementation HTMLCharacterData
@synthesize data = _data;
- (instancetype)initWithName:(NSString *)name type:(HTMLNodeType)type data:(NSString *)data
{
self = [super initWithName:name type:type];
if (self) {
if (data) {
_data = [[NSMutableString alloc] initWithString:data];
}
}
return self;
}
- (NSString *)data
{
if (_data == nil) {
_data = [[NSMutableString alloc] initWithString:@""];
}
return _data;
}
- (NSString *)textContent
{
return [self.data copy];
}
- (void)setTextContent:(NSString *)textContent
{
[self setData:textContent];
}
- (NSUInteger)length
{
return self.data.length;
}
#pragma mark - Data
NS_INLINE void CheckValidOffset(HTMLCharacterData *node, NSUInteger offset, NSString *cmd)
{
if (offset > node.length) {
[NSException raise:HTMLKitIndexSizeError
format:@"%@: Index Size Error, invalid index %lu for character data node %@.",
cmd, (unsigned long)offset, node];
}
}
- (void)setData:(NSString *)data
{
[self replaceDataInRange:NSMakeRange(0, self.length) withData:data];
}
- (void)appendData:(NSString *)data
{
[self replaceDataInRange:NSMakeRange(self.length, 0) withData:data];
}
- (void)insertData:(NSString *)data atOffset:(NSUInteger)offset
{
[self replaceDataInRange:NSMakeRange(offset, 0) withData:data];
}
- (void)deleteDataInRange:(NSRange)range
{
[self replaceDataInRange:range withData:@""];
}
- (void)replaceDataInRange:(NSRange)range withData:(NSString *)data
{
CheckValidOffset(self, range.location, NSStringFromSelector(_cmd));
range.length = MIN(range.length, self.length - range.location);
[(NSMutableString *)self.data replaceCharactersInRange:range withString:data];
[self.ownerDocument didRemoveCharacterDataInNode:self atOffset:range.location withLength:range.length];
[self.ownerDocument didAddCharacterDataToNode:self atOffset:range.location withLength:data.length];
}
- (NSString *)substringDataWithRange:(NSRange)range
{
return [_data substringWithRange:range];
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
{
HTMLCharacterData *copy = [super copyWithZone:zone];
copy->_data = [_data mutableCopy];
return copy;
}
@end
+2 -25
View File
@@ -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;
}
+59 -10
View File
@@ -9,19 +9,19 @@
#import "HTMLDocument.h"
#import "HTMLParser.h"
#import "HTMLNodeIterator.h"
#import "HTMLRange.h"
#import "HTMLCharacterData.h"
#import "HTMLText.h"
#import "HTMLKitDOMExceptions.h"
#import "HTMLNode+Private.h"
@interface HTMLNodeIterator (Private)
- (void)runRemovingStepsForNode:(HTMLNode *)oldNode
withOldParent:(HTMLNode *)oldParent
andOldPreviousSibling:(HTMLNode *)oldPreviousSibling;
@end
#import "HTMLNodeIterator+Private.h"
#import "HTMLRange+Private.h"
@interface HTMLDocument ()
{
HTMLDocument *_inertTemplateDocument;
NSMutableArray *_nodeIterators;
NSHashTable *_nodeIterators;
NSHashTable *_ranges;
}
@property (nonatomic, assign) HTMLDocumentReadyState readyState;
@end
@@ -41,7 +41,8 @@
self = [super initWithName:@"#document" type:HTMLNodeDocument];
if (self) {
_readyState = HTMLDocumentLoading;
_nodeIterators = [NSMutableArray new];
_nodeIterators = [[NSHashTable alloc] initWithOptions:NSHashTableWeakMemory capacity:10];
_ranges = [[NSHashTable alloc] initWithOptions:NSHashTableWeakMemory capacity:10];
}
return self;
}
@@ -143,14 +144,62 @@
[_nodeIterators removeObject:iterator];
}
#pragma mark - Ranges
- (void)attachRange:(HTMLRange *)range
{
[_ranges addObject:range];
}
- (void)detachRange:(HTMLRange *)range
{
[_ranges removeObject:range];
}
- (void)didRemoveCharacterDataInNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length
{
for (HTMLRange *range in _ranges) {
[range didRemoveCharacterDataInNode:node atOffset:offset withLength:length];
}
}
- (void)didAddCharacterDataToNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length
{
for (HTMLRange *range in _ranges) {
[range didAddCharacterDataToNode:node atOffset:offset withLength:length];
}
}
- (void)didInsertNewTextNode:(HTMLText *)newNode intoParent:(HTMLNode *)parent afterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset
{
for (HTMLRange *range in _ranges) {
[range didInsertNewTextNode:newNode intoParent:parent afterSplittingTextNode:node atOffset:offset];
}
}
- (void)clampRangesAfterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset
{
for (HTMLRange *range in _ranges) {
[range clampRangesAfterSplittingTextNode:node atOffset:offset];
}
}
#pragma mark - Mutation Callback
- (void)runRemovingStepsForNode:(HTMLNode *)oldNode
withOldParent:(HTMLNode *)oldParent
andOldPreviousSibling:(HTMLNode *)oldPreviousSibling
{
for (HTMLRange *range in _ranges) {
[range runRemovingStepsForNode:oldNode
withOldParent:oldParent
andOldPreviousSibling:oldPreviousSibling];
}
for (HTMLNodeIterator *iterator in _nodeIterators) {
[iterator runRemovingStepsForNode:oldNode
withOldParent:oldParent
andOldPreviousSibling:oldPreviousSibling];
withOldParent:oldParent
andOldPreviousSibling:oldPreviousSibling];
}
}
+7 -2
View File
@@ -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;
}
+20 -10
View File
@@ -32,7 +32,7 @@
- (instancetype)initWithTagName:(NSString *)tagName
{
return [self initWithTagName:tagName attributes:@{}];
return [self initWithTagName:tagName attributes:nil];
}
- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes
@@ -45,8 +45,9 @@
self = [super initWithName:tagName type:HTMLNodeElement];
if (self) {
_tagName = [tagName copy];
_attributes = [HTMLOrderedDictionary new];
_attributes = nil;
if (attributes != nil) {
_attributes = [HTMLOrderedDictionary new];
[_attributes addEntriesFromDictionary:attributes];
}
_htmlNamespace = htmlNamespace;
@@ -56,24 +57,33 @@
#pragma mark - Special Attributes
- (NSMutableDictionary<NSString *,NSString *> *)attributes
{
if (_attributes == nil) {
_attributes = [HTMLOrderedDictionary new];
}
return _attributes;
}
- (NSString *)elementId
{
return _attributes[@"id"] ?: @"";
return self.attributes[@"id"] ?: @"";
}
- (void)setElementId:(NSString *)elementId
{
_attributes[@"id"] = elementId;
self.attributes[@"id"] = elementId;
}
- (NSString *)className
{
return _attributes[@"class"] ?: @"";
return self.attributes[@"class"] ?: @"";
}
- (void)setClassName:(NSString *)className
{
_attributes[@"class"] = className;
self.attributes[@"class"] = className;
}
- (HTMLDOMTokenList *)classList
@@ -85,22 +95,22 @@
- (BOOL)hasAttribute:(NSString *)name
{
return _attributes[name] != nil;
return self.attributes[name] != nil;
}
- (NSString *)objectForKeyedSubscript:(NSString *)name;
{
return _attributes[name];
return self.attributes[name];
}
- (void)setObject:(NSString *)value forKeyedSubscript:(NSString *)attribute
{
_attributes[attribute] = value;
self.attributes[attribute] = value;
}
- (void)removeAttribute:(NSString *)name
{
[_attributes removeObjectForKey:name];
[self.attributes removeObjectForKey:name];
}
- (NSString *)textContent
+14 -9
View File
@@ -48,10 +48,10 @@
#pragma mark - Errors
- (void)emitParseError:(NSString *)reason
- (void)emitParseError:(NSString *)code details:(NSString *)details
{
if (self.errorCallback) {
self.errorCallback(reason);
self.errorCallback(code, details);
}
}
@@ -82,16 +82,16 @@
return LINE_FEED;
}
if (CFStringIsSurrogateLowCharacter(nextInputCharacter)) {
NSString *reason = [NSString stringWithFormat:@"Non-Unicode character found (an isolated low surrogate: 0x%X)", (unsigned int)nextInputCharacter];
[self emitParseError:reason];
NSString *details = [NSString stringWithFormat:@"Non-Unicode character found (an isolated low surrogate: 0x%X)", (unsigned int)nextInputCharacter];
[self emitParseError:@"surrogate-in-input-stream" details:details];
return nextInputCharacter;
}
if (CFStringIsSurrogateHighCharacter(nextInputCharacter)) {
UniChar surrogateLow = CFStringGetCharacterFromInlineBuffer(&_buffer, _location + 1);
if (CFStringIsSurrogateLowCharacter(surrogateLow) == NO) {
NSString *reason = [NSString stringWithFormat:@"Non-Unicode character found (an isolated high surrogate: 0x%X)", (unsigned int)nextInputCharacter];
[self emitParseError:reason];
NSString *details = [NSString stringWithFormat:@"Non-Unicode character found (an isolated high surrogate: 0x%X)", (unsigned int)nextInputCharacter];
[self emitParseError:@"surrogate-in-input-stream" details:details];
return nextInputCharacter;
}
@@ -99,9 +99,14 @@
nextInputCharacter = CFStringGetLongCharacterForSurrogatePair(nextInputCharacter, surrogateLow);
}
if (isControlOrUndefinedCharacter(nextInputCharacter)) {
NSString *reason = [NSString stringWithFormat:@"A control/undefined character found: (0x%X)", (unsigned int)nextInputCharacter];
[self emitParseError:reason];
if (isControlCharacter(nextInputCharacter)) {
NSString *details = [NSString stringWithFormat:@"A control character found: (0x%X)", (unsigned int)nextInputCharacter];
[self emitParseError:@"control-character-in-input-stream" details:details];
}
if (isNoncharacter(nextInputCharacter)) {
NSString *details = [NSString stringWithFormat:@"A noncharacter found: (0x%X)", (unsigned int)nextInputCharacter];
[self emitParseError:@"noncharacter-in-input-stream" details:details];
}
return nextInputCharacter;
+1 -1
View File
@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.9.4</string>
<string>2.1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
+4
View File
@@ -14,3 +14,7 @@ NSString * const HTMLKitNotFoundError = @"NotFoundError";
NSString * const HTMLKitNotSupportedError = @"NotSupportedError";
NSString * const HTMLKitSyntaxError = @"SyntaxError";
NSString * const HTMLKitInvalidCharacterError = @"InvalidCharacterError";
NSString * const HTMLKitInvalidNodeTypeError = @"InvalidNodeTypeError";
NSString * const HTMLKitIndexSizeError = @"IndexSizeError";
NSString * const HTMLKitWrongDocumentError = @"WrongDocumentError";
NSString * const HTMLKitInvalidStateError = @"InvalidStateError";
+1 -1
View File
@@ -46,7 +46,7 @@
- (void)addElement:(HTMLElement *)element
{
NSUInteger existing = 0;
for (HTMLElement *node in _list.reverseObjectEnumerator.allObjects) {
for (HTMLElement *node in _list.reverseObjectEnumerator) {
if ([node isEqual:[HTMLMarker marker]]) {
break;
}
+117 -61
View File
@@ -16,12 +16,12 @@
#import "HTMLKitDOMExceptions.h"
#import "HTMLNodeFilter.h"
#import "CSSSelector.h"
#import "HTMLDocument+Private.h"
#import "HTMLDOMUtils.h"
@interface HTMLDocument (Private)
- (void)runRemovingStepsForNode:(HTMLNode *)oldNode
withOldParent:(HTMLNode *)oldParent
andOldPreviousSibling:(HTMLNode *)oldPreviousSibling;
@end
NSString * const ValidationNodePreInsertion = @"-ensurePreInsertionValidityOfNode:beforeChildNode:";
NSString * const ValidationNodeReplacement = @"-ensureReplacementValidityOfChildNode:withNode:";
NSString * const RemoveChildNode = @"-removeChildNode:";
@interface HTMLNode ()
{
@@ -40,13 +40,22 @@
if (self) {
_name = name;
_nodeType = type;
_childNodes = [NSMutableOrderedSet new];
_childNodes = nil;
}
return self;
}
#pragma mark - Properties
- (NSOrderedSet<HTMLNode *> *)childNodes
{
if (_childNodes == nil) {
_childNodes = [NSMutableOrderedSet new];
}
return _childNodes;
}
- (HTMLDocument *)ownerDocument
{
if (_nodeType == HTMLNodeDocument) {
@@ -59,7 +68,14 @@
- (void)setOwnerDocument:(HTMLDocument *)ownerDocument
{
_ownerDocument = ownerDocument;
[self.childNodes.array makeObjectsPerformSelector:@selector(setOwnerDocument:) withObject:ownerDocument];
for (HTMLNode *child in _childNodes) {
[child setOwnerDocument:ownerDocument];
}
}
- (HTMLNode *)rootNode
{
return _parentNode == nil ? self : _parentNode.rootNode;
}
- (void)setParentNode:(HTMLNode *)parentNode
@@ -74,12 +90,12 @@
- (HTMLNode *)firstChild
{
return self.childNodes.firstObject;
return _childNodes.firstObject;
}
- (HTMLNode *)lastChild
{
return self.childNodes.lastObject;
return _childNodes.lastObject;
}
- (HTMLNode *)previousSibling
@@ -118,11 +134,21 @@
return node.asElement;
}
- (NSUInteger)index
{
return [_parentNode indexOfChildNode:self];
}
- (NSString *)textContent
{
return nil;
}
- (NSUInteger)length
{
return self.childNodesCount;
}
#pragma mark - Cast
- (HTMLElement *)asElement
@@ -134,12 +160,16 @@
- (BOOL)hasChildNodes
{
return self.childNodes.count > 0;
return _childNodes.count > 0;
}
- (BOOL)hasChildNodeOfType:(HTMLNodeType)type
{
NSUInteger index = [self.childNodes indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
if (_childNodes == nil) {
return NO;
}
NSUInteger index = [_childNodes indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
if ([(HTMLNode *)obj nodeType] == type) {
*stop = YES;
return YES;
@@ -152,30 +182,35 @@
- (NSUInteger)childNodesCount
{
return self.childNodes.count;
return _childNodes.count;
}
- (BOOL)isEmpty
{
return self.length == 0;
}
- (HTMLNode *)childNodeAtIndex:(NSUInteger)index
{
return [self.childNodes objectAtIndex:index];
return [_childNodes objectAtIndex:index];
}
- (NSUInteger)childElementsCount
{
return [self.childNodes indexesOfObjectsPassingTest:^BOOL(HTMLNode * _Nonnull node, NSUInteger idx, BOOL * _Nonnull stop) {
return [_childNodes indexesOfObjectsPassingTest:^BOOL(HTMLNode * _Nonnull node, NSUInteger idx, BOOL * _Nonnull stop) {
return node.nodeType == HTMLNodeElement;
}].count;
}
- (NSUInteger)indexOfChildNode:(HTMLNode *)node
{
return [self.childNodes indexOfObject:node];
return [_childNodes indexOfObject:node];
}
- (HTMLElement *)childElementAtIndex:(NSUInteger)index
{
NSUInteger counter = 0;
for (HTMLNode *node in self.childNodes) {
for (HTMLNode *node in _childNodes) {
if (node.nodeType == HTMLNodeElement) {
if (counter == index) {
return node.asElement;
@@ -189,7 +224,7 @@
- (NSUInteger)indexOfChildElement:(HTMLElement *)element
{
NSUInteger counter = 0;
for (HTMLNode *node in self.childNodes) {
for (HTMLNode *node in _childNodes) {
if (node.nodeType == HTMLNodeElement) {
if (node == element) {
return counter;
@@ -246,7 +281,9 @@
[node removeAllChildNodes];
}
[nodes makeObjectsPerformSelector:@selector(setParentNode:) withObject:self];
for (HTMLNode *node in nodes) {
[node setParentNode:self];
}
return node;
}
@@ -274,7 +311,7 @@
- (void)removeFromParentNode
{
[self.parentNode removeChildNode:self];
[_parentNode removeChildNode:self];
}
- (HTMLNode *)removeChildNode:(HTMLNode *)child
@@ -282,7 +319,7 @@
if (child.parentNode != self) {
[NSException raise:HTMLKitNotFoundError
format:@"%@: Not Fount Error, removing non-child node %@. The object can not be found here.",
NSStringFromSelector(_cmd), child];
RemoveChildNode, child];
}
HTMLNode *oldNode = child;
@@ -306,16 +343,18 @@
- (void)reparentChildNodesIntoNode:(HTMLNode *)node
{
for (HTMLNode *child in self.childNodes.array) {
for (HTMLNode *child in _childNodes) {
[node appendNode:child];
}
[(NSMutableOrderedSet *)self.childNodes removeAllObjects];
[(NSMutableOrderedSet *)_childNodes removeAllObjects];
}
- (void)removeAllChildNodes
{
[self.childNodes.array makeObjectsPerformSelector:@selector(setParentNode:) withObject:nil];
[(NSMutableOrderedSet *)self.childNodes removeAllObjects];
for (HTMLNode *child in _childNodes) {
[child setParentNode:nil];
}
[(NSMutableOrderedSet *)_childNodes removeAllObjects];
}
- (HTMLDocumentPosition)compareDocumentPositionWithNode:(HTMLNode *)otherNode
@@ -328,41 +367,43 @@
return HTMLDocumentPositionEquivalent;
}
NSArray * (^ ancestorNodes) (HTMLNode *) = ^ NSArray * (HTMLNode *node) {
NSMutableArray *ancestors = [NSMutableArray array];
for (HTMLNode *node = self; node; node = node.parentNode) {
[ancestors addObject:node];
}
return ancestors;
};
NSArray *ancestors1 = ancestorNodes(self);
NSArray *ancestors2 = ancestorNodes(otherNode);
if (ancestors1.lastObject != ancestors2.lastObject) {
return HTMLDocumentPositionDisconnected |
HTMLDocumentPositionImplementationSpecific |
HTMLDocumentPositionFollowing;
if (self.ownerDocument != otherNode.ownerDocument) {
return (HTMLDocumentPositionDisconnected | HTMLDocumentPositionImplementationSpecific |
self.hash < otherNode.hash ? HTMLDocumentPositionPreceding : HTMLDocumentPositionFollowing);
}
for (NSUInteger i = MIN(ancestors1.count - 1, ancestors2.count - 1); i; --i) {
HTMLNode *child1 = ancestors1[i];
HTMLNode *child2 = ancestors2[i];
NSArray *ancestors1 = GetAncestorNodes(self);
NSArray *ancestors2 = GetAncestorNodes(otherNode);
if (ancestors1.lastObject != ancestors2.lastObject) {
return (HTMLDocumentPositionDisconnected | HTMLDocumentPositionImplementationSpecific |
self.hash < otherNode.hash ? HTMLDocumentPositionPreceding : HTMLDocumentPositionFollowing);
}
NSUInteger index1 = ancestors1.count;
NSUInteger index2 = ancestors2.count;
for (NSUInteger i = MIN(index1, index2); i; --i) {
index1 -= 1;
index2 -= 1;
HTMLNode *child1 = ancestors1[index1];
HTMLNode *child2 = ancestors2[index2];
if (child1 != child2) {
for (HTMLNode *sibling = child1.nextSibling; sibling; sibling = sibling.nextSibling) {
if (sibling == child2) {
return HTMLDocumentPositionFollowing;
return HTMLDocumentPositionPreceding;
}
}
return HTMLDocumentPositionPreceding;
return HTMLDocumentPositionFollowing;
}
}
if (ancestors1.count < ancestors2.count) {
return HTMLDocumentPositionContainedBy | HTMLDocumentPositionFollowing;
} else {
return HTMLDocumentPositionContains | HTMLDocumentPositionPreceding;
} else {
return HTMLDocumentPositionContainedBy | HTMLDocumentPositionFollowing;
}
}
@@ -384,7 +425,7 @@
return self.nodeType != HTMLNodeDocument && self.ownerDocument == otherNode;
}
for (HTMLNode *parentNode = self.parentNode; parentNode; parentNode = parentNode.parentNode) {
for (HTMLNode *parentNode = _parentNode; parentNode; parentNode = parentNode.parentNode) {
if (parentNode == otherNode) {
return YES;
}
@@ -406,7 +447,7 @@
return;
}
[self.childNodes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[_childNodes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
block(obj, idx, stop);
}];
}
@@ -417,7 +458,7 @@
return;
}
[self.childNodes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[_childNodes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[HTMLElement class]]) {
block([obj asElement], idx, stop);
}
@@ -540,18 +581,18 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin
- (void)ensurePreInsertionValidityOfNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child
{
CheckParentValid(self, NSStringFromSelector(_cmd));
CheckParentValid(self, ValidationNodePreInsertion);
CheckChildsParent(self, child, NSStringFromSelector(_cmd));
CheckChildsParent(self, child, ValidationNodePreInsertion);
CheckInsertedNodeValid(node, NSStringFromSelector(_cmd));
CheckInsertedNodeValid(node, ValidationNodePreInsertion);
CheckInvalidCombination(self, node, NSStringFromSelector(_cmd));
CheckInvalidCombination(self, node, ValidationNodePreInsertion);
void (^ hierarchyError)() = ^{
void (^ hierarchyError)(void) = ^{
[NSException raise:HTMLKitHierarchyRequestError
format:@"%@: Hierarchy Request Error, inserting (%@) into (%@). The operation would yield an incorrect node tree.",
NSStringFromSelector(_cmd), self, node];
ValidationNodePreInsertion, self, node];
};
if (self.nodeType == HTMLNodeDocument) {
@@ -590,18 +631,18 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin
- (void)ensureReplacementValidityOfChildNode:(HTMLNode *)child withNode:(HTMLNode *)node
{
CheckParentValid(self, NSStringFromSelector(_cmd));
CheckParentValid(self, ValidationNodeReplacement);
CheckChildsParent(self, child, NSStringFromSelector(_cmd));
CheckChildsParent(self, child, ValidationNodeReplacement);
CheckInsertedNodeValid(node, NSStringFromSelector(_cmd));
CheckInsertedNodeValid(node, ValidationNodeReplacement);
CheckInvalidCombination(self, node, NSStringFromSelector(_cmd));
CheckInvalidCombination(self, node, ValidationNodeReplacement);
void (^ hierarchyError)() = ^{
void (^ hierarchyError)(void) = ^{
[NSException raise:HTMLKitHierarchyRequestError
format:@"%@: Hierarchy Request Error. The operation would yield an incorrect node tree.",
NSStringFromSelector(_cmd)];
ValidationNodeReplacement];
};
void (^ checkParentHasAnotherChildOfType)(HTMLNodeType) = ^ void (HTMLNodeType type) {
@@ -650,6 +691,21 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin
#endif
#pragma mark - Clone
- (instancetype)cloneNodeDeep:(BOOL)deep
{
HTMLNode *copy = [self copy];
if (deep) {
for (HTMLNode *child in _childNodes) {
[copy appendNode:[child cloneNodeDeep:YES]];
}
}
return copy;
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
+1 -5
View File
@@ -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;
+22 -5
View File
@@ -10,29 +10,46 @@
@interface HTMLParseErrorToken ()
{
NSString *_reason;
NSString *_code;
NSString *_details;
NSUInteger _location;
}
@end
@implementation HTMLParseErrorToken
@synthesize reason = _reason;
@synthesize code = _code;
@synthesize details = _details;
@synthesize location = _location;
- (instancetype)initWithReasonMessage:(NSString *)reason andStreamLocation:(NSUInteger)location
- (instancetype)initWithCode:(NSString *)code details:(NSString *)details location:(NSUInteger)location
{
self = [super init];
if (self) {
self.type = HTMLTokenTypeParseError;
_reason = [reason copy];
_code = [code copy];
_details = [details copy];
_location = location;
}
return self;
}
- (BOOL)isEqual:(id)other
{
if ([other isKindOfClass:[self class]]) {
HTMLParseErrorToken *token = (HTMLParseErrorToken *)other;
return bothNilOrEqual(self.code, token.code);
}
return NO;
}
- (NSUInteger)hash
{
return self.code.hash + self.code.hash;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p Reason='%@' Location='%lu'>", self.class, self, _reason, (unsigned long)_location];
return [NSString stringWithFormat:@"<%@: %p Code='%@' Details='%@' Location='%lu'>", self.class, self, _code, _details, (unsigned long)_location];
}
@end
+26 -45
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 ()
{
@@ -80,6 +73,10 @@
_tokenizer = [[HTMLTokenizer alloc] initWithString:string ?: @""];
_tokenizer.parser = self;
__weak HTMLParser *weakSelf = self;
_tokenizer.parseErrorCallback = ^(HTMLParseErrorToken *token) {
[weakSelf emitParseError:@"Tokenization error: %@", token.asParseError];
};
_pendingTableCharacterTokens = [[HTMLCharacterToken alloc] initWithString:@""];
@@ -137,6 +134,10 @@
[self initializeDocument];
_tokenizer = [[HTMLTokenizer alloc] initWithString:_tokenizer.string];
_tokenizer.parser = self;
__weak HTMLParser *weakSelf = self;
_tokenizer.parseErrorCallback = ^(HTMLParseErrorToken *token) {
[weakSelf emitParseError:@"Tokenization error: %@", token.asParseError];
};
_contextElement = contextElement;
_fragmentParsingAlgorithm = YES;
@@ -233,11 +234,6 @@
return NO;
};
if (token.isParseError) {
[self emitParseError:@"Tokenizer Parser Error: %@", token.asParseError];
return;
}
if (_ignoreNextLineFeedCharacterToken) {
_ignoreNextLineFeedCharacterToken = NO;
if (token.isCharacterToken) {
@@ -457,9 +453,11 @@
beforeChildNode:&child];
if (adjustedInsertionLocation.nodeType != HTMLNodeDocument) {
if (child != nil && child.previousSibling.nodeType == HTMLNodeText) {
[(HTMLText *)child.previousSibling appendString:data];
HTMLText *textNode = (HTMLText *)child.previousSibling;
[textNode appendData:data];
} else if (adjustedInsertionLocation.lastChild.nodeType == HTMLNodeText) {
[(HTMLText *)adjustedInsertionLocation.lastChild appendString:data];
HTMLText *textNode = (HTMLText *)adjustedInsertionLocation.lastChild;
[textNode appendData:data];
} else {
HTMLText *text = [[HTMLText alloc] initWithData:data];
[adjustedInsertionLocation insertNode:text beforeChildNode:child];
@@ -519,7 +517,7 @@
- (void)generateImpliedEndTagsExceptForElement:(NSString *)tagName
{
while ([self.currentNode.tagName isEqualToAny:@"dd", @"dt", @"li", @"menuitem", @"option", @"optgroup", @"p", @"rb", @"rp", @"rt", @"rtc", nil] &&
while ([self.currentNode.tagName isEqualToAny:@"dd", @"dt", @"li", @"option", @"optgroup", @"p", @"rb", @"rp", @"rt", @"rtc", nil] &&
![self.currentNode.tagName isEqualToString:tagName]) {
[_stackOfOpenElements popCurrentNode];
}
@@ -1167,8 +1165,8 @@
[self HTMLInsertionModeInTemplate:token];
} else {
for (HTMLElement *node in _stackOfOpenElements) {
if ([node.tagName isEqualToAny:@"dd", @"dt", @"li", @"menuitem", @"optgroup", @"option", @"p", @"rb",
@"rp", @"rt", @"rtc", @"tbody", @"td", @"tfoot", @"th", @"thead", @"tr", @"body", @"html", nil]) {
if ([node.tagName isEqualToAny:@"dd", @"dt", @"li", @"optgroup", @"option", @"p", @"rb", @"rp",
@"rt", @"rtc", @"tbody", @"td", @"tfoot", @"th", @"thead", @"tr", @"body", @"html", nil]) {
[self emitParseError:@"EOF reached with unclosed element <%@> in <body>", node.tagName];
break;
}
@@ -1231,20 +1229,12 @@
[self switchInsertionMode:HTMLInsertionModeInFrameset];
} else if ([tagName isEqualToAny:@"address", @"article", @"aside", @"blockquote", @"center",
@"details", @"dialog", @"dir", @"div", @"dl", @"fieldset", @"figcaption", @"figure",
@"footer", @"header", @"hgroup", @"main", @"nav", @"ol", @"p", @"section",
@"footer", @"header", @"hgroup", @"main", @"menu", @"nav", @"ol", @"p", @"section",
@"summary", @"ul", nil]) {
if ([_stackOfOpenElements hasElementInButtonScopeWithTagName:@"p"]) {
[self closePElement];
}
[self insertElementForToken:token];
} else if ([tagName isEqualToString:@"menu"]) {
if ([_stackOfOpenElements hasElementInButtonScopeWithTagName:@"p"]) {
[self closePElement];
}
if ([self.currentNode.tagName isEqualToString:@"menuitem"]) {
[_stackOfOpenElements popCurrentNode];
}
[self insertElementForToken:token];
} else if ([tagName isEqualToAny:@"h1", @"h2", @"h3", @"h4", @"h5", @"h6", nil]) {
if ([_stackOfOpenElements hasElementInButtonScopeWithTagName:@"p"]) {
[self closePElement];
@@ -1285,7 +1275,7 @@
@"dd": @[@"dd", @"dt"],
@"dt": @[@"dd", @"dt"]};
for (HTMLElement *node in _stackOfOpenElements.reverseObjectEnumerator.allObjects) {
for (HTMLElement *node in _stackOfOpenElements.reverseObjectEnumerator) {
if ([map[tagName] containsObject:node.tagName]) {
[self generateImpliedEndTagsExceptForElement:node.tagName];
if (![self.currentNode.tagName isEqualToString:node.tagName]) {
@@ -1388,9 +1378,6 @@
if ([_stackOfOpenElements hasElementInButtonScopeWithTagName:@"p"]) {
[self closePElement];
}
if ([self.currentNode.tagName isEqualToString:@"menuitem"]) {
[_stackOfOpenElements popCurrentNode];
}
[self insertElementForToken:token];
[_stackOfOpenElements popCurrentNode];
_framesetOkFlag = NO;
@@ -1436,12 +1423,6 @@
}
[self reconstructActiveFormattingElements];
[self insertElementForToken:token];
} else if ([tagName isEqualToString:@"menuitem"]) {
if ([self.currentNode.tagName isEqualToString:@"menuitem"]) {
[_stackOfOpenElements popCurrentNode];
}
[self reconstructActiveFormattingElements];
[self insertElementForToken:token];
} else if ([tagName isEqualToAny:@"rb", @"rtc", nil]) {
if ([_stackOfOpenElements hasElementInScopeWithTagName:@"ruby"]) {
[self generateImpliedEndTagsExceptForElement:nil];
@@ -1496,8 +1477,8 @@
[self emitParseError:@"Unexpected end tag </body> without body element in scope in <body>"];
}
for (HTMLElement *node in _stackOfOpenElements) {
if ([node.tagName isEqualToAny:@"dd", @"dt", @"li", @"menuitem", @"optgroup", @"option", @"p", @"rb", @"rp",
@"rt", @"rtc", @"tbody", @"td", @"tfoot", @"th", @"thead", @"tr", @"body", @"html", nil]) {
if ([node.tagName isEqualToAny:@"dd", @"dt", @"li", @"optgroup", @"option", @"p", @"rb", @"rp", @"rt",
@"rtc", @"tbody", @"td", @"tfoot", @"th", @"thead", @"tr", @"body", @"html", nil]) {
[self emitParseError:@"Misnested end tag </%@> with open element <%@> in <body>", tagName, node.tagName];
break;
}
@@ -1551,7 +1532,7 @@
}
[self closePElement];
} else if ([tagName isEqualToString:@"li"]) {
if (![_stackOfOpenElements hasElementInListItemScopeWithTagName:@"li"]) {
if (![_stackOfOpenElements hasElementInListItemScopeWithTagName:tagName]) {
[self emitParseError:@"Unexpected <li> element in <body>"];
return;
}
@@ -1571,7 +1552,7 @@
}
[_stackOfOpenElements popElementsUntilElementPoppedWithTagName:tagName];
} else if ([tagName isEqualToAny:@"h1", @"h2", @"h3", @"h4", @"h5", @"h6", nil]) {
if (![_stackOfOpenElements hasAnyElementInScopeWithAnyOfTagNames:@[@"h1", @"h2", @"h3", @"h4", @"h5", @"h6"]]) {
if (![_stackOfOpenElements hasHeaderElementInScope]) {
[self emitParseError:@"Unexpected <%@> element in <body>", tagName];
return;
}
@@ -1591,7 +1572,7 @@
return;
}
} else if ([tagName isEqualToAny:@"applet", @"marquee", @"object", nil]) {
if (![_stackOfOpenElements hasAnyElementInScopeWithAnyOfTagNames:@[@"applet", @"marquee", @"object"]]) {
if (![_stackOfOpenElements hasElementInScopeWithTagName:tagName]) {
[self emitParseError:@"Unexpected <%@> element in <body>", tagName];
return;
}
@@ -1612,7 +1593,7 @@
- (void)processAnyOtherEndTagTokenInBody:(HTMLTagToken *)token
{
for (HTMLElement *node in _stackOfOpenElements.reverseObjectEnumerator.allObjects) {
for (HTMLElement *node in _stackOfOpenElements.reverseObjectEnumerator) {
if ([node.tagName isEqualToString:token.tagName]) {
[self generateImpliedEndTagsExceptForElement:token.tagName];
if (![node.tagName isEqualToString:self.currentNode.tagName]) {
@@ -2552,7 +2533,7 @@
return;
case HTMLTokenTypeStartTag:
{
void (^ anythingElse)() = ^ {
void (^ anythingElse)(void) = ^ {
if (self.adjustedCurrentNode.htmlNamespace == HTMLNamespaceMathML) {
AdjustMathMLAttributes(token.asTagToken);
}
@@ -2567,7 +2548,7 @@
}
};
void (^ matchedCase)() = ^ {
void (^ matchedCase)(void) = ^ {
[self emitParseError:@"Unexpected start tag <%@> in foreign content", token.asTagToken.tagName];
if (_fragmentParsingAlgorithm) {
anythingElse();
+767
View File
@@ -0,0 +1,767 @@
//
// HTMLRange.m
// HTMLKit
//
// Created by Iska on 20/11/16.
// Copyright © 2016 BrainCookie. All rights reserved.
//
#import "HTMLRange.h"
#import "HTMLDocument.h"
#import "HTMLKitDOMExceptions.h"
#import "HTMLDocument+Private.h"
#import "HTMLDOMUtils.h"
#import "HTMLNodeTraversal.h"
@interface HTMLRange ()
{
HTMLDocument *_ownerDocument;
}
@end
@implementation HTMLRange
#pragma mark - Lifecycle
- (instancetype)initWithDocument:(HTMLDocument *)document
{
return [self initWithDocument:document startContainer:document startOffset:0 endContainer:document endOffset:0];
}
- (instancetype)initWithDowcument:(HTMLDocument *)document
{
return [self initWithDocument:document startContainer:document startOffset:0 endContainer:document endOffset:0];
}
- (instancetype)initWithDowcument:(HTMLDocument *)document
startContainer:(HTMLNode *)startContainer startOffset:(NSUInteger)startOffset
endContainer:(HTMLNode *)endContainer endOffset:(NSUInteger)endOffset
{
return [self initWithDocument:document
startContainer:startContainer startOffset:startOffset
endContainer:endContainer endOffset:endOffset];
}
- (instancetype)initWithDocument:(HTMLDocument *)document
startContainer:(HTMLNode *)startContainer startOffset:(NSUInteger)startOffset
endContainer:(HTMLNode *)endContainer endOffset:(NSUInteger)endOffset
{
self = [super init];
if (self) {
_ownerDocument = document;
[_ownerDocument attachRange:self];
[self setStartNode:startContainer startOffset:startOffset];
[self setEndNode:endContainer endOffset:endOffset];
}
return self;
}
- (void)dealloc
{
[_ownerDocument detachRange:self];
}
#pragma mark - Properties
- (BOOL)isCollapsed
{
return _startContainer == _endContainer && _startOffset == _endOffset;
}
- (HTMLNode *)commonAncestorContainer
{
return GetCommonAncestorContainer(_startContainer, _endContainer);
}
- (HTMLNode *)rootNode
{
return _startContainer.rootNode;
}
#pragma mark - Boundaries
NS_INLINE void CheckValidBoundaryNode(HTMLDocument *document, HTMLNode *node, NSString *cmd)
{
if (node.ownerDocument != document) {
[NSException raise:HTMLKitWrongDocumentError
format:@"%@: Invalid Node Error, %@ is not in the same document.",
cmd, node];
}
}
NS_INLINE void CheckValidBoundaryNodeType(HTMLNode *node, NSString *cmd)
{
if (node == nil || node.nodeType == HTMLNodeDocumentType) {
[NSException raise:HTMLKitInvalidNodeTypeError
format:@"%@: Invalid Node Type Error, %@ is not a valid range boundary node.",
cmd, node];
}
}
NS_INLINE void CheckValidBoundaryOffset(HTMLNode *node, NSUInteger offset, NSString *cmd)
{
if (node.length < offset) {
[NSException raise:HTMLKitIndexSizeError
format:@"%@: Index Size Error, invalid index %lu for range boundary node %@.",
cmd, (unsigned long)offset, node];
}
}
NS_INLINE void CheckValidDocument(HTMLRange *lhs, HTMLRange *rhs, NSString *cmd)
{
if (lhs.rootNode != rhs.rootNode) {
[NSException raise:HTMLKitWrongDocumentError
format:@"%@: Wrong Document Error, ranges %@ and %@ are not in the same document.",
cmd, lhs, rhs];
}
}
NS_INLINE NSComparisonResult CompareBoundaries(HTMLNode *startNode, NSUInteger startOffset, HTMLNode *endNode, NSUInteger endOffset)
{
if (startNode == endNode) {
if (startOffset == endOffset) {
return NSOrderedSame;
} else if (startOffset < endOffset) {
return NSOrderedAscending;
} else {
return NSOrderedDescending;
}
}
HTMLDocumentPosition position = [startNode compareDocumentPositionWithNode:endNode];
if ((position & HTMLDocumentPositionFollowing) == HTMLDocumentPositionFollowing) {
if (CompareBoundaries(endNode, endOffset, startNode, startOffset) == NSOrderedAscending) {
return NSOrderedDescending;
} else {
return NSOrderedAscending;
}
}
if ((position & HTMLDocumentPositionContains) == HTMLDocumentPositionContains) {
HTMLNode *child = endNode;
while (child.parentNode != startNode) {
child = child.parentNode;
}
if (child.index < startOffset) {
return NSOrderedDescending;
}
}
return NSOrderedAscending;
}
- (void)setStartNode:(HTMLNode *)node startOffset:(NSUInteger)offset
{
CheckValidBoundaryNode(_ownerDocument, node, NSStringFromSelector(_cmd));
CheckValidBoundaryNodeType(node, NSStringFromSelector(_cmd));
CheckValidBoundaryOffset(node, offset, NSStringFromSelector(_cmd));
if (self.rootNode != node.rootNode ||
CompareBoundaries(node, offset, _endContainer, _endOffset) == NSOrderedDescending) {
_endContainer = node;
_endOffset = offset;
}
_startContainer = node;
_startOffset = offset;
}
- (void)setEndNode:(HTMLNode *)node endOffset:(NSUInteger)offset
{
CheckValidBoundaryNode(_ownerDocument, node, NSStringFromSelector(_cmd));
CheckValidBoundaryNodeType(node, NSStringFromSelector(_cmd));
CheckValidBoundaryOffset(node, offset, NSStringFromSelector(_cmd));
if (self.rootNode != node.rootNode ||
CompareBoundaries(node, offset, _startContainer, _startOffset) == NSOrderedAscending) {
_startContainer = node;
_startOffset = offset;
}
_endContainer = node;
_endOffset = offset;
}
- (void)setStartBeforeNode:(HTMLNode *)node
{
HTMLNode *parent = node.parentNode;
[self setStartNode:parent startOffset:node.index];
}
- (void)setStartAfterNode:(HTMLNode *)node
{
HTMLNode *parent = node.parentNode;
[self setStartNode:parent startOffset:node.index + 1];
}
- (void)setEndBeforeNode:(HTMLNode *)node
{
HTMLNode *parent = node.parentNode;
[self setEndNode:parent endOffset:node.index];
}
- (void)setEndAfterNode:(HTMLNode *)node
{
HTMLNode *parent = node.parentNode;
[self setEndNode:parent endOffset:node.index + 1];
}
- (void)collapseToStart
{
[self setEndNode:_startContainer endOffset:_startOffset];
}
- (void)collapseToEnd
{
[self setStartNode:_endContainer startOffset:_endOffset];
}
- (void)selectNode:(HTMLNode *)node
{
HTMLNode *parent = node.parentNode;
[self setStartNode:parent startOffset:node.index];
[self setEndNode:parent endOffset:node.index + 1];
}
- (void)selectNodeContents:(HTMLNode *)node
{
[self setStartNode:node startOffset:0];
[self setEndNode:node endOffset:node.length];
}
- (NSComparisonResult)compareBoundaryPoints:(HTMLRangeComparisonMethod)method sourceRange:(HTMLRange *)sourceRange
{
CheckValidDocument(self, sourceRange, NSStringFromSelector(_cmd));
switch (method) {
case HTMLRangeComparisonMethodStartToStart:
return CompareBoundaries(_startContainer, _startOffset, sourceRange.startContainer, sourceRange.startOffset);
case HTMLRangeComparisonMethodStartToEnd:
return CompareBoundaries(_endContainer, _endOffset, sourceRange.startContainer, sourceRange.startOffset);
case HTMLRangeComparisonMethodEndToEnd:
return CompareBoundaries(_endContainer, _endOffset, sourceRange.endContainer, sourceRange.endOffset);
case HTMLRangeComparisonMethodEndToStart:
return CompareBoundaries(_startContainer, _startOffset, sourceRange.endContainer, sourceRange.endOffset);
}
}
#pragma mark - Containment
- (NSComparisonResult)comparePoint:(HTMLNode *)node offset:(NSUInteger)offset
{
CheckValidBoundaryNode(_ownerDocument, node, NSStringFromSelector(_cmd));
CheckValidBoundaryNodeType(node, NSStringFromSelector(_cmd));
CheckValidBoundaryOffset(node, offset, NSStringFromSelector(_cmd));
if (CompareBoundaries(node, offset, _startContainer, _startOffset) == NSOrderedAscending) {
return NSOrderedAscending;
}
if (CompareBoundaries(node, offset, _endContainer, _endOffset) == NSOrderedDescending) {
return NSOrderedDescending;
}
return NSOrderedSame;
}
- (BOOL)containsPoint:(HTMLNode *)node offset:(NSUInteger)offset
{
return [self comparePoint:node offset:offset] == NSOrderedSame;
}
- (BOOL)intersectsNode:(HTMLNode *)node
{
if (self.rootNode != node.rootNode) {
return NO;
}
HTMLNode *parent = node.parentNode;
if (parent == nil) {
return YES;
}
NSUInteger offset = node.index;
if (CompareBoundaries(parent, offset, _endContainer, _endOffset) == NSOrderedAscending &&
CompareBoundaries(parent, offset + 1, _startContainer, _startOffset) == NSOrderedDescending) {
return YES;
}
return NO;
}
- (BOOL)containsNode:(HTMLNode *)node
{
return CompareBoundaries(_startContainer, _startOffset, node, 0) == NSOrderedAscending &&
CompareBoundaries(_endContainer, _endOffset, node, node.length) == NSOrderedDescending;
}
- (BOOL)partiallyContainsNode:(HTMLNode *)node
{
return [GetAncestorNodes(_startContainer) containsObject:node] || [GetAncestorNodes(_endContainer) containsObject:node];
}
- (NSArray *)containedNodes:(HTMLNode *)commonAncestor
{
NSMutableArray *containedNodes = [NSMutableArray array];
[commonAncestor.childNodes enumerateObjectsUsingBlock:^(HTMLNode * _Nonnull node, NSUInteger idx, BOOL * _Nonnull stop) {
if (node.nodeType == HTMLNodeDocumentType) {
[NSException raise:HTMLKitHierarchyRequestError format:@"Hierarchy Request Error, encountered a DOCTYPE contained in range: %@", self];
}
if ([self containsNode:node]) {
[containedNodes addObject:node];
}
}];
return containedNodes;
}
#pragma mark - Update Callbacks
- (void)didRemoveCharacterDataInNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length
{
if (_startContainer == node && _startOffset > offset) {
if (_startOffset <= offset + length) {
_startOffset = offset;
} else {
_startOffset = _startOffset - length;
}
}
if (_endContainer == node && _endOffset > offset) {
if (_endOffset <= offset + length) {
_endOffset = offset;
} else {
_endOffset = _endOffset - length;
}
}
}
- (void)didAddCharacterDataToNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length
{
if (_startContainer == node && _startOffset > offset) {
_startOffset = _startOffset + length;
}
if (_endContainer == node && _endOffset > offset) {
_endOffset = _endOffset + length;
}
}
- (void)didInsertNewTextNode:(HTMLText *)newNode intoParent:(HTMLNode *)parent afterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset
{
if (_startContainer == node && _startOffset > offset) {
_startContainer = newNode;
_startOffset -= offset;
}
if (_endContainer == node && _endOffset > offset) {
_endContainer = newNode;
_endOffset -= offset;
}
if (_startContainer == parent && _startOffset == node.index + 1) {
_startOffset += 1;
}
if (_endContainer == parent && _endOffset == node.index + 1) {
_endOffset += 1;
}
}
- (void)clampRangesAfterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset
{
if (_startContainer == node && _startOffset > offset) {
_startOffset = offset;
}
if (_endContainer == node && _endOffset > offset) {
_endOffset = offset;
}
}
- (void)runRemovingStepsForNode:(HTMLNode *)oldNode withOldParent:(HTMLNode *)oldParent andOldPreviousSibling:(HTMLNode *)oldPreviousSibling
{
NSUInteger oldIndex = oldPreviousSibling.index + 1;
if ([_startContainer containsNode:oldNode]) {
[self setStartNode:oldNode startOffset:oldIndex];
}
if ([_endContainer containsNode:oldNode]) {
[self setEndNode:oldNode endOffset:oldIndex];
}
if (_startContainer == oldParent && _startOffset > oldIndex) {
_startOffset -= 1;
}
if (_endContainer == oldParent && _endOffset > oldIndex) {
_endOffset -= 1;
}
}
#pragma mark - Mutations
NS_INLINE HTMLNode * GetHighestPartiallyContainedChild(HTMLNode *node, HTMLNode *root)
{
if (node == root) {
return nil;
}
while (node.parentNode != root) {
node = node.parentNode;
}
return node;
}
NS_INLINE HTMLCharacterData * CloneCharachterData(HTMLNode *node, NSUInteger start, NSUInteger length, BOOL delete)
{
HTMLCharacterData *clone = (HTMLCharacterData *)[node copy];
NSRange range = NSMakeRange(start, length);
[clone setData:[clone.data substringWithRange:range]];
if (delete) {
[(HTMLCharacterData *)node deleteDataInRange:range];
}
return clone;
}
- (void)deleteContents
{
if (self.isCollapsed) {
return;
}
if (_startContainer == _endContainer && [_startContainer isKindOfClass:[HTMLCharacterData class]]) {
[(HTMLCharacterData *)_startContainer deleteDataInRange:NSMakeRange(_startOffset, _endOffset - _startOffset)];
return;
}
HTMLNode *commonAncestor = self.commonAncestorContainer;
NSMutableArray *containedNodes = [NSMutableArray array];
HTMLNode *node = FollowingNode(_startContainer, commonAncestor);
while (node) {
if ([self containsNode:node]) {
[containedNodes addObject:node];
node = FollowingNodeSkippingChildren(node, commonAncestor);
} else {
node = FollowingNode(node, commonAncestor);
}
}
HTMLNode *newNode = _startContainer;
NSUInteger newOffset = _startOffset;
if (![_startContainer containsNode:_endContainer]) {
HTMLNode *referenceNode = _startContainer;
while (referenceNode.parentNode) {
if ([referenceNode.parentNode containsNode:_endContainer]) {
newNode = referenceNode.parentNode;
newOffset = referenceNode.index + 1;
break;
}
referenceNode = referenceNode.parentNode;
}
}
if ([_startContainer isKindOfClass:[HTMLCharacterData class]]) {
[(HTMLCharacterData *)_startContainer deleteDataInRange:NSMakeRange(_startOffset, _startContainer.length - _startOffset)];
}
for (HTMLNode *node in containedNodes) {
[node removeFromParentNode];
}
if ([_endContainer isKindOfClass:[HTMLCharacterData class]]) {
[(HTMLCharacterData *)_endContainer deleteDataInRange:NSMakeRange(0, _endOffset)];
}
[self setStartNode:newNode startOffset:newOffset];
[self setEndNode:newNode endOffset:newOffset];
}
- (HTMLDocumentFragment *)extractContents
{
HTMLDocumentFragment *fragment = [[HTMLDocumentFragment alloc] initWithDocument:_ownerDocument];
// Nothing todo
if (self.isCollapsed) {
return fragment;
}
// Same character data container, handle that and return
if (_startContainer == _endContainer && [_startContainer isKindOfClass:[HTMLCharacterData class]]) {
HTMLCharacterData *clone = CloneCharachterData(_startContainer, _startOffset, _endOffset - _startOffset, YES);
[fragment appendNode:clone];
return fragment;
}
HTMLNode *commonAncestor = self.commonAncestorContainer;
HTMLNode *firstPartiallyContainedChild = GetHighestPartiallyContainedChild(_startContainer, commonAncestor);
HTMLNode *lastPartiallyContainedChild = GetHighestPartiallyContainedChild(_endContainer, commonAncestor);
NSArray *containedNodes = [self containedNodes:commonAncestor];
HTMLNode *newNode = _startContainer;
NSUInteger newOffset = _startOffset;
if (![_startContainer containsNode:_endContainer]) {
HTMLNode *referenceNode = _startContainer;
while (referenceNode.parentNode) {
if ([referenceNode.parentNode containsNode:_endContainer]) {
newNode = referenceNode.parentNode;
newOffset = referenceNode.index + 1;
break;
}
referenceNode = referenceNode.parentNode;
}
}
if ([firstPartiallyContainedChild isKindOfClass:[HTMLCharacterData class]]) {
HTMLCharacterData *clone = CloneCharachterData(_startContainer, _startOffset, _startContainer.length - _startOffset, YES);
[fragment appendNode:clone];
} else if (firstPartiallyContainedChild != nil) {
HTMLNode *clone = [firstPartiallyContainedChild copy];
[fragment appendNode:clone];
HTMLRange *subRange = [[HTMLRange alloc] initWithDocument:_ownerDocument
startContainer:_startContainer
startOffset:_startOffset
endContainer:firstPartiallyContainedChild
endOffset:firstPartiallyContainedChild.length];
HTMLDocumentFragment *subFragment = [subRange extractContents];
[clone appendNode:subFragment];
}
for (HTMLNode *node in containedNodes) {
[fragment appendNode:node];
}
if ([lastPartiallyContainedChild isKindOfClass:[HTMLCharacterData class]]) {
HTMLCharacterData *clone = CloneCharachterData(_endContainer, 0, _endOffset, YES);
[fragment appendNode:clone];
} else if (lastPartiallyContainedChild != nil) {
HTMLNode *clone = [lastPartiallyContainedChild copy];
[fragment appendNode:clone];
HTMLRange *subRange = [[HTMLRange alloc] initWithDocument:_ownerDocument
startContainer:lastPartiallyContainedChild
startOffset:0
endContainer:_endContainer
endOffset:_endOffset];
HTMLDocumentFragment *subFragment = [subRange extractContents];
[clone appendNode:subFragment];
}
[self setStartNode:newNode startOffset:newOffset];
[self setEndNode:newNode endOffset:newOffset];
return fragment;
}
- (HTMLDocumentFragment *)cloneContents
{
HTMLDocumentFragment *fragment = [[HTMLDocumentFragment alloc] initWithDocument:_ownerDocument];
// Nothing todo
if (self.isCollapsed) {
return fragment;
}
// Same character data container, handle that and return
if (_startContainer == _endContainer && [_startContainer isKindOfClass:[HTMLCharacterData class]]) {
HTMLCharacterData *clone = CloneCharachterData(_startContainer, _startOffset, _endOffset - _startOffset, NO);
[fragment appendNode:clone];
return fragment;
}
HTMLNode *commonAncestor = self.commonAncestorContainer;
HTMLNode *firstPartiallyContainedChild = GetHighestPartiallyContainedChild(_startContainer, commonAncestor);
HTMLNode *lastPartiallyContainedChild = GetHighestPartiallyContainedChild(_endContainer, commonAncestor);
NSArray *containedNodes = [self containedNodes:commonAncestor];
if ([firstPartiallyContainedChild isKindOfClass:[HTMLCharacterData class]]) {
HTMLCharacterData *clone = CloneCharachterData(_startContainer, _startOffset, _startContainer.length - _startOffset, NO);
[fragment appendNode:clone];
} else if (firstPartiallyContainedChild != nil) {
HTMLNode *clone = [firstPartiallyContainedChild copy];
[fragment appendNode:clone];
HTMLRange *subRange = [[HTMLRange alloc] initWithDocument:_ownerDocument
startContainer:_startContainer
startOffset:_startOffset
endContainer:firstPartiallyContainedChild
endOffset:firstPartiallyContainedChild.length];
HTMLDocumentFragment *subFragment = [subRange cloneContents];
[clone appendNode:subFragment];
}
for (HTMLNode *node in containedNodes) {
HTMLNode *clone = [node cloneNodeDeep:YES];
[fragment appendNode:clone];
}
if ([lastPartiallyContainedChild isKindOfClass:[HTMLCharacterData class]]) {
HTMLCharacterData *clone = CloneCharachterData(_endContainer, 0, _endOffset, NO);
[fragment appendNode:clone];
} else if (lastPartiallyContainedChild != nil) {
HTMLNode *clone = [lastPartiallyContainedChild copy];
[fragment appendNode:clone];
HTMLRange *subRange = [[HTMLRange alloc] initWithDocument:_ownerDocument
startContainer:lastPartiallyContainedChild
startOffset:0
endContainer:_endContainer
endOffset:_endOffset];
HTMLDocumentFragment *subFragment = [subRange cloneContents];
[clone appendNode:subFragment];
}
return fragment;
}
#pragma mark - Insertion & Surround
NS_INLINE void CheckValidInsertionNode(HTMLNode *startContainer, HTMLNode *node, NSString *cmd)
{
if (startContainer == node || startContainer.nodeType == HTMLNodeComment ||
(startContainer.nodeType == HTMLNodeText && startContainer.parentNode == nil)) {
[NSException raise:HTMLKitHierarchyRequestError
format:@"%@: Hierarchy Request Error, cannot insert node into range: %@", cmd, node];
}
}
- (void)insertNode:(HTMLNode *)node
{
CheckValidInsertionNode(_startContainer, node, NSStringFromSelector(_cmd));
HTMLNode *referenceNode = nil;
if (_startContainer.nodeType == HTMLNodeText) {
referenceNode = _startContainer;
} else {
referenceNode = [_startContainer childNodeAtIndex:_startOffset];
}
HTMLNode *parent = _startContainer;
if (referenceNode != nil) {
parent = referenceNode.parentNode;
}
if (_startContainer.nodeType == HTMLNodeText) {
referenceNode = [(HTMLText *)_startContainer splitTextAtOffset:_startOffset];
}
if (node == referenceNode) {
referenceNode = referenceNode.nextSibling;
}
[node removeFromParentNode];
NSUInteger newOffset = referenceNode ? referenceNode.index : parent.length;
newOffset += (node.nodeType == HTMLNodeDocumentFragment) ? node.length : 1;
[parent insertNode:node beforeChildNode:referenceNode];
if (self.isCollapsed) {
[self setEndNode:parent endOffset:newOffset];
}
}
NS_INLINE void CheckValidSurroundState(HTMLRange *range, NSString *cmd)
{
for (HTMLNode *node in GetAncestorNodes(range.startContainer)) {
if ([node containsNode:range.endContainer]) {
return;
}
if (node.nodeType != HTMLNodeText) {
[NSException raise:HTMLKitInvalidStateError
format:@"%@: Invalid State Error, cannot surround range with a partially-contaied non-text node.", cmd];
}
};
for (HTMLNode *node in GetAncestorNodes(range.endContainer)) {
if ([node containsNode:range.startContainer]) {
return;
}
if (node.nodeType != HTMLNodeText) {
[NSException raise:HTMLKitInvalidNodeTypeError
format:@"%@: Invalid State Error, cannot surround range with a partially-contaied non-text node.", cmd];
}
};
}
NS_INLINE void CheckValidSurroundNodeType(HTMLNode *node, NSString *cmd)
{
if (node == nil || node.nodeType == HTMLNodeDocumentType || node.nodeType == HTMLNodeDocument ||
node.nodeType == HTMLNodeDocumentFragment) {
[NSException raise:HTMLKitInvalidNodeTypeError
format:@"%@: Invalid Node Type Error, %@ is not a valid new parent for a range.",
cmd, node];
}
}
- (void)surroundContents:(HTMLNode *)newParent
{
CheckValidSurroundState(self, NSStringFromSelector(_cmd));
CheckValidSurroundNodeType(newParent, NSStringFromSelector(_cmd));
HTMLDocumentFragment *fragment = [self extractContents];
[newParent removeAllChildNodes];
[self insertNode:newParent];
[newParent appendNode:fragment];
[self selectNode:newParent];
}
#pragma mark - Description & Stringifier
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p (%@, %lu), (%@, %lu)>", self.class, self,
_startContainer, (unsigned long)_startOffset,
_endContainer, (unsigned long)_endOffset];
}
- (NSString *)textContent
{
HTMLNode *lastNode = nil;
if ([_endContainer isKindOfClass:[HTMLCharacterData class]]) {
lastNode = FollowingNodeSkippingChildren(_endContainer, _ownerDocument);
} else if (_endContainer.childNodesCount > _endOffset) {
lastNode = [_endContainer childNodeAtIndex:_endOffset];
} else {
lastNode = FollowingNodeSkippingChildren(_endContainer, _ownerDocument);
}
NSMutableString *content = [NSMutableString string];
for (HTMLNode *node = _startContainer; node != lastNode; node = FollowingNode(node, _ownerDocument)) {
if (node.nodeType == HTMLNodeText) {
HTMLText *text = (HTMLText *)node;
if (node == _startContainer) {
NSString *string = [text substringDataWithRange:NSMakeRange(_startOffset, _startContainer.length - _startOffset)];
[content appendString:string];
} else if (node == _endContainer) {
NSString *string = [text substringDataWithRange:NSMakeRange(0, _endOffset)];
[content appendString:string];
} else {
[content appendString:text.data];
}
}
}
return content;
}
@end
+128 -83
View File
@@ -14,7 +14,6 @@
@interface HTMLStackOfOpenElements ()
{
NSMutableArray *_stack;
NSDictionary *_specificScopeElementTypes;
}
@end
@@ -27,26 +26,6 @@
self = [super init];
if (self) {
_stack = [NSMutableArray new];
_specificScopeElementTypes = @{
@"applet": @(HTMLNamespaceHTML),
@"caption": @(HTMLNamespaceHTML),
@"html": @(HTMLNamespaceHTML),
@"table": @(HTMLNamespaceHTML),
@"td": @(HTMLNamespaceHTML),
@"th": @(HTMLNamespaceHTML),
@"marquee": @(HTMLNamespaceHTML),
@"object": @(HTMLNamespaceHTML),
@"template": @(HTMLNamespaceHTML),
@"mi": @(HTMLNamespaceMathML),
@"mo": @(HTMLNamespaceMathML),
@"mn": @(HTMLNamespaceMathML),
@"ms": @(HTMLNamespaceMathML),
@"mtext": @(HTMLNamespaceMathML),
@"annotation-xml": @(HTMLNamespaceMathML),
@"foreignObject": @(HTMLNamespaceSVG),
@"desc": @(HTMLNamespaceSVG),
@"title": @(HTMLNamespaceSVG)
};
}
return self;
}
@@ -195,82 +174,148 @@
#pragma mark - Element Scope
NS_INLINE BOOL IsSpecificScopeElement(HTMLElement *element)
{
switch (element.htmlNamespace) {
case HTMLNamespaceHTML:
return [element.tagName isEqualToAny:@"applet", @"caption", @"html", @"table", @"td", @"th", @"marquee", @"object", @"template", nil];
case HTMLNamespaceMathML:
return [element.tagName isEqualToAny:@"mi", @"mo", @"mn", @"ms", @"mtext", @"annotation-xml", nil];
case HTMLNamespaceSVG:
return [element.tagName isEqualToAny:@"foreignObject", @"desc", @"title", nil];
}
}
NS_INLINE BOOL IsHeaderElement(HTMLElement *element)
{
if (element.htmlNamespace != HTMLNamespaceHTML) {
return NO;
}
return [element.tagName isEqualToAny:@"h1", @"h2", @"h3", @"h4", @"h5", @"h6", nil];
}
NS_INLINE BOOL IsTableScopeElement(HTMLElement *element)
{
if (element.htmlNamespace != HTMLNamespaceHTML) {
return NO;
}
return [element.tagName isEqualToAny:@"html", @"table", @"template", nil];
}
NS_INLINE BOOL IsListItemScopeElement(HTMLElement *element)
{
if (element.htmlNamespace != HTMLNamespaceHTML) {
return NO;
}
return [element.tagName isEqualToAny:@"ol", @"ul", nil];
}
NS_INLINE BOOL IsSelectScopeElement(HTMLElement *element)
{
if (element.htmlNamespace != HTMLNamespaceHTML) {
return NO;
}
return ![element.tagName isEqualToString:@"optgroup"] && ![element.tagName isEqualToString:@"option"];
}
NS_INLINE BOOL IsButtonScopeElement(HTMLElement *element)
{
if (element.htmlNamespace != HTMLNamespaceHTML) {
return NO;
}
return [element.tagName isEqualToString:@"button"];
}
- (HTMLElement *)hasElementInScopeWithTagName:(NSString *)tagName;
{
return [self hasAnyElementInSpecificScopeWithTagNames:@[tagName] andElementTypes:_specificScopeElementTypes];
}
- (HTMLElement *)hasAnyElementInScopeWithAnyOfTagNames:(NSArray *)tagNames
{
return [self hasAnyElementInSpecificScopeWithTagNames:tagNames andElementTypes:_specificScopeElementTypes];
}
- (HTMLElement *)hasElementInListItemScopeWithTagName:(NSString *)tagName
{
NSMutableDictionary *elementTypes = [NSMutableDictionary dictionaryWithDictionary:_specificScopeElementTypes];
[elementTypes addEntriesFromDictionary:@{@"ol": @(HTMLNamespaceHTML),
@"ul": @(HTMLNamespaceHTML)}];
return [self hasElementInSpecificScopeWithTagName:tagName
andElementTypes:elementTypes];
}
- (HTMLElement *)hasElementInButtonScopeWithTagName:(NSString *)tagName
{
NSMutableDictionary *elementTypes = [NSMutableDictionary dictionaryWithDictionary:_specificScopeElementTypes];
[elementTypes addEntriesFromDictionary:@{@"button": @(HTMLNamespaceHTML)}];
return [self hasElementInSpecificScopeWithTagName:tagName
andElementTypes:elementTypes];
}
- (HTMLElement *)hasElementInTableScopeWithTagName:(NSString *)tagName
{
return [self hasElementInSpecificScopeWithTagName:tagName
andElementTypes:@{@"html": @(HTMLNamespaceHTML),
@"table": @(HTMLNamespaceHTML),
@"template": @(HTMLNamespaceHTML)}];
}
- (HTMLElement *)hasElementInTableScopeWithAnyOfTagNames:(NSArray *)tagNames
{
return [self hasAnyElementInSpecificScopeWithTagNames:tagNames
andElementTypes:@{@"html": @(HTMLNamespaceHTML),
@"table": @(HTMLNamespaceHTML),
@"template": @(HTMLNamespaceHTML)}];
}
- (HTMLElement *)hasElementInSelectScopeWithTagName:(NSString *)tagName
{
for (HTMLElement *node in _stack.reverseObjectEnumerator) {
if ([node.tagName isEqualToString:tagName]) {
if (node.htmlNamespace == HTMLNamespaceHTML && [tagName isEqualToString:node.tagName]) {
return node;
}
if (!(node.htmlNamespace == HTMLNamespaceHTML &&
[node.tagName isEqualToAny:@"optgroup", @"option", nil])) {
if (IsSpecificScopeElement(node)) {
return nil;
}
}
return nil;
}
- (HTMLElement *)hasElementInSpecificScopeWithTagName:(NSString *)tagName
andElementTypes:(NSDictionary *)elementTypes
{
return [self hasAnyElementInSpecificScopeWithTagNames:@[tagName] andElementTypes:elementTypes];
}
- (HTMLElement *)hasAnyElementInSpecificScopeWithTagNames:(NSArray *)tagNames
andElementTypes:(NSDictionary *)elementTypes
- (HTMLElement *)hasHeaderElementInScope
{
for (HTMLElement *node in _stack.reverseObjectEnumerator) {
if ([tagNames containsObject:node.tagName]) {
NSNumber *namespace = elementTypes[node.tagName] ?: @(HTMLNamespaceHTML);
if ([namespace isEqual:@(node.htmlNamespace)]) {
return node;
}
if (IsHeaderElement(node)) {
return node;
}
if ([elementTypes[node.tagName] isEqual:@(node.htmlNamespace)]) {
if (IsSpecificScopeElement(node)) {
return nil;
}
}
return nil;
}
- (HTMLElement *)hasElementInTableScopeWithTagName:(NSString *)tagName
{
for (HTMLElement *node in _stack.reverseObjectEnumerator) {
if (node.htmlNamespace == HTMLNamespaceHTML && [tagName isEqualToString:node.tagName]) {
return node;
}
if (IsTableScopeElement(node)) {
return nil;
}
}
return nil;
}
- (HTMLElement *)hasElementInTableScopeWithAnyOfTagNames:(NSArray *)tagNames
{
for (HTMLElement *node in _stack.reverseObjectEnumerator) {
if (node.htmlNamespace == HTMLNamespaceHTML && [tagNames containsObject:node.tagName]) {
return node;
}
if (IsTableScopeElement(node)) {
return nil;
}
}
return nil;
}
- (HTMLElement *)hasElementInListItemScopeWithTagName:(NSString *)tagName
{
for (HTMLElement *node in _stack.reverseObjectEnumerator) {
if (node.htmlNamespace == HTMLNamespaceHTML && [tagName isEqualToString:node.tagName]) {
return node;
}
if (IsSpecificScopeElement(node) || IsListItemScopeElement(node)) {
return nil;
}
}
return nil;
}
- (HTMLElement *)hasElementInButtonScopeWithTagName:(NSString *)tagName
{
for (HTMLElement *node in _stack.reverseObjectEnumerator) {
if (node.htmlNamespace == HTMLNamespaceHTML && [tagName isEqualToString:node.tagName]) {
return node;
}
if (IsSpecificScopeElement(node) || IsButtonScopeElement(node)) {
return nil;
}
}
return nil;
}
- (HTMLElement *)hasElementInSelectScopeWithTagName:(NSString *)tagName
{
for (HTMLElement *node in _stack.reverseObjectEnumerator) {
if (node.htmlNamespace == HTMLNamespaceHTML && [tagName isEqualToString:node.tagName]) {
return node;
}
if (IsSelectScopeElement(node)) {
return nil;
}
}
+1 -4
View File
@@ -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
+38 -23
View File
@@ -9,7 +9,9 @@
#import "HTMLText.h"
#import "HTMLElement.h"
#import "NSString+HTMLKit.h"
#import "HTMLNode+Private.h"
#import "HTMLCharacterData+Private.h"
#import "HTMLKitDOMExceptions.h"
#import "HTMLDocument+Private.h"
@implementation HTMLText
@@ -20,35 +22,48 @@
- (instancetype)initWithData:(NSString *)data
{
self = [super initWithName:@"#text" type:HTMLNodeText];
if (self) {
_data = [[NSMutableString alloc] initWithString:data ?: @""];
}
return self;
}
- (NSString *)textContent
{
return [self.data copy];
}
- (void)setTextContent:(NSString *)textContent
{
[self.data setString:textContent ?: @""];
return [super initWithName:@"#text" type:HTMLNodeText data:data];
}
- (void)appendString:(NSString *)string
{
[self.data appendString:string];
[self appendData:string];
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
NS_INLINE void CheckValidOffset(HTMLNode *node, NSUInteger offset, NSString *cmd)
{
HTMLText *copy = [super copyWithZone:zone];
copy.data = self.data;
return copy;
if (offset > node.length) {
[NSException raise:HTMLKitIndexSizeError
format:@"%@: Index Size Error, invalid offset %lu for splitting text node %@.",
cmd, (unsigned long)offset, node];
}
}
- (HTMLText *)splitTextAtOffset:(NSUInteger)offset
{
CheckValidOffset(self, offset, NSStringFromSelector(_cmd));
NSUInteger length = self.length;
NSUInteger count = length - offset;
NSRange range = NSMakeRange(offset, count);
NSString *newData = [self.data substringWithRange:range];
HTMLText *newNode = [[HTMLText alloc] initWithData:newData];
[self.ownerDocument adoptNode:newNode];
HTMLNode *parent = self.parentNode;
if (parent != nil) {
[parent insertNode:newNode beforeChildNode:self.nextSibling];
[self.ownerDocument didInsertNewTextNode:newNode intoParent:parent afterSplittingTextNode:self atOffset:offset];
}
[self deleteDataInRange:range];
if (parent != nil) {
[self.ownerDocument clampRangesAfterSplittingTextNode:self atOffset:offset];
}
return newNode;
}
#pragma mark - Serialization
+822 -655
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -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
+9 -9
View File
@@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
@return The universal CSS selector.
*/
extern CSSSelector * universalSelector();
extern CSSSelector * universalSelector(void);
/**
CSS type selector, e.g. 'div', 'p', ...etc.
@@ -125,7 +125,7 @@ extern CSSSelector * nthLastOfTypeSelector(CSSNthExpression expression);
@return Odd-Child selector.
*/
extern CSSSelector * oddSelector();
extern CSSSelector * oddSelector(void);
/**
CSS even-child selector: ':nth-child(even)'
@@ -134,49 +134,49 @@ extern CSSSelector * oddSelector();
@return Even-Child selector.
*/
extern CSSSelector * evenSlector();
extern CSSSelector * evenSlector(void);
/**
CSS first-child selector: ':nth-child(1)'
@return First-Child selector.
*/
extern CSSSelector * firstChildSelector();
extern CSSSelector * firstChildSelector(void);
/**
CSS first-child selector: ':nth-last-child(1)'
@return First-Child selector.
*/
extern CSSSelector * lastChildSelector();
extern CSSSelector * lastChildSelector(void);
/**
CSS first-of-type selector: ':nth-first-of-type(1)'
@return First-Of-Type selector.
*/
extern CSSSelector * firstOfTypeSelector();
extern CSSSelector * firstOfTypeSelector(void);
/**
CSS last-of-type selector: ':nth-last-of-type(1)'
@return Last-Of-Type selector.
*/
extern CSSSelector * lastOfTypeSelector();
extern CSSSelector * lastOfTypeSelector(void);
/**
CSS only-child selector: ':first-child:last-child'
@return Only-Child selector.
*/
extern CSSSelector * onlyChildSelector();
extern CSSSelector * onlyChildSelector(void);
/**
CSS only-of-type selector: ':first-of-type:last-of-type'
@return Only-Of-Type selector.
*/
extern CSSSelector * onlyOfTypeSelector();
extern CSSSelector * onlyOfTypeSelector(void);
#pragma mark - Combinators
+20 -20
View File
@@ -15,102 +15,102 @@ NS_ASSUME_NONNULL_BEGIN
/**
@return Root element selector: ':root'
*/
extern CSSSelector * rootSelector();
extern CSSSelector * rootSelector(void);
/**
@return Empy element selector: ':empty'
*/
extern CSSSelector * emptySelector();
extern CSSSelector * emptySelector(void);
/**
@return A parent element selector: ':parent'
*/
extern CSSSelector * parentSelector();
extern CSSSelector * parentSelector(void);
/**
@return A button element selector: ':button'
*/
extern CSSSelector * buttonSelector();
extern CSSSelector * buttonSelector(void);
/**
@return A checkbox element selector: ':checkbox'
*/
extern CSSSelector * checkboxSelector();
extern CSSSelector * checkboxSelector(void);
/**
@return A file element selector: ':file'
*/
extern CSSSelector * fileSelector();
extern CSSSelector * fileSelector(void);
/**
@return A header element selector: ':header'
*/
extern CSSSelector * headerSelector();
extern CSSSelector * headerSelector(void);
/**
@return An image element selector: ':image'
*/
extern CSSSelector * imageSelector();
extern CSSSelector * imageSelector(void);
/**
@return A parent element selector: ':parent'
*/
extern CSSSelector * inputSelector();
extern CSSSelector * inputSelector(void);
/**
@return A link element selector: ':link'
*/
extern CSSSelector * linkSelector();
extern CSSSelector * linkSelector(void);
/**
@return A password element selector: ':password'
*/
extern CSSSelector * passwordSelector();
extern CSSSelector * passwordSelector(void);
/**
@return A radio element selector: ':radio'
*/
extern CSSSelector * radioSelector();
extern CSSSelector * radioSelector(void);
/**
@return A reset element selector: ':reset'
*/
extern CSSSelector * resetSelector();
extern CSSSelector * resetSelector(void);
/**
@return A submit element selector: ':submit'
*/
extern CSSSelector * submitSelector();
extern CSSSelector * submitSelector(void);
/**
@return A text element selector: ':text'
*/
extern CSSSelector * textSelector();
extern CSSSelector * textSelector(void);
/**
@return An enabled element selector: ':enabled'
*/
extern CSSSelector * enabledSelector();
extern CSSSelector * enabledSelector(void);
/**
@return A disabled element selector: ':disabled'
*/
extern CSSSelector * disabledSelector();
extern CSSSelector * disabledSelector(void);
/**
@return A checked element selector: ':checked'
*/
extern CSSSelector * checkedSelector();
extern CSSSelector * checkedSelector(void);
/**
@return An optional element selector: ':optional'
*/
extern CSSSelector * optionalSelector();
extern CSSSelector * optionalSelector(void);
/**
@return A required element selector: ':required'
*/
extern CSSSelector * requiredSelector();
extern CSSSelector * requiredSelector(void);
/**
Less-than selector, e.g. 'lt(2)'
@@ -0,0 +1,28 @@
//
// HTMLCharacterData+Private.h
// HTMLKit
//
// Created by Iska on 26/11/16.
// Copyright © 2016 BrainCookie. All rights reserved.
//
#import "HTMLCharacterData.h"
#import "HTMLNode.h"
/**
Private HTML Character Data methods which are not intended for public API.
*/
@interface HTMLCharacterData ()
/**
Designated initializer of the HTML CharacterData, which, however, should not be used directly. It is intended to be
called only by subclasses, i.e. HTMLText and HTMLComment.
@param name The node's name.
@param type The node's type.
@param data The node's data string.
@return A new instance of a HTML CharacterData.
*/
- (instancetype)initWithName:(NSString *)name type:(HTMLNodeType)type data:(NSString *)data NS_DESIGNATED_INITIALIZER;
@end
+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
+2 -5
View File
@@ -6,17 +6,14 @@
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import "HTMLNode.h"
#import "HTMLCharacterData.h"
NS_ASSUME_NONNULL_BEGIN
/**
A HTML Comment node
*/
@interface HTMLComment : HTMLNode
/** @brief The comment string. */
@property (nonatomic, copy) NSString *data;
@interface HTMLComment : HTMLCharacterData
/**
Initializes a new HTML comment node.
+2
View File
@@ -11,9 +11,11 @@
#import "HTMLDocumentType.h"
#import "HTMLDocumentFragment.h"
#import "HTMLElement.h"
#import "HTMLCharacterData.h"
#import "HTMLComment.h"
#import "HTMLText.h"
#import "HTMLTemplate.h"
#import "HTMLRange.h"
#import "HTMLDOMTokenList.h"
#import "HTMLNodeIterator.h"
#import "HTMLTreeWalker.h"
+15
View File
@@ -0,0 +1,15 @@
//
// HTMLDOMUtils.h
// HTMLKit
//
// Created by Iska on 03/12/16.
// Copyright © 2016 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "HTMLDOM.h"
@class HTMLNode;
extern HTMLNode * GetCommonAncestorContainer(HTMLNode *nodeA, HTMLNode *nodeB);
extern NSArray<HTMLNode *> * GetAncestorNodes(HTMLNode *node);
+99
View File
@@ -0,0 +1,99 @@
//
// HTMLDocument+Private.h
// HTMLKit
//
// Created by Iska on 27/11/16.
// Copyright © 2016 BrainCookie. All rights reserved.
//
#import "HTMLDocument.h"
#import "HTMLNode.h"
#import "HTMLCharacterData.h"
#import "HTMLNodeIterator.h"
#import "HTMLRange.h"
#import "HTMLText.h"
/**
Private HTML Document methods which are not intended for public API.
*/
@interface HTMLDocument ()
@property (nonatomic, assign) HTMLDocumentReadyState readyState;
/**
Runs the necessary steps after removing a node from the DOM.
@param oldNode The old node that was removed.
@param oldParent The old parent of the node that was removed.
@param oldPreviousSibling The old previous sibling node of the node that was removed.
*/
- (void)runRemovingStepsForNode:(HTMLNode *)oldNode
withOldParent:(HTMLNode *)oldParent
andOldPreviousSibling:(HTMLNode *)oldPreviousSibling;
/**
Attaches a node iterator to this document.
@param iterator The iterator to attach.
*/
- (void)attachNodeIterator:(HTMLNodeIterator *)iterator;
/**
Detaches a node interator from this document.
@param iterator The iterator to detach.
*/
- (void)detachNodeIterator:(HTMLNodeIterator *)iterator;
/**
Attaches a range to this document.
@param range The range to attach.
*/
- (void)attachRange:(HTMLRange *)range;
/**
Detaches a range from this document.
@param range The range to detach.
*/
- (void)detachRange:(HTMLRange *)range;
/**
Callback on removing text from a CharacterData node.
@param node The CharacterData node.
@param offset The offset at which the data was removed.
@param length The length of the data that was removed.
*/
- (void)didRemoveCharacterDataInNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length;
/**
Callback on adding text from a CharacterData node.
@param node The CharacterData node.
@param offset The offset at which the data was added.
@param length The length of the data that was added.
*/
- (void)didAddCharacterDataToNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length;
/**
Callback on inserting a new text node when an old text node is split.
@param newNode The new text node after splitting.
@param parent The parent where newNode was inserted.
@param node The old text node that was split.
@param offset The offset of splitting.
*/
- (void)didInsertNewTextNode:(HTMLText *)newNode intoParent:(HTMLNode *)parent afterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset;
/**
Callback for clamping current ranges whose end boundary is after the text node upon splitting it.
@param node The text node that was split.
@param offset The offset of splitting
*/
- (void)clampRangesAfterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset;
@end
+3 -3
View File
@@ -53,7 +53,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
The element's attribites.
*/
@property (nonatomic, strong) NSMutableDictionary *attributes;
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSString *> *attributes;
/**
@warning Use one of the initWithTagName: methods instead.
@@ -75,7 +75,7 @@ NS_ASSUME_NONNULL_BEGIN
@param attributes The attributes.
@return A new HTML element.
*/
- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes;
- (instancetype)initWithTagName:(NSString *)tagName attributes:(nullable NSDictionary<NSString *, NSString *> *)attributes;
/**
Initializes a new HTML element with the given tag name, namespace, and attributes.
@@ -85,7 +85,7 @@ NS_ASSUME_NONNULL_BEGIN
@param attributes The attributes.
@return A new HTML element.
*/
- (instancetype)initWithTagName:(NSString *)tagName namespace:(HTMLNamespace)htmlNamespace attributes:(NSDictionary *)attributes;
- (instancetype)initWithTagName:(NSString *)tagName namespace:(HTMLNamespace)htmlNamespace attributes:(nullable NSDictionary<NSString *, NSString *> *)attributes;
/**
Checks whether this element has an attribute with the given name.
+1 -1
View File
@@ -17,7 +17,7 @@
@param reason The string describing the reason of the reported error.
*/
typedef void (^ HTMLStreamReaderErrorCallback)(NSString *reason);
typedef void (^ HTMLStreamReaderErrorCallback)(NSString *code, NSString *details);
/**
* HTML Input Stream Reader processor conforming to the HTML standard
+5
View File
@@ -14,3 +14,8 @@ extern NSString * const HTMLKitNotSupportedError;
extern NSString * const HTMLKitSyntaxError;
extern NSString * const HTMLKitInvalidCharacterError;
extern NSString * const HTMLKitInvalidNodeTypeError;
extern NSString * const HTMLKitIndexSizeError;
extern NSString * const HTMLKitWrongDocumentError;
extern NSString * const HTMLKitInvalidStateError;
+36 -5
View File
@@ -28,7 +28,7 @@ typedef NS_ENUM(short, HTMLNodeType)
/**
A node's position in the HTML document when compared with other nodes.
*/
typedef NS_ENUM(unsigned short, HTMLDocumentPosition)
typedef NS_OPTIONS(unsigned short, HTMLDocumentPosition)
{
HTMLDocumentPositionEquivalent = 0x0,
HTMLDocumentPositionDisconnected = 0x01,
@@ -71,6 +71,11 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition)
*/
@property (nonatomic, weak, readonly, nullable) HTMLDocument *ownerDocument;
/**
The root node of this node, if any.
*/
@property (nonatomic, weak, readonly, nullable) HTMLNode *rootNode;
/**
The parent node of this node, if any.
*/
@@ -86,7 +91,7 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition)
/**
A read-only ordered set of child nodes.
*/
@property (nonatomic, strong, readonly) NSOrderedSet *childNodes;
@property (nonatomic, strong, readonly) NSOrderedSet<HTMLNode *> *childNodes;
/**
The first child node, if any.
@@ -122,6 +127,11 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition)
*/
@property (nonatomic, strong, readonly, nullable) HTMLElement *nextSiblingElement;
/**
The index of this node.
*/
@property (nonatomic, readonly, assign) NSUInteger index;
/**
The text content of this node.
*/
@@ -137,6 +147,11 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition)
*/
@property (nonatomic, copy) NSString *innerHTML;
/**
The length of the node as described in https://dom.spec.whatwg.org/#concept-node-length
*/
@property (nonatomic, assign) NSUInteger length;
/**
@abstract Use concrete subclasses of the HTML Node.
*/
@@ -164,6 +179,21 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition)
*/
- (NSUInteger)childNodesCount;
/**
Checks whether the node is empty as described in https://dom.spec.whatwg.org/#concept-node-length
@return `YES` if the node is empty, `NO` otherwise.
*/
- (BOOL)isEmpty;
/**
Clones this node.
@param deep If `YES` then also clones child nodes. Otherwise a shallow clone is returned, which behaves the same as `copy`.
@return A clone of this node.
*/
- (instancetype)cloneNodeDeep:(BOOL)deep;
/**
Returns the child node at a given index.
@@ -220,7 +250,7 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition)
@param node The nodes to prepend.
*/
- (void)prependNodes:(NSArray *)nodes;
- (void)prependNodes:(NSArray<HTMLNode *> *)nodes;
/**
Appends the given node to the set of child nodes.
@@ -235,7 +265,7 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition)
@param nodes The nodes to append.
*/
- (void)appendNodes:(NSArray *)nodes;
- (void)appendNodes:(NSArray<HTMLNode *> *)nodes;
/**
Inserts a given node before a child node.
@@ -318,7 +348,8 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition)
- (BOOL)isDescendantOfNode:(HTMLNode *)node;
/**
Checks whether this node contains the given node.
Checks whether this node contains the given node. This performs an `invlusive ancestor` check, i.e. it returns `YES`
if the given node is the same object as this node.
@param node The node to check.
@return `YES` if this node contains the given node, `NO` otherwsie.
@@ -0,0 +1,28 @@
//
// HTMLNodeIterator+Private.h
// HTMLKit
//
// Created by Iska on 27/11/16.
// Copyright © 2016 BrainCookie. All rights reserved.
//
#import "HTMLNodeIterator.h"
#import "HTMLNode.h"
/**
Private HTML Node Iterator methods which are not intended for public API.
*/
@interface HTMLNodeIterator (Private)
/**
Runs the necessary steps after removing a node from the DOM.
@param oldNode The old node that was removed.
@param oldParent The old parent of the node that was removed.
@param oldPreviousSibling The old previous sibling node of the node that was removed.
*/
- (void)runRemovingStepsForNode:(HTMLNode *)oldNode
withOldParent:(HTMLNode *)oldParent
andOldPreviousSibling:(HTMLNode *)oldPreviousSibling;
@end
+8 -5
View File
@@ -18,19 +18,22 @@
*/
@interface HTMLParseErrorToken : HTMLToken
/** @brief The error's reason message. */
@property (nonatomic, copy) NSString *reason;
/** @brief The parse error's code as specified at https://html.spec.whatwg.org/multipage/parsing.html#parse-errors. */
@property (nonatomic, strong, readonly) NSString *code;
/** @brief Additional detailed error information. */
@property (nonatomic, strong, readonly) NSString *details;
/** @brief The error's location in the stream. */
@property (nonatomic, assign) NSUInteger location;
@property (nonatomic, assign, readonly) NSUInteger location;
/**
Initializes a new Parse Error token.
@param reason The error's reason message.
@param code The parse error's as specified at https://html.spec.whatwg.org/multipage/parsing.html#parse-errors.
@param location The error's location in the stream.
@return A new instance of a parse error token.
*/
- (instancetype)initWithReasonMessage:(NSString *)reason andStreamLocation:(NSUInteger)location;
- (instancetype)initWithCode:(NSString *)code details:(NSString *)details location:(NSUInteger)location;
@end
+23
View File
@@ -0,0 +1,23 @@
//
// HTLMLParser+Private.h
// HTMLKit
//
// Created by Iska on 27/11/16.
// Copyright © 2016 BrainCookie. All rights reserved.
//
#import "HTMLParser.h"
#import "HTMLElement.h"
/**
Private HTML Parser properties & methods which are not intended for public API.
*/
@interface HTMLParser (Private)
/**
The adjusted current node in the context of HTML parsing as described in:
https://html.spec.whatwg.org/#adjusted-current-node
*/
@property (nonatomic, strong, readonly) HTMLElement *adjustedCurrentNode;
@end
+2 -2
View File
@@ -24,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
An array of errors that occurred during document parsing.
*/
@property (nonatomic, strong, readonly) NSArray *parseErrors;
@property (nonatomic, strong, readonly) NSArray<NSString *> *parseErrors;
/**
The parsed HTML Document.
@@ -67,7 +67,7 @@ NS_ASSUME_NONNULL_BEGIN
@see HTMLElement
*/
- (NSArray *)parseFragmentWithContextElement:(HTMLElement *)contextElement;
- (NSArray<HTMLNode *> *)parseFragmentWithContextElement:(HTMLElement *)contextElement;
@end
+61
View File
@@ -0,0 +1,61 @@
//
// HTMLRange+Private.h
// HTMLKit
//
// Created by Iska on 27/11/16.
// Copyright © 2016 BrainCookie. All rights reserved.
//
#import "HTMLRange.h"
#import "HTMLCharacterData.h"
@interface HTMLRange ()
/**
Runs the necessary steps after removing data from a character data node that may be a range's boundary.
@param node The character data node.
@param offset The offset at which the data was removed.
@param length The length of the data that was removed.
*/
- (void)didRemoveCharacterDataInNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length;
/**
Runs the necessary steps after adding data to a character data node that may be a range's boundary.
@param node The character data node.
@param offset The offset at which the data was added.
@param length The length of the data that was added.
*/
- (void)didAddCharacterDataToNode:(HTMLCharacterData *)node atOffset:(NSUInteger)offset withLength:(NSUInteger)length;
/**
Runs the necessary steps after inserting a new text node when an old text node is split.
@param newNode The new text node after splitting.
@param parent The parent where newNode was inserted.
@param node The old text node that was split.
@param offset The offset of splitting.
*/
- (void)didInsertNewTextNode:(HTMLText *)newNode intoParent:(HTMLNode *)parent afterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset;
/**
Runs the necessary steps to clamp the range whose end boundary is after the text node upon splitting it.
@param node The text node that was split.
@param offset The offset of splitting
*/
- (void)clampRangesAfterSplittingTextNode:(HTMLText *)node atOffset:(NSUInteger)offset;
/**
Runs the necessary steps after removing a node from the DOM.
@param oldNode The old node that was removed.
@param oldParent The old parent of the node that was removed.
@param oldPreviousSibling The old previous sibling node of the node that was removed.
*/
- (void)runRemovingStepsForNode:(HTMLNode *)oldNode
withOldParent:(HTMLNode *)oldParent
andOldPreviousSibling:(HTMLNode *)oldPreviousSibling;
@end
+265
View File
@@ -0,0 +1,265 @@
//
// HTMLRange.h
// HTMLKit
//
// Created by Iska on 20/11/16.
// Copyright © 2016 BrainCookie. All rights reserved.
//
#import "HTMLNode.h"
#import "HTMLDocumentFragment.h"
NS_ASSUME_NONNULL_BEGIN
/**
The comparison method of range boundaries
- HTMLRangeComparisonMethodStartToStart: Compares the start boundary-point of sourceRange to the start boundary-point of this range.
- HTMLRangeComparisonMethodStartToEnd: Compares the start boundary-point of sourceRange to the end boundary-point of this range.
- HTMLRangeComparisonMethodEndToEnd: Compares the end boundary-point of sourceRange to the end boundary-point of this range.
- HTMLRangeComparisonMethodEndToStart: Compares the end boundary-point of sourceRange to the start boundary-point of this range.
*/
typedef NS_ENUM(unsigned short, HTMLRangeComparisonMethod)
{
HTMLRangeComparisonMethodStartToStart = 0,
HTMLRangeComparisonMethodStartToEnd = 1,
HTMLRangeComparisonMethodEndToEnd = 2,
HTMLRangeComparisonMethodEndToStart = 3
};
#pragma mark - DOM Range
/**
A HTML Range, represents a sequence of content within a node tree.
Each range has a start and an end which are boundary points.
A boundary point is a tuple consisting of a node and a non-negative numeric offset.
https://dom.spec.whatwg.org/#ranges
*/
@interface HTMLRange : NSObject
/**
The node of the start boundary point.
*/
@property (nonatomic, readonly, strong) HTMLNode *startContainer;
/**
The offset of the start boundary point.
*/
@property (nonatomic, readonly, assign) NSUInteger startOffset;
/**
The node of the end boundary point.
*/
@property (nonatomic, readonly, strong) HTMLNode *endContainer;
/**
The offset of the end boundary point.
*/
@property (nonatomic, readonly, assign) NSUInteger endOffset;
/**
Checks whether the range is collapsed, i.e. if start is the same as end.
@return `YES` if the range is collapsed, `NO` otherwise.
*/
@property (nonatomic, readonly, assign, getter=isCollapsed) BOOL collapsed;
/**
The common container node that contains both start and end nodes.
*/
@property (nonatomic, readonly, weak) HTMLNode *commonAncestorContainer;
/**
@abstract A range is always associated with a HTML Document. Use `initWithDocument:` initializer instead.
*/
- (instancetype)init NS_UNAVAILABLE;
/**
Initializes a new range instance for the given document.
@param document The HTML doucment for which the range will be constructed.
@return A new HTML Range instance.
*/
- (instancetype)initWithDocument:(HTMLDocument *)document;
/**
Deprecated due to typo.
*/
- (instancetype)initWithDowcument:(HTMLDocument *)document __attribute__((deprecated("Replaced by -initWithDocument:")));
/**
Initializes a new range instance for the given document and boundaries.
@param document The HTML doucment for which the range will be constructed.
@param startContainer The node for the start boundary
@param startOffset The offset of the start boundary
@param endContainer The node for the end boundary
@param endOffset The offset of the end boundary
@return A new HTML Range instance.
*/
- (instancetype)initWithDocument:(HTMLDocument *)document
startContainer:(HTMLNode *)startContainer startOffset:(NSUInteger)startOffset
endContainer:(HTMLNode *)endContainer endOffset:(NSUInteger)endOffset;
/**
Deprecated due to typo.
*/
- (instancetype)initWithDowcument:(HTMLDocument *)document
startContainer:(HTMLNode *)startContainer startOffset:(NSUInteger)startOffset
endContainer:(HTMLNode *)endContainer endOffset:(NSUInteger)endOffset
__attribute__((deprecated("Replaced by -initWithDocument:startContainer:startOffset:endContainer:endOffset:")));
/**
Sets the start boundary.
@param startNode The new node of the start boundary.
@param startOffset The new offset of the start boundary.
*/
- (void)setStartNode:(HTMLNode *)node startOffset:(NSUInteger)offset;
/**
Sets the end boundary.
@param startNode The new node of the end boundary.
@param startOffset The new offset of the end boundary.
*/
- (void)setEndNode:(HTMLNode *)node endOffset:(NSUInteger)offset;
/**
Sets the start boundary before the given node.
@param node The node before which the boundary will be set.
*/
- (void)setStartBeforeNode:(HTMLNode *)node;
/**
Sets the start boundary after the given node.
@param node The node after which the boundary will be set.
*/
- (void)setStartAfterNode:(HTMLNode *)node;
/**
Sets the end boundary before the given node.
@param node The node before which the boundary will be set.
*/
- (void)setEndBeforeNode:(HTMLNode *)node;
/**
Sets the end boundary after the given node.
@param node The node after which the boundary will be set.
*/
- (void)setEndAfterNode:(HTMLNode *)node;
/**
Collapses this range to its start.
*/
- (void)collapseToStart;
/**
Collapses this range to its end.
*/
- (void)collapseToEnd;
/**
Selects the given node in the range.
@param node The node to select
*/
- (void)selectNode:(HTMLNode *)node;
/**
Selects the node's contents in the range.
@param node The node to select the contents.
*/
- (void)selectNodeContents:(HTMLNode *)node;
/**
Compares the boundary points of the given range with this range.
@param method The comparison method.
@param sourceRange The source range for comparison.
@return `NSOrderedAscending` if ordered before, `NSOrderedSame` if ordered same, `NSOrderedDescending` otherwise.
@see HTMLRangeComparisonMethod
*/
- (NSComparisonResult)compareBoundaryPoints:(HTMLRangeComparisonMethod)method sourceRange:(HTMLRange *)sourceRange;
/**
Compares the given point (reference node, offset) with this range.
@param node The node to compare with this range.
@param offset The offset inside the reference node.
@return `NSOrderedAscending`, `NSOrderedSame`, or `NSOrderedDescending` depending on whether the node is before, the same as, or after this range.
*/
- (NSComparisonResult)comparePoint:(HTMLNode *)node offset:(NSUInteger)offset;
/**
Checks if the given point (reference node, offset) is in this range.
@param node The node to compare with this range.
@param offset The offset inside the reference node.
@return `YES` if the given point is in this range, `NO` otherwise.
*/
- (BOOL)containsPoint:(HTMLNode *)node offset:(NSUInteger)offset;
/**
Checks if the given node intersects this range.
@param node The node to compare with this range.
@return `YES` if the given node intersects the range, `NO` otherwise.
*/
- (BOOL)intersectsNode:(HTMLNode *)node;
/**
Deletes the contents represented by this range from the associated document.
*/
- (void)deleteContents;
/**
Extracts the contents represented by this range from the associated document.
@return A document fragment with the extracted contents.
*/
- (HTMLDocumentFragment *)extractContents;
/**
Clones the contents represented by this range in the associated document.
@return A document fragment with the cloned contents.
*/
- (HTMLDocumentFragment *)cloneContents;
/**
Inserts the given node at the start of this range.
If the node is being added to a text node, then the text node is split at the insertion point and the given node
is inserted between the resulting text nodes.
@param node The node to insert.
*/
- (void)insertNode:(HTMLNode *)node;
/**
Surrounds the contents of this range with the given node.
The range's boundaries will placed around the given node, i.e. start is before and end is after newParent.
@param newParent The new parent node which will surround the range.
*/
- (void)surroundContents:(HTMLNode *)newParent;
/**
The stringifier of the range.
@return The text contents of the range.
*/
- (NSString *)textContent;
@end
NS_ASSUME_NONNULL_END
+3 -3
View File
@@ -163,11 +163,11 @@
https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-the-specific-scope
*/
- (HTMLElement *)hasElementInScopeWithTagName:(NSString *)tagName;
- (HTMLElement *)hasAnyElementInScopeWithAnyOfTagNames:(NSArray *)tagNames;
- (HTMLElement *)hasElementInListItemScopeWithTagName:(NSString *)tagName;
- (HTMLElement *)hasElementInButtonScopeWithTagName:(NSString *)tagName;
- (HTMLElement *)hasHeaderElementInScope;
- (HTMLElement *)hasElementInTableScopeWithTagName:(NSString *)tagName;
- (HTMLElement *)hasElementInTableScopeWithAnyOfTagNames:(NSArray *)tagNames;
- (HTMLElement *)hasElementInListItemScopeWithTagName:(NSString *)tagName;
- (HTMLElement *)hasElementInButtonScopeWithTagName:(NSString *)tagName;
- (HTMLElement *)hasElementInSelectScopeWithTagName:(NSString *)tagName;
/**
+5 -6
View File
@@ -6,17 +6,14 @@
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import "HTMLNode.h"
#import "HTMLCharacterData.h"
NS_ASSUME_NONNULL_BEGIN
/**
A HTML Text node
*/
@interface HTMLText : HTMLNode
/** @brief The text string. */
@property (nonatomic, copy) NSMutableString *data;
@interface HTMLText : HTMLCharacterData
/**
Initializes a new HTML text node.
@@ -31,7 +28,9 @@ NS_ASSUME_NONNULL_BEGIN
@param string The string to append.
*/
- (void)appendString:(NSString *)string;
- (void)appendString:(NSString *)string __attribute__((deprecated("Use `appendData:` instead.")));
- (HTMLText *)splitTextAtOffset:(NSUInteger)offset;
@end
+15 -1
View File
@@ -16,6 +16,13 @@
@class HTMLParser;
/**
Typedef for the parse error callback block.
@param token The parse error token.
*/
typedef void (^ HTMLTokenizerParseErrorCallback)(HTMLParseErrorToken *token);
/**
* HTML Tokenizer
* https://html.spec.whatwg.org/multipage/syntax.html#tokenization
@@ -37,7 +44,14 @@
@see HTMLParser
*/
@property (nonatomic, weak, readonly) HTMLParser *parser;
@property (nonatomic, weak) HTMLParser *parser;
/**
An error callback block, which gets called when encountering parse errors while tokenizing the stream
Parse error tokens are dropped if the callback is `nil`.
*/
@property (nonatomic, copy) HTMLTokenizerParseErrorCallback parseErrorCallback;
/**
Initializes a new Tokenizer with the given string.
+28 -10
View File
@@ -23,11 +23,16 @@
CHAR( DIGIT_NINE, 0x0039 ) \
CHAR( LATIN_CAPITAL_LETTER_A, 0x0041 ) \
CHAR( LATIN_CAPITAL_LETTER_F, 0x0046 ) \
CHAR( LATIN_CAPITAL_LETTER_P, 0x0050 ) \
CHAR( LATIN_CAPITAL_LETTER_S, 0x0053 ) \
CHAR( LATIN_CAPITAL_LETTER_X, 0x0058 ) \
CHAR( LATIN_CAPITAL_LETTER_Z, 0x005A ) \
CHAR( RIGHT_SQUARE_BRACKET, 0x005D ) \
CHAR( GRAVE_ACCENT, 0x0060 ) \
CHAR( LATIN_SMALL_LETTER_A, 0x0061 ) \
CHAR( LATIN_SMALL_LETTER_F, 0x0066 ) \
CHAR( LATIN_SMALL_LETTER_P, 0x0070 ) \
CHAR( LATIN_SMALL_LETTER_S, 0x0073 ) \
CHAR( LATIN_SMALL_LETTER_X, 0x0078 ) \
CHAR( LATIN_SMALL_LETTER_Z, 0x007A ) \
CHAR( HYPHEN_MINUS, 0x002D ) \
@@ -82,13 +87,17 @@ NUMERIC_REPLACEMENT_CHARACTERS
#undef CHAR
};
NS_INLINE BOOL isControlOrUndefinedCharacter(UTF32Char character)
NS_INLINE BOOL isControlCharacter(unsigned long long character)
{
return ((character >= 0x0001 && character <= 0x0008) ||
(character >= 0x000D && character <= 0x001F) ||
(character >= 0x007F && character <= 0x009F) ||
(character >= 0xFDD0 && character <= 0xFDEF) ||
character == 0x000B ||
(character >= 0x000E && character <= 0x001F) ||
(character >= 0x007F && character <= 0x009F));
}
NS_INLINE BOOL isNoncharacter(unsigned long long character)
{
return ((character >= 0xFDD0 && character <= 0xFDEF) ||
character == 0xFFFE ||
character == 0xFFFF ||
character == 0x1FFFE ||
@@ -137,6 +146,18 @@ NS_INLINE BOOL isHexDigit(UTF32Char character)
(character >= LATIN_SMALL_LETTER_A && character <= LATIN_SMALL_LETTER_F));
}
NS_INLINE BOOL isUpperHexDigit(UTF32Char character)
{
return ((character >= LATIN_CAPITAL_LETTER_A && character <= LATIN_CAPITAL_LETTER_F) ||
(character >= DIGIT_ZERO && character <= DIGIT_NINE));
}
NS_INLINE BOOL isLowerHexDigit(UTF32Char character)
{
return ((character >= LATIN_SMALL_LETTER_A && character <= LATIN_SMALL_LETTER_F) ||
(character >= DIGIT_ZERO && character <= DIGIT_NINE));
}
NS_INLINE BOOL isAlphanumeric(UTF32Char character)
{
return ((character >= DIGIT_ZERO && character <= DIGIT_NINE) ||
@@ -151,17 +172,14 @@ NS_INLINE BOOL isStringAlphanumeric(NSString *string)
return ([string rangeOfCharacterFromSet:set].location == NSNotFound);
}
NS_INLINE BOOL isInvalidNumericRange(unsigned long long numeric)
NS_INLINE BOOL isSurrogate(unsigned long long character)
{
return ((numeric >= 0xD800 && numeric <= 0xDFFF) ||
numeric > 0x10FFFF);
return (character >= 0xD800 && character <= 0xDFFF);
}
NS_INLINE unichar NumericReplacementCharacter(UTF32Char character)
{
if (character == NULL_CHAR) {
return REPLACEMENT_CHAR;
} else if (character >= 0x0080 && character <= 0x009F) {
if (character >= 0x0080 && character <= 0x009F) {
return NumericReplacementTable[character - 0x0080];
} else {
return NULL_CHAR;
+16 -3
View File
@@ -12,9 +12,7 @@
#define TOKENIZER_STATES \
STATE_ENTRY( HTMLTokenizerStateData, = 0) \
STATE_ENTRY( HTMLTokenizerStateCharacterReferenceInData, ) \
STATE_ENTRY( HTMLTokenizerStateRCDATA, ) \
STATE_ENTRY( HTMLTokenizerStateCharacterReferenceInRCDATA, ) \
STATE_ENTRY( HTMLTokenizerStateRAWTEXT, ) \
STATE_ENTRY( HTMLTokenizerStateScriptData, ) \
STATE_ENTRY( HTMLTokenizerStatePLAINTEXT, ) \
@@ -59,6 +57,10 @@
STATE_ENTRY( HTMLTokenizerStateCommentStart, ) \
STATE_ENTRY( HTMLTokenizerStateCommentStartDash, ) \
STATE_ENTRY( HTMLTokenizerStateComment, ) \
STATE_ENTRY( HTMLTokenizerStateCommentLessThanSign, ) \
STATE_ENTRY( HTMLTokenizerStateCommentLessThanSignBang, ) \
STATE_ENTRY( HTMLTokenizerStateCommentLessThanSignBangDash, ) \
STATE_ENTRY( HTMLTokenizerStateCommentLessThanSignBangDashDash, ) \
STATE_ENTRY( HTMLTokenizerStateCommentEndDash, ) \
STATE_ENTRY( HTMLTokenizerStateCommentEnd, ) \
STATE_ENTRY( HTMLTokenizerStateCommentEndBang, ) \
@@ -78,7 +80,18 @@
STATE_ENTRY( HTMLTokenizerStateDOCTYPESystemIdentifierSingleQuoted, ) \
STATE_ENTRY( HTMLTokenizerStateAfterDOCTYPESystemIdentifier, ) \
STATE_ENTRY( HTMLTokenizerStateBogusDOCTYPE, ) \
STATE_ENTRY( HTMLTokenizerStateCDATASection, )
STATE_ENTRY( HTMLTokenizerStateCDATASection, ) \
STATE_ENTRY( HTMLTokenizerStateCDATASectionBracket, ) \
STATE_ENTRY( HTMLTokenizerStateCDATASectionEnd, ) \
STATE_ENTRY( HTMLTokenizerStateCharacterReference, ) \
STATE_ENTRY( HTMLTokenizerStateNamedCharacterReference, ) \
STATE_ENTRY( HTMLTokenizerStateAmbiguousAmpersand, ) \
STATE_ENTRY( HTMLTokenizerStateNumericCharacterReference, ) \
STATE_ENTRY( HTMLTokenizerStateHexadecimalCharacterReferenceStart, ) \
STATE_ENTRY( HTMLTokenizerStateDecimalCharacterReferenceStart, ) \
STATE_ENTRY( HTMLTokenizerStateHexadecimalCharacterReference, ) \
STATE_ENTRY( HTMLTokenizerStateDecimalCharacterReference, ) \
STATE_ENTRY( HTMLTokenizerStateNumericCharacterReferenceEnd, )
typedef NS_ENUM(NSUInteger, HTMLTokenizerState)
{
+38
View File
@@ -0,0 +1,38 @@
module HTMLKit {
umbrella header "HTMLKit.h"
module * { export * }
export *
explicit module Private {
textual header "CSSCodePoints.h"
header "CSSInputStream.h"
header "HTMLCharacterToken.h"
header "HTMLCommentToken.h"
header "HTMLDOCTYPEToken.h"
header "HTMLElementAdjustment.h"
header "HTMLElementTypes.h"
header "HTMLEOFToken.h"
header "HTMLInputStreamReader.h"
header "HTMLListOfActiveFormattingElements.h"
header "HTMLMarker.h"
header "HTMLParseErrorToken.h"
header "HTMLParserInsertionModes.h"
header "HTMLStackOfOpenElements.h"
header "HTMLTagToken.h"
header "HTMLToken.h"
header "HTMLTokenizer.h"
textual header "HTMLTokenizerCharacters.h"
header "HTMLTokenizerEntities.h"
header "HTMLTokenizerStates.h"
header "HTMLTokens.h"
header "HTMLNode+Private.h"
header "HTMLDocument+Private.h"
header "HTMLCharacterData+Private.h"
header "HTMLRange+Private.h"
header "HTMLNodeIterator+Private.h"
header "HTMLParser+Private.h"
header "HTMLNodeTraversal.h"
header "HTMLDOMUtils.h"
}
}
-30
View File
@@ -1,30 +0,0 @@
module HTMLKit {
umbrella header "HTMLKit.h"
private header "CSSCodePoints.h"
private header "CSSInputStream.h"
private header "HTMLCharacterToken.h"
private header "HTMLCommentToken.h"
private header "HTMLDOCTYPEToken.h"
private header "HTMLElementAdjustment.h"
private header "HTMLElementTypes.h"
private header "HTMLEOFToken.h"
private header "HTMLInputStreamReader.h"
private header "HTMLListOfActiveFormattingElements.h"
private header "HTMLMarker.h"
private header "HTMLNode+Private.h"
private header "HTMLNodeTraversal.h"
private header "HTMLParseErrorToken.h"
private header "HTMLParserInsertionModes.h"
private header "HTMLStackOfOpenElements.h"
private header "HTMLTagToken.h"
private header "HTMLToken.h"
private header "HTMLTokenizer.h"
private header "HTMLTokenizerCharacters.h"
private header "HTMLTokenizerEntities.h"
private header "HTMLTokenizerStates.h"
private header "HTMLTokens.h"
module * { export * }
export *
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -7,7 +7,7 @@
//
#import <XCTest/XCTest.h>
#import <HTMLKit/HTMLKit.h>
#import "HTMLKit.h"
#import "CSSSelectorTest.h"
#import "CSSSelectorParser.h"
@@ -11,6 +11,7 @@
#import "HTMLDocument.h"
#import "HTMLElement.h"
#import "CSSSelectors.h"
#import "HTMLKitTestUtil.h"
static NSString * const CSSTests = @"css-tests";
@@ -18,8 +19,7 @@ static NSString * const CSSTests = @"css-tests";
+ (NSArray *)loadCSSSelectorTests
{
NSString *path = [[NSBundle bundleForClass:self.class] resourcePath];
path = [path stringByAppendingPathComponent:CSSTests];
NSString *path = [HTMLKitTestUtil pathForFixture:CSSTests ofType:nil inDirectory:nil];
NSMutableArray *tests = [NSMutableArray array];
NSArray *testFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
@@ -14,9 +14,9 @@
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *input;
@property (nonatomic, strong) NSArray *output;
@property (nonatomic, strong) NSArray *errors;
@property (nonatomic, strong) NSArray *initialStates;
@property (nonatomic, copy) NSString *lastStartTag;
@property (nonatomic, assign) BOOL ignoreErrorOrder;
+ (NSDictionary *)loadHTML5LibTokenizerTests;
@@ -7,23 +7,22 @@
//
#import "HTML5LibTokenizerTest.h"
#import "HTMLOrderedDictionary.h"
#import "HTMLTokenizerStates.h"
#import "HTMLTokens.h"
#import "HTMLKitTestUtil.h"
static NSString * const HTML5LibTests = @"html5lib-tests";
static NSString * const TOKENIZER = @"tokenizer";
static NSString * const Tokenizer = @"tokenizer";
@implementation HTML5LibTokenizerTest
+ (NSDictionary *)loadHTML5LibTokenizerTests
{
NSString *path = [[NSBundle bundleForClass:self.class] resourcePath];
path = [path stringByAppendingPathComponent:HTML5LibTests];
path = [path stringByAppendingPathComponent:TOKENIZER];
NSMutableDictionary *testsMap = [NSMutableDictionary dictionary];
NSString *path = [HTMLKitTestUtil pathForFixture:Tokenizer ofType:nil inDirectory:HTML5LibTests];
NSArray *testFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
NSMutableDictionary *testsMap = [NSMutableDictionary dictionary];
for (NSString *testFile in testFiles) {
if (![testFile.pathExtension isEqualToString:@"test"]) {
continue;
@@ -71,16 +70,16 @@ static NSString * const TOKENIZER = @"tokenizer";
{
BOOL doubleEscaped = [test[@"doubleEscaped"] boolValue];
// Test Title
// Test Title
self.title = test[@"description"];
// Test Input
// Test Input
self.input = test[@"input"];
if (doubleEscaped) {
self.input = [self processDoubleEscaped:self.input];
}
// Test Output
// Test Output
NSMutableArray *tokens = [NSMutableArray array];
NSArray *outputs = test[@"output"];
for (NSArray *output in outputs) {
@@ -90,7 +89,7 @@ static NSString * const TOKENIZER = @"tokenizer";
[tokens addObject:[HTMLEOFToken token]];
self.output = tokens;
// Test Initial States
// Test Initial States
NSMutableArray *initialStates = [NSMutableArray array];
NSArray *states = test[@"initialStates"];
@@ -102,6 +101,10 @@ static NSString * const TOKENIZER = @"tokenizer";
state = HTMLTokenizerStateRCDATA;
} else if ([name isEqualToString:@"RAWTEXT state"]) {
state = HTMLTokenizerStateRAWTEXT;
} else if ([name isEqualToString:@"Script data state"]) {
state = HTMLTokenizerStateScriptData;
} else if ([name isEqualToString:@"CDATA section state"]) {
state = HTMLTokenizerStateCDATASection;
}
[initialStates addObject:@(state)];
}
@@ -111,19 +114,21 @@ static NSString * const TOKENIZER = @"tokenizer";
self.initialStates = initialStates;
// Test Last Start Tag
// Test Last Start Tag
self.lastStartTag = test[@"lastStartTag"];
// Ignore Error Order
self.ignoreErrorOrder = [test[@"ignoreErrorOrder"] boolValue];
// Test errors
NSArray *errors = test[@"errors"];
NSMutableArray *errorTokens = [NSMutableArray new];
for (NSDictionary *error in errors) {
HTMLParseErrorToken *token = [[HTMLParseErrorToken alloc] initWithCode:error[@"code"] details:nil location:0];
[errorTokens addObject:token];
}
self.errors = errorTokens;
}
- (HTMLToken *)processOutputToken:(id)output doubleEscaped:(BOOL)doubleEscaped
{
if ([output isKindOfClass:[NSString class]] && [output isEqualToString:@"ParseError"]) {
return [HTMLParseErrorToken new];
}
NSString *type = [output firstObject];
NSString *data = nil;
@@ -147,8 +152,6 @@ static NSString * const TOKENIZER = @"tokenizer";
return token;
} else if ([type isEqualToString:@"EndTag"]) {
return [[HTMLEndTagToken alloc] initWithTagName:data];
} else if ([type isEqualToString:@"ParseError"]) {
return [HTMLParseErrorToken new];
} else if ([type isEqualToString:@"StartTag"]) {
HTMLStartTagToken *token = [[HTMLStartTagToken alloc] initWithTagName:data];
NSDictionary *attributes = output[2];
@@ -13,6 +13,7 @@
#import "HTMLElement.h"
#import "HTMLText.h"
#import "HTMLComment.h"
#import "HTMLKitTestUtil.h"
static NSString * const HTML5LibTests = @"html5lib-tests";
static NSString * const TreeConstruction = @"tree-construction";
@@ -21,13 +22,10 @@ static NSString * const TreeConstruction = @"tree-construction";
+ (NSDictionary *)loadHTML5LibTreeConstructionTests
{
NSString *path = [[NSBundle bundleForClass:self.class] resourcePath];
path = [path stringByAppendingPathComponent:HTML5LibTests];
path = [path stringByAppendingPathComponent:TreeConstruction];
NSMutableDictionary *testsMap = [NSMutableDictionary dictionary];
NSString *path = [HTMLKitTestUtil pathForFixture:TreeConstruction ofType:nil inDirectory:HTML5LibTests];
NSArray *testFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
NSMutableDictionary *testsMap = [NSMutableDictionary dictionary];
for (NSString *testFile in testFiles) {
if (![testFile.pathExtension isEqualToString:@"dat"]) {
continue;
@@ -48,7 +46,7 @@ static NSString * const TreeConstruction = @"tree-construction";
NSMutableArray *tests = [NSMutableArray array];
NSScanner *scanner = [NSScanner scannerWithString:contents];
NSString * (^ nextTest)() = ^ NSString * () {
NSString * (^ nextTest)(void) = ^ NSString * () {
NSString *str;
[scanner scanUpToString:@"\n\n#data" intoString:&str];
return str;
+100
View File
@@ -0,0 +1,100 @@
//
// HTMLCharacterDataTests.m
// HTMLKit
//
// Created by Iska on 10/01/17.
// Copyright © 2017 BrainCookie. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "HTMLElement.h"
#import "HTMLCharacterData.h"
#import "HTMLText.h"
@interface HTMLCharacterDataTests : XCTestCase
@end
@implementation HTMLCharacterDataTests
- (void)testSetData
{
HTMLText *text = [[HTMLText alloc] initWithData:@"This is a text"];
[text setData:@"New text"];
XCTAssertEqualObjects(text.data, @"New text");
}
- (void)testAppendData
{
HTMLText *text = [[HTMLText alloc] initWithData:@"This is a text"];
[text appendData:@"New text"];
XCTAssertEqualObjects(text.data, @"This is a textNew text");
}
- (void)testReplaceData
{
HTMLText *text = [[HTMLText alloc] initWithData:@"This is a text"];
[text replaceDataInRange:NSMakeRange(0, 5) withData:@"New text "];
XCTAssertEqualObjects(text.data, @"New text is a text");
[text replaceDataInRange:NSMakeRange(4, 4) withData:@"data"];
XCTAssertEqualObjects(text.data, @"New data is a text");
}
- (void)testDeleteData
{
HTMLText *text = [[HTMLText alloc] initWithData:@"This is a text"];
[text deleteDataInRange:NSMakeRange(5, 3)];
XCTAssertEqualObjects(text.data, @"This a text");
[text deleteDataInRange:NSMakeRange(0, text.data.length)];
XCTAssertEqualObjects(text.data, @"");
}
- (void)testInsertData
{
HTMLText *text = [[HTMLText alloc] initWithData:@"This is a text"];
[text insertData:@"New " atOffset:10];
XCTAssertEqualObjects(text.data, @"This is a New text");
[text insertData:@"Prefix " atOffset:0];
XCTAssertEqualObjects(text.data, @"Prefix This is a New text");
}
- (void)testSplitText_Invalid
{
HTMLText *text = [[HTMLText alloc] initWithData:@"text"];
XCTAssertThrows([text splitTextAtOffset:5]);
}
- (void)testSplitText
{
HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div"];
HTMLText *text = [[HTMLText alloc] initWithData:@"This is a text"];
[div appendNode:text];
HTMLText *newText = [text splitTextAtOffset:7];
XCTAssertEqualObjects(newText.data, @" a text");
XCTAssertEqual(div.childNodesCount, 2);
XCTAssertEqualObjects(div.firstChild.textContent, @"This is");
XCTAssertEqualObjects(div.lastChild.textContent, @" a text");
[div appendNode:[[HTMLElement alloc] initWithTagName:@"p"]];
newText = [newText splitTextAtOffset:0];
XCTAssertEqualObjects(newText.data, @" a text");
XCTAssertEqual(div.childNodesCount, 4);
XCTAssertEqualObjects([div childNodeAtIndex:0].textContent, @"This is");
XCTAssertEqualObjects([div childNodeAtIndex:1].textContent, @"");
XCTAssertEqualObjects([div childNodeAtIndex:2].textContent, @" a text");
XCTAssertEqual([div childNodeAtIndex:3].nodeType, HTMLNodeElement);
}
@end
@@ -8,6 +8,7 @@
#import <XCTest/XCTest.h>
#import "HTMLParser.h"
#import "HTMLKitTestUtil.h"
@interface HTMLKitParserPerformance : XCTestCase
@@ -17,10 +18,9 @@
#define HTMLKIT_NO_DOM_CHECKS
- (void)testParserPerformance
- (void)_testParserPerformance
{
NSString *path = [[NSBundle bundleForClass:self.class] resourcePath];
path = [path stringByAppendingPathComponent:@"HTML Standard.html"];
NSString *path = [HTMLKitTestUtil pathForFixture:@"HTML Standard" ofType:@"html" inDirectory:@"Fixtures"];
NSString *string = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
@@ -11,5 +11,7 @@
@interface HTMLKitTestUtil : NSObject
+ (NSInvocation *)addTestToClass:(Class)cls withName:(NSString *)name block:(id)block;
+ (id)ivarForInstacne:(id)instance name:(NSString *)name;
+ (NSString *)pathForFixture:(NSString *)fixture ofType:(NSString *)type inDirectory:(NSString *)directory;
@end
@@ -26,4 +26,29 @@
return invocation;
}
+ (id)ivarForInstacne:(id)instance name:(NSString *)name
{
Ivar ivar = class_getInstanceVariable([instance class], [name UTF8String]);
return object_getIvar(instance, ivar);
}
+ (NSString *)pathForFixture:(NSString *)fixture ofType:(NSString *)type inDirectory:(NSString *)directory
{
// Try testing bundle first
NSString *path = [[NSBundle bundleForClass:self.class] pathForResource:fixture ofType:type inDirectory:directory];
if (path) {
return path;
}
path = [[@(__FILE__) stringByDeletingLastPathComponent] stringByDeletingLastPathComponent];
if (directory) {
path = [path stringByAppendingPathComponent:directory];
}
NSString *resource = type ? [NSString stringWithFormat:@"%@.%@", fixture, type] : fixture;
path = [path stringByAppendingPathComponent:resource];
return path;
}
@end
@@ -10,6 +10,7 @@
#import "HTMLTokenizer.h"
#import "HTMLTokenizerStates.h"
#import "HTMLTokens.h"
#import "HTMLKitTestUtil.h"
@interface HTMLKitTokenizerPerformance : XCTestCase
@@ -17,10 +18,9 @@
@implementation HTMLKitTokenizerPerformance
- (void)testTokenizerPerformance
- (void)_testTokenizerPerformance
{
NSString *path = [[NSBundle bundleForClass:self.class] resourcePath];
path = [path stringByAppendingPathComponent:@"HTML Standard.html"];
NSString *path = [HTMLKitTestUtil pathForFixture:@"HTML Standard" ofType:@"html" inDirectory:@"Fixtures"];
NSString *string = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
@@ -81,20 +81,31 @@
for (NSNumber *state in test.initialStates) {
HTMLTokenizer *tokenizer = [[HTMLTokenizer alloc] initWithString:test.input];
[tokenizer setValue:test.lastStartTag forKey:@"_lastStartTagName"];
[tokenizer setValue:test.lastStartTag forKey:@"_lastStartTagName"];
tokenizer.state = [state integerValue];
NSArray *expectedTokens = test.output;
NSArray *tokens = tokenizer.allObjects;
NSArray *expectedErrors = test.errors;
NSMutableArray *actualErrors = [NSMutableArray new];
tokenizer.parseErrorCallback = ^(HTMLParseErrorToken *token) {
[actualErrors addObject:token];
};
NSString *message = [NSString stringWithFormat:@"HTML5Lib test in file: \'%@\' Title: '%@'\nInput: '%@'\nExpected:\n%@\nActual:\n%@\n",
NSArray *expectedTokens = test.output;
NSArray *actualTokens = tokenizer.allObjects;
NSString *message = [NSString stringWithFormat:@"\nTest file: \'%@\'\nTitle: '%@'\nInput: '%@'\n",
test.testFile,
test.title,
test.input,
expectedTokens,
tokens];
XCTAssertEqualObjects(tokens, expectedTokens, @"%@", message);
test.input];
XCTAssertEqualObjects(actualTokens, expectedTokens, @"%@", message);
message = [NSString stringWithFormat:@"\nTest file: \'%@\'\nTitle: '%@'\nInput: '%@'\n",
test.testFile,
test.title,
test.input];
XCTAssertEqualObjects(actualErrors, expectedErrors, @"%@", message);
}
}
}

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