166 Commits

Author SHA1 Message Date
iska e788c832c6 Merge branch 'release/0.3.0' 2015-11-29 03:35:31 +01:00
iska a33f4e0834 Use nodes tree description in HTML Parser tests 2015-11-29 03:29:13 +01:00
iska 9e4007dcba Use nullability annotations for HTML Ordered Dictionary 2015-11-29 03:29:12 +01:00
iska 50b67cfa94 Add CSS Selector tests into own virtual group 2015-11-29 03:29:12 +01:00
iska 47d244289e Merge branch 'feature/css_selectors' into develop 2015-11-28 21:14:24 +01:00
iska 6b81d6e465 Add tests for the CSS Selector Parser 2015-11-28 21:11:48 +01:00
iska 13b8fe7808 Add CSS Selectors Parser implementation 2015-11-28 21:11:16 +01:00
iska 94fed8afa1 Add CSS test fixtures
Tests are adapted from the official suite:
http://www.w3.org/Style/CSS/Test/CSS3/Selectors/current/html/static/flat/

Test file format:
Each test file is a HTML fragment with:

<script> element with id="selectors": contains a JSON array of several selector
definitions. Each selector definition has a selector string as input, a "match" array
of the expected matched IDs. Some selectors must fail with a parse error, such
selectors have an "error" key with the error location in the selector string.

<div> element with id="testDOM": contains the DOM fragment which should be
used to select the elements for each of the defined selectors.
2015-11-28 21:10:31 +01:00
iska 30dc6cf343 Add HTML Node method to count child elements 2015-11-28 21:09:44 +01:00
iska badf070907 Add HTMLKit error domain header 2015-11-28 21:09:25 +01:00
iska 3443321459 Add missing check in cosnume-character HTML stream method
Should only advance location if not reconsuming previous character
2015-11-28 21:08:39 +01:00
iska 60dc6bfc43 Fix nth-last-child selector 2015-11-28 21:07:10 +01:00
iska 60a5821a2c Fix general sibling combinator selector 2015-11-28 21:06:52 +01:00
iska 621dbe36d6 Fix pseudo selectors' names 2015-11-27 19:44:57 +01:00
iska 50ae2c5283 Fix :root selector 2015-11-27 19:41:09 +01:00
iska 0cb374681c Fix sibling combinator selectors 2015-11-27 01:10:27 +01:00
iska c09adc2f96 Add Node properties for previous and next sibling elements 2015-11-27 01:10:07 +01:00
iska 09991a149f Fix :disabled selector's check for disabled form elements
Should've  used or- instead of all-combinator
2015-11-27 01:01:02 +01:00
iska f735fcdfd6 Treat nil attribute value as an empty string in the Attribute selector 2015-11-27 00:47:45 +01:00
iska 5a3c1063e7 Fix Nth-Expression selector's compute-index methods 2015-11-27 00:47:24 +01:00
iska 8cb088367d Add Node methods to handle child elements' indices 2015-11-27 00:46:57 +01:00
iska 7ec90d6470 Fix escaped-code-point consuming in CSS input stream 2015-11-25 21:34:43 +01:00
iska 002564f9ae Use debug description in combinator selectors 2015-11-25 21:33:06 +01:00
iska 4c80c520d4 Fix reconsume checks in input stream reader for CSS logic 2015-11-24 23:04:21 +01:00
iska 85abf35099 Fix implementation of the shorthand "anyOf" selector 2015-11-24 23:03:13 +01:00
iska 578806ec2f Add description methods for compound selectors 2015-11-24 23:02:32 +01:00
iska 7923efb02b Add compound selector method to append further selectors 2015-11-24 23:02:14 +01:00
iska f4d42e0753 Fix description methods for combinator selectors 2015-11-24 23:01:49 +01:00
iska 56f99f9168 Add input stream method to consume a CSS combinator 2015-11-24 23:01:27 +01:00
iska c8dd1d7791 Fix consume CSS identifier input stream method
http://www.w3.org/TR/CSS21/syndata.html#characters
https://drafts.csswg.org/css-syntax/#consume-name
2015-11-24 23:01:11 +01:00
iska c57d3453fc Add id & class selector tests 2015-10-26 22:27:00 +01:00
iska a413156514 Fix method name for checked element selector 2015-10-26 22:26:43 +01:00
iska 203d153487 Fix hyphen-attribute test 2015-10-26 22:26:19 +01:00
iska 4ceb913b9f Add HTMLKit workspace and setup project headers 2015-10-26 22:26:00 +01:00
iska 7896f1687e Remove temp test case from type selector tests class 2015-10-23 23:51:30 +02:00
iska 55bb8effb2 Add tests for the CSS Combinator selectors 2015-10-23 23:48:47 +02:00
iska 2e10c23f52 Add debug description method to HTML Element 2015-10-23 22:54:34 +02:00
iska 4f47a750e0 Use lowercase string in the Nth-Expression selector parser 2015-10-23 22:54:06 +02:00
iska 46beae1b2d Change attribute selector's debug description string 2015-10-23 22:53:06 +02:00
iska 2e441727bc Fix equals method in the attribute selector for iOS target 2015-10-23 22:52:40 +02:00
iska cc101b9f4e Fix type selectors in the main header's implementation 2015-10-23 22:52:14 +02:00
iska 0635744a2d Add a CSS selector-base Node Filter implementation 2015-10-23 22:50:51 +02:00
iska b5bb0a48b6 Add tests for the Nth-Expression selectors 2015-10-23 22:50:20 +02:00
iska 95ffccf67b Add HTML Node methods for selecting elements with given CSS Selectors 2015-10-23 22:49:25 +02:00
iska 6af01a1214 Add -ObjC to iOS testing target to load category methods 2015-10-22 19:20:22 +02:00
iska 667693939f Update project settings for Xcode7 2015-10-22 19:12:42 +02:00
iska f05adfb56b Add implementation for the Nth-Expressions selector
http://www.w3.org/TR/css3-selectors/#structural-pseudos
2015-10-21 23:34:24 +02:00
iska bed2edf22a Remove obsolete CSS Tokenizer and Token classes 2015-10-21 22:32:09 +02:00
iska 2574736359 Use pseudo class and block selectors for parameterless pseudo class selectors 2015-10-21 22:30:58 +02:00
iska ab7de014cf Add implementation for "required" and "optional" selectors
https://html.spec.whatwg.org/multipage/scripting.html#selector-reqiored
https://html.spec.whatwg.org/multipage/scripting.html#selector-optional
2015-10-21 22:30:22 +02:00
iska 15e768267a Rename CSS tokenizer code points header file 2015-10-21 22:21:24 +02:00
iska ee82cacc48 Add CSS input stream method to consume characters till given codepoint is met 2015-10-21 22:16:18 +02:00
iska 61d7c683fb Add implementations for structural pseudo class selectors
https://html.spec.whatwg.org/multipage/scripting.html#selectors
http://api.jquery.com/category/selectors/jquery-selector-extensions/
http://sizzlejs.com
2015-10-21 22:15:36 +02:00
iska bd84884bf7 Add a block-based selector class for inline selector implementations 2015-10-21 22:13:03 +02:00
iska f248b39d26 Move Nth-Expression typedef to the public header 2015-10-21 00:35:07 +02:00
iska 118ea137f4 Update existing type and attribute tests for new structure 2015-10-20 21:31:08 +02:00
iska 0366f39d1a Add public header for all available selectors and hide the actual implementations 2015-10-20 21:30:39 +02:00
iska d107c05a44 Add stubs for the Nth-Expression Selectors
http://www.w3.org/TR/css3-selectors/#structural-pseudos
2015-10-20 21:17:42 +02:00
iska 5ca3bf1192 Replace nullability specifiers from methods and properties with nullability-regions 2015-10-20 21:15:30 +02:00
iska 02393ddedd Add implementation for the CSS Combinators
http://www.w3.org/TR/css3-selectors/#combinators
2015-10-19 21:51:46 +02:00
iska 33df9e7fb2 Add implementation for combining multiple selectors with "All" and "Exists" quantifiers 2015-10-19 21:49:51 +02:00
iska 6020b2bdd9 Add implementation for the negation and "has" selectors
The negation selector is specified here:
http://www.w3.org/TR/css3-selectors/#negation

The "has" selector is an extra convenience selector
2015-10-19 21:41:33 +02:00
iska c44c77d63d Refactor selectors code
- Remove simple sequence
- Remove simple selector protocol
- Let selectors subclass CSSSelector directly
2015-10-18 22:36:03 +02:00
iska 9905f45e27 Change the CSS selector base class to a protocol unrelated to the HTML Node Filter 2015-10-12 20:12:50 +02:00
iska c868bd1a56 Add HTML stream method to consume characters in given string 2015-10-12 20:00:44 +02:00
iska 8f38aed6c6 Add "not" attribute selector to match attribute not-having given value 2015-10-12 20:00:08 +02:00
iska 1917fb1f1c Add base class for pseudo class selectors
http://www.w3.org/TR/css3-selectors/#pseudo-classes
2015-10-11 23:43:29 +02:00
iska dc93bb07f6 Remove unneeded methods from the base selector class 2015-10-11 23:41:15 +02:00
iska a74be4dfa9 Add nullability specifiers to the simple sequence of selectors 2015-10-11 23:40:51 +02:00
iska 7cd47486ac Add nullability specifiers to type selector 2015-10-11 23:40:15 +02:00
iska ad902e3138 Add nullability specifiers to attribute selector 2015-10-11 23:40:03 +02:00
iska 6cfe3b6f83 Add implementation for CSS nth-expressions
https://drafts.csswg.org/css-syntax/#anb-microsyntax
2015-10-10 21:34:50 +02:00
iska a2096b0e54 Add debug descriptions for the attribute, type & sequence selectors 2015-10-10 00:06:09 +02:00
iska 79fda9fdbe Remove extra initializer methods from type and attribute selectors
keep it simple
2015-10-06 23:05:25 +02:00
iska 1a05c41f86 Refactor CSS input stream to subclass the HTML input stream class to prevent duplication 2015-10-06 23:01:59 +02:00
iska bf751d94b1 Add category method for the hex number character set
and use a dispatch once to initialize the set only once
2015-10-04 23:50:39 +02:00
iska 43705dac69 Update gitignore file 2015-10-02 19:29:10 +02:00
iska 3c7e6e1913 Change returned code points to UniChar instead of UTF32Chars in CSS input stream 2015-10-02 19:28:32 +02:00
iska af4444e9a8 Add log message on CSS Tokenizer error
Proper handling will be implemented later
2015-06-23 00:07:47 +02:00
iska 23d0d62295 Reorganize CSS related classes 2015-06-22 23:48:31 +02:00
iska b0668e121e Remove obsolete CSS lexer 2015-06-22 23:40:16 +02:00
iska aa95bbd9f2 Add Unicode Range CSS Token parsing 2015-06-22 20:55:33 +02:00
iska 1544f71c50 Add initial implementation for a CSS Tokenizer
http://www.w3.org/TR/2013/WD-css-syntax-3-20131105/#tokenization
2015-06-22 00:44:09 +02:00
iska b1ddbc5f75 Add CSS Tokenizer related helper function for dealing with code points 2015-06-22 00:43:32 +02:00
iska 576dc4dfb2 Add CSS Token classes 2015-06-22 00:42:34 +02:00
iska c4aacfae2b Add CSS Input Stream class
http://www.w3.org/TR/2013/WD-css-syntax-3-20131105/#tokenizing-and-parsing
2015-06-15 22:06:26 +02:00
iska a54cae8829 Fix type & attribute selectors and their tests after merge with new DOM 2015-06-07 22:34:50 +02:00
iska 852dfad19c Add enum values for the other different tokens in a CSS Selector 2015-06-07 20:52:42 +02:00
iska a5d53b3eef Fix acceptNode method in the selector's implementation after merge with new DOM 2015-06-07 00:08:40 +02:00
iska 5e5b903c52 Fix import in CSS Selector test classes 2015-06-06 23:29:04 +02:00
iska 40b6e41e12 Merge branch 'develop' into feature/css_selectors
Conflicts:
	HTMLKit.xcodeproj/project.pbxproj
	HTMLKit/HTMLNodeFilter.h
2015-06-06 20:20:27 +02:00
iska 0401118657 Merge branch 'release/0.2.0' 2015-06-06 20:12:42 +02:00
iska 045e4db9d1 Add node iterator, tree walker & node filter imports to the HTMLDOM header 2015-06-06 20:11:49 +02:00
iska a7caa34762 Disable HTML DOM checks in parser's performance test
The baseline was set before the DOM validations were implemented.
2015-06-06 20:10:28 +02:00
iska df53870880 Drop the "Node" suffix from HTML Node's first child & last child properties
https://dom.spec.whatwg.org/#interface-node
2015-06-06 19:04:03 +02:00
iska 7f25aacaf1 Add test cases for the Tree Walker class 2015-06-06 19:00:02 +02:00
iska 5e482788ce Add Tree Walker's initializer methods 2015-06-06 18:59:06 +02:00
iska a39890e6e9 Add documentElement property for the HTML Document
The documentElement is the <html> element
2015-06-06 18:58:45 +02:00
iska fd516c67ef Change Node Filter's show options enum to NS_OPTIONS 2015-06-05 23:01:55 +02:00
iska a390edf599 Reintroduce the HTMLNodeFilterValue for Tree Walker implementation
While the Node Iterator treats skip & reject the same way, the Tree Walker can skip over a node
and all its children if rejected.

This reverses dc3de7a470
2015-06-05 23:00:50 +02:00
iska 0c82f6891b Add implementation for the Tree Walker
https://dom.spec.whatwg.org/#interface-treewalker
2015-06-05 22:46:41 +02:00
iska 0d7c57d755 Refactor common node traversal methods into separate class
These will be used in the Tree Walker
https://dom.spec.whatwg.org/#interface-treewalker
2015-06-05 22:09:02 +02:00
iska 40400864d8 Add block-based API for Node Filter
- HTMLNodeFilterBlock is a block-based class implementation conforming to HTMLNodeFilter
- HTMLNode & NodeIterator get block-based initializers
2015-06-05 18:06:23 +02:00
iska dc3de7a470 Remove the HTMLNodeFilterValue enum since Reject and Skip are semantically equivalent 2015-06-05 17:05:22 +02:00
iska c1b18f527c Refactor Node Iterator's initializers so that the filter argument is last 2015-06-05 17:04:28 +02:00
iska d4bb3c6636 Add implementation for the removing steps of the HTML Node Iterator
https://dom.spec.whatwg.org/#interface-nodeiterator
2015-06-05 16:47:37 +02:00
iska 0baa343e95 Add Document methods to access the root, head & body elements
The implementation will be changed/adapted later when the CSS Selectors are ready.
2015-06-05 16:46:36 +02:00
iska 2f1555e93d Rename group for DOM tests 2015-06-02 01:06:13 +02:00
iska a0fd89f417 Move test class for Ordered Dictionary into own group 2015-06-02 01:05:44 +02:00
iska d002c6cedd Add HTML Node methods for comparing node positions in a tree
https://dom.spec.whatwg.org/#dom-node-comparedocumentpositionother
2015-05-31 16:53:54 +02:00
iska 71fefa2fa9 Add methods to attach Node Iterators to the owner document node of the iterator's root
This is a prerequisite to implement the iterator's removing steps.
https://dom.spec.whatwg.org/#interface-nodeiterator
2015-05-29 20:16:17 +02:00
iska 066bdeffa8 Add Node Iterator test cases for iteration and filtering logic 2015-05-29 00:39:52 +02:00
iska 3df5a219e1 Add Node Iterator initializers with filter and show-options arguments 2015-05-28 23:26:08 +02:00
iska e1c3533a1c Remove unused import in Tree Construction test class 2015-05-28 22:57:31 +02:00
iska dfeebc7f7f Remove frameworks search path entry causing an ignored warning on build 2015-05-28 22:56:49 +02:00
iska cd6e8bf4fc Remove Tree Enumerator since it was superseded by the Node Iterator implementation 2015-05-28 01:22:29 +02:00
iska 0fa394b911 Fix traversing method in Node Iterator 2015-05-28 01:21:06 +02:00
iska 13ed63017e Fix initial Node Iterator values 2015-05-28 00:55:45 +02:00
iska ac561570f3 Use correct DOM import in test classes 2015-05-28 00:43:27 +02:00
iska 165cdb5a75 Fix HTML Element initializer usage after arguments reordering 2015-05-28 00:42:59 +02:00
iska aece7138a4 Rename Tree Enumeration Tests to Node Iterator Tests 2015-05-28 00:42:20 +02:00
iska 45156d98fd Add implementation for HTML Node Iterator
This, along with Tree Walker, will replace the Tree Enumerator.
https://dom.spec.whatwg.org/#interface-nodeiterator
2015-05-28 00:34:17 +02:00
iska 7892a925c8 Fix Element initializer after reordering the arguments 2015-05-27 00:59:37 +02:00
iska f8ee4a38b4 Add HTML Node Filter protocol 2015-05-27 00:59:10 +02:00
iska de597a51a4 Reorganize HTML DOM classes 2015-05-27 00:53:58 +02:00
iska 470b9b8d37 Change arguments order in HTML Element initializer for convenience
Dictionary argument moved to last position.
2015-05-27 00:39:39 +02:00
iska 915f4cc064 Remove baseURI property from HTMLNode
Proper support will be implemented when needed.
2015-05-27 00:38:07 +02:00
iska 11af0509a3 Rename type property to nodeType in HTMLNode for better clarity 2015-05-27 00:37:23 +02:00
iska 2ee3b2d2b3 Rename HTMLNode header to HTMLDOM 2015-05-22 20:06:24 +02:00
iska 1e9118078f Rename Nodes group to DOM 2015-05-21 23:24:13 +02:00
iska 74aec57785 Add method to set inner HTML of an Element 2015-05-21 00:20:20 +02:00
iska 111acb4f8f Add a convenience HTML Document initializer
Creates a document directly given a HTML string, instead of manually creating a parser.
2015-05-20 22:39:52 +02:00
iska 05e224283a Expose tokenizer's position variables for better error reporting 2015-05-16 14:23:06 +02:00
iska 8490d4d2ca Add tokenizer method to get next-non-space token 2015-05-16 14:22:44 +02:00
iska 52b850317e Remove selector method to match a HTML Element used in the simple selectors
Selectors have only one method to implement, the Node Filter's acceptNode
2015-05-15 22:37:56 +02:00
iska f5d970ba87 Add implementation for a CSS Simple Sequence of Selectors
http://www.w3.org/TR/css3-selectors/#sequence
2015-05-15 02:37:51 +02:00
iska 0c8fd754c3 Add CSS Simple Selector protocol to the Type & Attribute selectors 2015-05-15 02:29:18 +02:00
iska 4f03e00003 Add a protocol for CSS Simple Selectors
Simple Selectors should conform to this protocol in order to be addable to the Simple Selector Sequence.
2015-05-15 02:28:03 +02:00
iska 62b385d9b4 Add test cases for changing selectors' properties for the Type & Attribute selectors 2015-05-14 21:42:53 +02:00
iska a7a30a7cbf Add convenience initializer for the Type Selector 2015-05-14 21:27:08 +02:00
iska 221b085fe5 Add tests for the Type & Attribute selectors 2015-05-14 21:18:55 +02:00
iska a2d9e65f0e Fix Attribute Selector for the Hyphen-case
Check for prefix instead of suffix with "-"
2015-05-14 21:16:29 +02:00
iska 745d0f72a7 Add convenience methods to create Class & ID Attribute Selectors
http://www.w3.org/TR/css3-selectors/#class-html
http://www.w3.org/TR/css3-selectors/#id-selectors
2015-05-14 21:15:39 +02:00
iska c401c3ca2c Add umbrella header for all CSS Selectors 2015-05-14 17:09:17 +02:00
iska 3cb5ed9a42 Add implementation for a CSS Attribute Selector
http://www.w3.org/TR/css3-selectors/#attribute-selectors
2015-05-14 16:32:47 +02:00
iska acec99ffea Add implementation for a CSS Type Selector
Matches an Element with the given tag name or all Elements (universal selector)
http://www.w3.org/TR/css3-selectors/#type-selectors
2015-05-14 16:23:23 +02:00
iska 7c58268dfd Add initial implementation for the CSS Selector base class 2015-05-14 16:21:26 +02:00
iska c87076b470 Add a NSCharacterSet category for HTML Whitespace characters
http://www.w3.org/TR/css3-selectors/#whitespace
2015-05-14 16:19:31 +02:00
iska 81a20f0333 Add HTML Node Filter protocol
https://dom.spec.whatwg.org/#interface-nodefilter
2015-05-14 16:15:41 +02:00
iska 4fcaf4c810 Add implementation for a CSS Selector Tokenizer
The Tokenizer is based on a flex-generated lexer for the grammar defined in the CSS Selectors Level 3 Spec
http://www.w3.org/TR/css3-selectors/#lex
2015-05-09 18:58:50 +02:00
iska 2c9f68e2ea Add Node methods to prepend nodes 2015-05-02 21:08:21 +02:00
iska 3d3e4d52b4 Add a NO_DOM_CHECKS macro to disable validations for DOM manipulations
DOM validation checks are expensive. This macro would allow for removing these checks from the compiled
product, which would increase performance by about 20-30%. However, the user is responsible for the validity
of the manipulations and the resulting DOM.
2015-05-02 20:52:03 +02:00
iska bf061d6367 Add support for inserting a Document Fragment
Previous implementation would just insert the fragment as a child node, whereas it should insert its
child nodes.
2015-05-02 20:47:40 +02:00
iska 40baea8ece Move tests for Mutation Algorithms into separate class 2015-04-27 19:43:43 +02:00
iska a2c6f22495 Add comments for insertion and replacement tests 2015-04-25 22:31:47 +02:00
iska b2f1864c80 Add tests for validating the replacing child node within a Document 2015-04-25 22:31:12 +02:00
iska cffe1cc703 Fix validation for replacing a child node and refactor common logic into a block 2015-04-25 22:25:31 +02:00
iska bc6bbf1d63 Add tests for validation for inserting an Element & Document Type into a Document 2015-04-25 15:27:25 +02:00
iska 71aed89574 Fix validation for inserting a Document Fragment into a Document 2015-04-25 15:13:43 +02:00
iska 36960dceb3 Rename node parameter in the validation method of HTML Node for clarity 2015-04-25 13:21:08 +02:00
iska 3ddfa15b15 Add tests to validate the insertions of nodes into a given parent
These tests check the implementation of the Mutation Algorithms of the DOM Standard
https://dom.spec.whatwg.org/#mutation-algorithms
2015-04-25 13:20:15 +02:00
iska 071742fb27 Add default initializers for HTML Comment, Document Type & Document Fragment 2015-04-25 13:17:02 +02:00
iska 1bc896efc9 Add tests for HTML Node methods
Todo: Tests for negative cases, e.g. invalid manipulations that lead to hierarchy or not found errors.
2015-04-23 00:43:06 +02:00
iska 429f00adb6 Fix project settings after update to Xcode 6.3.1 2015-04-23 00:40:35 +02:00
iska d08ac2100e Fix the isEqualTo call in Parser
Use isEqual instead
2015-04-21 23:07:57 +02:00
iska 62a6336dce Merge branch 'release/0.1.0' into develop 2015-04-20 21:51:38 +02:00
184 changed files with 9160 additions and 511 deletions
+25 -6
View File
@@ -1,8 +1,12 @@
# OS X
.DS_Store
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
DerivedData
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
@@ -12,13 +16,28 @@ build/
*.perspectivev3
!default.perspectivev3
xcuserdata
## Other
*.xccheckout
profile
*.moved-aside
DerivedData
*.xcuserstate
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
*.ipa
# CocoaPods
Pods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
Pods/
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
Carthage/Checkouts
Carthage/Build
+401 -102
View File
@@ -7,82 +7,101 @@
objects = {
/* Begin PBXBuildFile section */
623406E11ADB04F9004677A3 /* HTMLTemplate.h in Headers */ = {isa = PBXBuildFile; fileRef = 623406DF1ADB04F9004677A3 /* HTMLTemplate.h */; };
620C877C1BD44CBE00FB3EEE /* CSSCompoundSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 620C877A1BD44CBE00FB3EEE /* CSSCompoundSelector.m */; };
620C877D1BD44CBE00FB3EEE /* CSSCompoundSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 620C877A1BD44CBE00FB3EEE /* CSSCompoundSelector.m */; };
620C877E1BD4519A00FB3EEE /* CSSPseudoFunctionSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 62ACB8DC1BC5C13E00E283C1 /* CSSPseudoFunctionSelector.m */; };
620C877F1BD4519A00FB3EEE /* CSSPseudoFunctionSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 62ACB8DC1BC5C13E00E283C1 /* CSSPseudoFunctionSelector.m */; };
620EE94A1BC46F2A0028ED34 /* CSSPseudoClassSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 620EE9481BC46F2A0028ED34 /* CSSPseudoClassSelector.m */; };
620EE94B1BC46F2A0028ED34 /* CSSPseudoClassSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 620EE9481BC46F2A0028ED34 /* CSSPseudoClassSelector.m */; };
62132E591C01F83200084175 /* CSSSelectorTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 62132E581C01F83200084175 /* CSSSelectorTest.m */; };
62132E5A1C01F83200084175 /* CSSSelectorTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 62132E581C01F83200084175 /* CSSSelectorTest.m */; };
62132E5C1C021FF200084175 /* css-tests in Resources */ = {isa = PBXBuildFile; fileRef = 62132E5B1C021FF200084175 /* css-tests */; };
62132E5D1C021FF200084175 /* css-tests in Resources */ = {isa = PBXBuildFile; fileRef = 62132E5B1C021FF200084175 /* css-tests */; };
621FBE5A1BDAD68700BC9555 /* CSSSelectorParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 621FBE591BDAD68700BC9555 /* CSSSelectorParserTests.m */; };
621FBE5B1BDAD68700BC9555 /* CSSSelectorParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 621FBE591BDAD68700BC9555 /* CSSSelectorParserTests.m */; };
621FBE5D1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 621FBE5C1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m */; };
621FBE5E1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 621FBE5C1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m */; };
622BF5821BBF2FD700363583 /* CSSSelectorParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 622BF5801BBF2FD700363583 /* CSSSelectorParser.m */; };
622BF5831BBF2FD700363583 /* CSSSelectorParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 622BF5801BBF2FD700363583 /* CSSSelectorParser.m */; };
623406E21ADB04F9004677A3 /* HTMLTemplate.m in Sources */ = {isa = PBXBuildFile; fileRef = 623406E01ADB04F9004677A3 /* HTMLTemplate.m */; };
623406E31ADB04F9004677A3 /* HTMLTemplate.m in Sources */ = {isa = PBXBuildFile; fileRef = 623406E01ADB04F9004677A3 /* HTMLTemplate.m */; };
623406E61ADB05AD004677A3 /* HTMLDocumentFragment.h in Headers */ = {isa = PBXBuildFile; fileRef = 623406E41ADB05AD004677A3 /* HTMLDocumentFragment.h */; };
623406E71ADB05AD004677A3 /* HTMLDocumentFragment.m in Sources */ = {isa = PBXBuildFile; fileRef = 623406E51ADB05AD004677A3 /* HTMLDocumentFragment.m */; };
623406E81ADB05AD004677A3 /* HTMLDocumentFragment.m in Sources */ = {isa = PBXBuildFile; fileRef = 623406E51ADB05AD004677A3 /* HTMLDocumentFragment.m */; };
623424881AB467B200726190 /* HTMLOrderedDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 623424861AB467B200726190 /* HTMLOrderedDictionary.h */; };
623424891AB467B200726190 /* HTMLOrderedDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 623424871AB467B200726190 /* HTMLOrderedDictionary.m */; };
6234248A1AB467B200726190 /* HTMLOrderedDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 623424871AB467B200726190 /* HTMLOrderedDictionary.m */; };
6234584E1A9D2FA4009BD491 /* HTMLNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 6234584C1A9D2FA4009BD491 /* HTMLNode.h */; };
6234584F1A9D2FA4009BD491 /* HTMLNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 6234584D1A9D2FA4009BD491 /* HTMLNode.m */; };
623458501A9D2FA4009BD491 /* HTMLNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 6234584D1A9D2FA4009BD491 /* HTMLNode.m */; };
6234BEEE1AABBF1400DEB15F /* HTMLStackOfOpenElements.h in Headers */ = {isa = PBXBuildFile; fileRef = 6234BEEC1AABBF1400DEB15F /* HTMLStackOfOpenElements.h */; };
6234BEEF1AABBF1400DEB15F /* HTMLStackOfOpenElements.m in Sources */ = {isa = PBXBuildFile; fileRef = 6234BEED1AABBF1400DEB15F /* HTMLStackOfOpenElements.m */; };
6234BEF01AABBF1400DEB15F /* HTMLStackOfOpenElements.m in Sources */ = {isa = PBXBuildFile; fileRef = 6234BEED1AABBF1400DEB15F /* HTMLStackOfOpenElements.m */; };
6234CF571ABF892400B6077D /* libHTMLKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14DE19C7834000AD0C32 /* libHTMLKit.a */; };
6235CE9B1AA509430026937B /* NSString+HTMLKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 6235CE991AA509430026937B /* NSString+HTMLKit.h */; };
6235CE9C1AA509430026937B /* NSString+HTMLKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 6235CE9A1AA509430026937B /* NSString+HTMLKit.m */; };
6235CE9D1AA509430026937B /* NSString+HTMLKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 6235CE9A1AA509430026937B /* NSString+HTMLKit.m */; };
6235CEA01AA5170A0026937B /* HTMLMarker.h in Headers */ = {isa = PBXBuildFile; fileRef = 6235CE9E1AA5170A0026937B /* HTMLMarker.h */; };
6235CEA11AA5170A0026937B /* HTMLMarker.m in Sources */ = {isa = PBXBuildFile; fileRef = 6235CE9F1AA5170A0026937B /* HTMLMarker.m */; };
6235CEA21AA5170A0026937B /* HTMLMarker.m in Sources */ = {isa = PBXBuildFile; fileRef = 6235CE9F1AA5170A0026937B /* HTMLMarker.m */; };
62362A3C1A9FA70400301989 /* HTMLText.h in Headers */ = {isa = PBXBuildFile; fileRef = 62362A3A1A9FA70400301989 /* HTMLText.h */; };
62362A3D1A9FA70400301989 /* HTMLText.m in Sources */ = {isa = PBXBuildFile; fileRef = 62362A3B1A9FA70400301989 /* HTMLText.m */; };
62362A3E1A9FA70400301989 /* HTMLText.m in Sources */ = {isa = PBXBuildFile; fileRef = 62362A3B1A9FA70400301989 /* HTMLText.m */; };
62363C3D1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h in Headers */ = {isa = PBXBuildFile; fileRef = 62363C3B1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h */; };
62363C3E1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */ = {isa = PBXBuildFile; fileRef = 62363C3C1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m */; };
62363C3F1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */ = {isa = PBXBuildFile; fileRef = 62363C3C1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m */; };
6236738E1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 6236738D1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m */; };
6236738F1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 6236738D1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m */; };
6238039F1AB63A8C008A53D0 /* HTMLEOFToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 6238039D1AB63A8C008A53D0 /* HTMLEOFToken.h */; };
623803A01AB63A8C008A53D0 /* HTMLEOFToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238039E1AB63A8C008A53D0 /* HTMLEOFToken.m */; };
623803A11AB63A8C008A53D0 /* HTMLEOFToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238039E1AB63A8C008A53D0 /* HTMLEOFToken.m */; };
623857911A9E772B003A45D9 /* HTMLDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = 6238578F1A9E772B003A45D9 /* HTMLDocument.h */; };
623857921A9E772B003A45D9 /* HTMLDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 623857901A9E772B003A45D9 /* HTMLDocument.m */; };
623857931A9E772B003A45D9 /* HTMLDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 623857901A9E772B003A45D9 /* HTMLDocument.m */; };
623857961A9E8606003A45D9 /* HTMLDocumentType.h in Headers */ = {isa = PBXBuildFile; fileRef = 623857941A9E8606003A45D9 /* HTMLDocumentType.h */; };
623857971A9E8606003A45D9 /* HTMLDocumentType.m in Sources */ = {isa = PBXBuildFile; fileRef = 623857951A9E8606003A45D9 /* HTMLDocumentType.m */; };
623857981A9E8606003A45D9 /* HTMLDocumentType.m in Sources */ = {isa = PBXBuildFile; fileRef = 623857951A9E8606003A45D9 /* HTMLDocumentType.m */; };
6238579B1A9E8934003A45D9 /* HTMLComment.h in Headers */ = {isa = PBXBuildFile; fileRef = 623857991A9E8934003A45D9 /* HTMLComment.h */; };
6238579C1A9E8934003A45D9 /* HTMLComment.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238579A1A9E8934003A45D9 /* HTMLComment.m */; };
6238579D1A9E8934003A45D9 /* HTMLComment.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238579A1A9E8934003A45D9 /* HTMLComment.m */; };
6238C9851AB8D6330006512E /* HTMLKitExceptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 6238C9831AB8D6330006512E /* HTMLKitExceptions.h */; };
6238C9861AB8D6330006512E /* HTMLKitExceptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238C9841AB8D6330006512E /* HTMLKitExceptions.m */; };
6238C9871AB8D6330006512E /* HTMLKitExceptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238C9841AB8D6330006512E /* HTMLKitExceptions.m */; };
623916C31AC707250066B4FE /* HTMLNodeTreeEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */; };
623916C41AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */; };
623916C51AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */; };
623916C71AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */; };
623916C81AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */; };
6238C9861AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238C9841AB8D6330006512E /* HTMLKitDOMExceptions.m */; };
6238C9871AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238C9841AB8D6330006512E /* HTMLKitDOMExceptions.m */; };
623916C71AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */; };
623916C81AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */; };
6239755A1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623975591AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m */; };
6239755B1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623975591AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m */; };
6239755E1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6239755D1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m */; };
6239755F1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6239755D1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m */; };
623CAF9D1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 623CAF9C1AD88BEA00E34C32 /* HTMLKitParserPerformance.m */; };
623CAF9E1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 623CAF9C1AD88BEA00E34C32 /* HTMLKitParserPerformance.m */; };
624493A619CCC54100BCDDF4 /* HTMLTokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493A419CCC54100BCDDF4 /* HTMLTokenizer.h */; };
624493A719CCC54100BCDDF4 /* HTMLTokenizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493A519CCC54100BCDDF4 /* HTMLTokenizer.m */; };
624493A819CCC54100BCDDF4 /* HTMLTokenizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493A519CCC54100BCDDF4 /* HTMLTokenizer.m */; };
624493AC19CD0CBE00BCDDF4 /* HTMLToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493AA19CD0CBE00BCDDF4 /* HTMLToken.h */; };
624493AD19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */; };
624493AE19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */; };
624AC8FF19FBF59800BD3C4A /* HTMLTokens.h in Headers */ = {isa = PBXBuildFile; fileRef = 624AC8FE19FBF4F700BD3C4A /* HTMLTokens.h */; };
624717191B22333200C11912 /* HTMLNodeTraversal.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717171B22333200C11912 /* HTMLNodeTraversal.m */; };
6247171A1B22333200C11912 /* HTMLNodeTraversal.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717171B22333200C11912 /* HTMLNodeTraversal.m */; };
6247171C1B2240B800C11912 /* HTMLTreeWalkerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247171B1B2240B800C11912 /* HTMLTreeWalkerTests.m */; };
6247171D1B2240B800C11912 /* HTMLTreeWalkerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247171B1B2240B800C11912 /* HTMLTreeWalkerTests.m */; };
624717B81B21FE5400B38302 /* HTMLNodeFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */; };
624717B91B21FE5400B38302 /* HTMLNodeFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */; };
624717BD1B22009200B38302 /* HTMLTreeWalker.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717BB1B22009200B38302 /* HTMLTreeWalker.m */; };
624717BE1B22009200B38302 /* HTMLTreeWalker.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717BB1B22009200B38302 /* HTMLTreeWalker.m */; };
6247A9441B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */; };
6247A9451B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */; };
624AB3171B0508AE00F3830D /* CSSTypeSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AB3161B0508AE00F3830D /* CSSTypeSelectorTests.m */; };
624AB3181B0508AE00F3830D /* CSSTypeSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AB3161B0508AE00F3830D /* CSSTypeSelectorTests.m */; };
624AB31A1B050A4D00F3830D /* CSSAttributeSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AB3191B050A4D00F3830D /* CSSAttributeSelectorTests.m */; };
624AB31B1B050A4D00F3830D /* CSSAttributeSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AB3191B050A4D00F3830D /* CSSAttributeSelectorTests.m */; };
624AC90119FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AC90019FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m */; };
624AC90219FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AC90019FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m */; };
624AC90519FBFE9A00BD3C4A /* html5lib-tests in Resources */ = {isa = PBXBuildFile; fileRef = 624AC90419FBFE8A00BD3C4A /* html5lib-tests */; };
624AC90619FBFEA000BD3C4A /* html5lib-tests in Resources */ = {isa = PBXBuildFile; fileRef = 624AC90419FBFE8A00BD3C4A /* html5lib-tests */; };
624AC90E19FC702E00BD3C4A /* HTML Standard.html in Resources */ = {isa = PBXBuildFile; fileRef = 624AC90D19FC702E00BD3C4A /* HTML Standard.html */; };
624AC90F19FC702E00BD3C4A /* HTML Standard.html in Resources */ = {isa = PBXBuildFile; fileRef = 624AC90D19FC702E00BD3C4A /* HTML Standard.html */; };
624B28B91B03DA820048D328 /* CSSTypeSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B28B71B03DA820048D328 /* CSSTypeSelector.m */; };
624B28BA1B03DA820048D328 /* CSSTypeSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B28B71B03DA820048D328 /* CSSTypeSelector.m */; };
624B28C01B0413200048D328 /* CSSAttributeSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B28BE1B0413200048D328 /* CSSAttributeSelector.m */; };
624B28C11B0413200048D328 /* CSSAttributeSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B28BE1B0413200048D328 /* CSSAttributeSelector.m */; };
624B28C51B04190D0048D328 /* NSCharacterSet+HTMLKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B28C31B04190D0048D328 /* NSCharacterSet+HTMLKit.m */; };
624B28C61B04190D0048D328 /* NSCharacterSet+HTMLKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B28C31B04190D0048D328 /* NSCharacterSet+HTMLKit.m */; };
624B371F1B24E9BA0010BDDF /* CSSInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B371D1B24E9BA0010BDDF /* CSSInputStream.m */; };
624B37201B24E9BA0010BDDF /* CSSInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B371D1B24E9BA0010BDDF /* CSSInputStream.m */; };
624B9FB21AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB11AE0313300646C4C /* HTMLKitStringCategoryTests.m */; };
624B9FB31AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB11AE0313300646C4C /* HTMLKitStringCategoryTests.m */; };
624B9FB51AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */; };
624B9FB61AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */; };
624FC37A1AE591D80015DDF9 /* HTMLKitNodesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */; };
624FC37B1AE591D80015DDF9 /* HTMLKitNodesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */; };
625A14B019C7829400AD0C32 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14AF19C7829400AD0C32 /* Cocoa.framework */; };
625A14BA19C7829400AD0C32 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 625A14B819C7829400AD0C32 /* InfoPlist.strings */; };
625A14BE19C7829400AD0C32 /* HTMLKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 625A14BD19C7829400AD0C32 /* HTMLKit.m */; };
625A14C519C7829400AD0C32 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14C419C7829400AD0C32 /* XCTest.framework */; };
625A14C619C7829400AD0C32 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14AF19C7829400AD0C32 /* Cocoa.framework */; };
625A14C919C7829400AD0C32 /* HTMLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14AC19C7829400AD0C32 /* HTMLKit.framework */; };
@@ -90,38 +109,102 @@
625A14E019C7834100AD0C32 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14DF19C7834100AD0C32 /* Foundation.framework */; };
625A14ED19C7834100AD0C32 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14C419C7829400AD0C32 /* XCTest.framework */; };
625A14EE19C7834100AD0C32 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14DF19C7834100AD0C32 /* Foundation.framework */; };
625A150219C783DE00AD0C32 /* HTMLKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 625A14BD19C7829400AD0C32 /* HTMLKit.m */; };
625A150319C783E200AD0C32 /* HTMLKit.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 625A14BC19C7829400AD0C32 /* HTMLKit.h */; };
625A150419C783EB00AD0C32 /* HTMLKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 625A14BC19C7829400AD0C32 /* HTMLKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
625A150819C78ABA00AD0C32 /* HTMLInputStreamReader.h in Headers */ = {isa = PBXBuildFile; fileRef = 625A150619C78ABA00AD0C32 /* HTMLInputStreamReader.h */; };
625A150919C78ABA00AD0C32 /* HTMLInputStreamReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 625A150719C78ABA00AD0C32 /* HTMLInputStreamReader.m */; };
625A150A19C78ABA00AD0C32 /* HTMLInputStreamReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 625A150719C78ABA00AD0C32 /* HTMLInputStreamReader.m */; };
6279F87419E1808D00F12EE5 /* HTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 6279F87219E1808D00F12EE5 /* HTMLElement.h */; };
626652F91C03D7AC00C3F121 /* HTMLKitErrorDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = 626652F81C03D30F00C3F121 /* HTMLKitErrorDomain.h */; settings = {ATTRIBUTES = (Public, ); }; };
6279F87519E1808D00F12EE5 /* HTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 6279F87319E1808D00F12EE5 /* HTMLElement.m */; };
6279F87619E1808D00F12EE5 /* HTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 6279F87319E1808D00F12EE5 /* HTMLElement.m */; };
62AE593519F97D880043F069 /* HTMLParseErrorToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE593319F97D880043F069 /* HTMLParseErrorToken.h */; };
628AF62F1BC99A6C00496128 /* CSSNthExpressionsParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 628AF62E1BC99A6C00496128 /* CSSNthExpressionsParserTests.m */; };
628AF6301BC99A6C00496128 /* CSSNthExpressionsParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 628AF62E1BC99A6C00496128 /* CSSNthExpressionsParserTests.m */; };
628AF6341BC9A63D00496128 /* CSSNthExpressionSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 628AF6321BC9A63D00496128 /* CSSNthExpressionSelector.m */; };
628AF6351BC9A63D00496128 /* CSSNthExpressionSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 628AF6321BC9A63D00496128 /* CSSNthExpressionSelector.m */; };
628AF63F1BC9D6FB00496128 /* CSSStructuralPseudoSelectors.m in Sources */ = {isa = PBXBuildFile; fileRef = 628AF63D1BC9D6FB00496128 /* CSSStructuralPseudoSelectors.m */; };
628AF6401BC9D6FB00496128 /* CSSStructuralPseudoSelectors.m in Sources */ = {isa = PBXBuildFile; fileRef = 628AF63D1BC9D6FB00496128 /* CSSStructuralPseudoSelectors.m */; };
629AA0EA1BDD9C2900D5CC98 /* HTMLKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 625A14BC19C7829400AD0C32 /* HTMLKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0EB1BDD9C2900D5CC98 /* HTMLParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 62F873E919E088C90062683C /* HTMLParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0EC1BDD9C2900D5CC98 /* HTMLDOM.h in Headers */ = {isa = PBXBuildFile; fileRef = 62362A3F1A9FDE8A00301989 /* HTMLDOM.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0ED1BDD9C2900D5CC98 /* HTMLNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 6234584C1A9D2FA4009BD491 /* HTMLNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0EE1BDD9C2900D5CC98 /* HTMLDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = 6238578F1A9E772B003A45D9 /* HTMLDocument.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0EF1BDD9C2900D5CC98 /* HTMLDocumentType.h in Headers */ = {isa = PBXBuildFile; fileRef = 623857941A9E8606003A45D9 /* HTMLDocumentType.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0F01BDD9C2900D5CC98 /* HTMLDocumentFragment.h in Headers */ = {isa = PBXBuildFile; fileRef = 623406E41ADB05AD004677A3 /* HTMLDocumentFragment.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0F11BDD9C2900D5CC98 /* HTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 6279F87219E1808D00F12EE5 /* HTMLElement.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0F21BDD9C2900D5CC98 /* HTMLComment.h in Headers */ = {isa = PBXBuildFile; fileRef = 623857991A9E8934003A45D9 /* HTMLComment.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0F31BDD9C2900D5CC98 /* HTMLText.h in Headers */ = {isa = PBXBuildFile; fileRef = 62362A3A1A9FA70400301989 /* HTMLText.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0F41BDD9C2A00D5CC98 /* HTMLTemplate.h in Headers */ = {isa = PBXBuildFile; fileRef = 623406DF1ADB04F9004677A3 /* HTMLTemplate.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0F51BDD9C2A00D5CC98 /* HTMLNodeIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0F61BDD9C2A00D5CC98 /* HTMLTreeWalker.h in Headers */ = {isa = PBXBuildFile; fileRef = 624717BA1B22009200B38302 /* HTMLTreeWalker.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0F71BDD9C2A00D5CC98 /* HTMLNodeTraversal.h in Headers */ = {isa = PBXBuildFile; fileRef = 624717161B22333200C11912 /* HTMLNodeTraversal.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0F81BDD9C2A00D5CC98 /* HTMLNodeFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0F91BDD9C2A00D5CC98 /* HTMLKitDOMExceptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 6238C9831AB8D6330006512E /* HTMLKitDOMExceptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0FA1BDD9C2A00D5CC98 /* HTMLNamespaces.h in Headers */ = {isa = PBXBuildFile; fileRef = 628B7CE61A080E1000602C87 /* HTMLNamespaces.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0FB1BDD9C2A00D5CC98 /* HTMLQuirksMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 623719431AA12EE8002E03C8 /* HTMLQuirksMode.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0FC1BDD9C2A00D5CC98 /* NSString+HTMLKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 6235CE991AA509430026937B /* NSString+HTMLKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0FD1BDD9C2A00D5CC98 /* NSCharacterSet+HTMLKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 624B28C21B04190D0048D328 /* NSCharacterSet+HTMLKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0FE1BDD9C2A00D5CC98 /* HTMLOrderedDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 623424861AB467B200726190 /* HTMLOrderedDictionary.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA0FF1BDD9C2A00D5CC98 /* CSSSelectors.h in Headers */ = {isa = PBXBuildFile; fileRef = 624AB3151B04EA4200F3830D /* CSSSelectors.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA1001BDD9C2A00D5CC98 /* CSSSelectorParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 622BF57F1BBF2FD700363583 /* CSSSelectorParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA1011BDD9C2A00D5CC98 /* CSSSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 62079BE71AF56F1E00D3B402 /* CSSSelector.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA1021BDD9C2B00D5CC98 /* CSSSelectorBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = 62FA98061BD5A7E200A2AF86 /* CSSSelectorBlock.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA1031BDD9C2B00D5CC98 /* CSSTypeSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 624B28B61B03DA820048D328 /* CSSTypeSelector.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA1041BDD9C2B00D5CC98 /* CSSAttributeSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 624B28BD1B0413200048D328 /* CSSAttributeSelector.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA1051BDD9C2B00D5CC98 /* CSSPseudoClassSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 620EE9471BC46F2A0028ED34 /* CSSPseudoClassSelector.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA1061BDD9C2B00D5CC98 /* CSSStructuralPseudoSelectors.h in Headers */ = {isa = PBXBuildFile; fileRef = 628AF63C1BC9D6FB00496128 /* CSSStructuralPseudoSelectors.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA1071BDD9C2B00D5CC98 /* CSSNthExpressionSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 628AF6311BC9A63D00496128 /* CSSNthExpressionSelector.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA1081BDD9C2B00D5CC98 /* CSSPseudoFunctionSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 62ACB8DB1BC5C13E00E283C1 /* CSSPseudoFunctionSelector.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA1091BDD9C2B00D5CC98 /* CSSCombinatorSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 62FA04E11BCC360D009ABF98 /* CSSCombinatorSelector.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA10A1BDD9C2B00D5CC98 /* CSSCompoundSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 620C87791BD44CBE00FB3EEE /* CSSCompoundSelector.h */; settings = {ATTRIBUTES = (Public, ); }; };
629AA10B1BDD9C5200D5CC98 /* HTMLTokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493A419CCC54100BCDDF4 /* HTMLTokenizer.h */; };
629AA10C1BDD9C5200D5CC98 /* HTMLTokenizerStates.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493A919CCE84A00BCDDF4 /* HTMLTokenizerStates.h */; };
629AA10D1BDD9C5300D5CC98 /* HTMLTokenizerCharacters.h in Headers */ = {isa = PBXBuildFile; fileRef = 62E7CAAE19CDFFB500465A83 /* HTMLTokenizerCharacters.h */; };
629AA10E1BDD9C5300D5CC98 /* HTMLTokenizerEntities.h in Headers */ = {isa = PBXBuildFile; fileRef = 62F31FDB19E9DCCF007F0657 /* HTMLTokenizerEntities.h */; };
629AA10F1BDD9C5300D5CC98 /* HTMLTokens.h in Headers */ = {isa = PBXBuildFile; fileRef = 624AC8FE19FBF4F700BD3C4A /* HTMLTokens.h */; };
629AA1101BDD9C5300D5CC98 /* HTMLToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493AA19CD0CBE00BCDDF4 /* HTMLToken.h */; };
629AA1111BDD9C5300D5CC98 /* HTMLCharacterToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE594719F9948A0043F069 /* HTMLCharacterToken.h */; };
629AA1121BDD9C5300D5CC98 /* HTMLCommentToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE594219F992F30043F069 /* HTMLCommentToken.h */; };
629AA1131BDD9C5300D5CC98 /* HTMLDOCTYPEToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE593819F97E1C0043F069 /* HTMLDOCTYPEToken.h */; };
629AA1141BDD9C5400D5CC98 /* HTMLParseErrorToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE593319F97D880043F069 /* HTMLParseErrorToken.h */; };
629AA1151BDD9C5400D5CC98 /* HTMLTagToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE593D19F9907C0043F069 /* HTMLTagToken.h */; };
629AA1161BDD9C5400D5CC98 /* HTMLEOFToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 6238039D1AB63A8C008A53D0 /* HTMLEOFToken.h */; };
629AA1171BDD9C5400D5CC98 /* HTMLStackOfOpenElements.h in Headers */ = {isa = PBXBuildFile; fileRef = 6234BEEC1AABBF1400DEB15F /* HTMLStackOfOpenElements.h */; };
629AA1181BDD9C5400D5CC98 /* HTMLListOfActiveFormattingElements.h in Headers */ = {isa = PBXBuildFile; fileRef = 62363C3B1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h */; };
629AA1191BDD9C5400D5CC98 /* HTMLElementTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 6223211D1A969B9300BACED5 /* HTMLElementTypes.h */; };
629AA11A1BDD9C5400D5CC98 /* HTMLElementAdjustment.h in Headers */ = {isa = PBXBuildFile; fileRef = 6234C3361AB3BF710046F527 /* HTMLElementAdjustment.h */; };
629AA11B1BDD9C5500D5CC98 /* HTMLParserInsertionModes.h in Headers */ = {isa = PBXBuildFile; fileRef = 6279F87119E17DC700F12EE5 /* HTMLParserInsertionModes.h */; };
629AA11C1BDD9C5500D5CC98 /* HTMLMarker.h in Headers */ = {isa = PBXBuildFile; fileRef = 6235CE9E1AA5170A0026937B /* HTMLMarker.h */; };
629AA11D1BDD9C5500D5CC98 /* CSSCodePoints.h in Headers */ = {isa = PBXBuildFile; fileRef = 624B37211B250BB50010BDDF /* CSSCodePoints.h */; };
629AA11E1BDD9C5500D5CC98 /* CSSInputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 624B371C1B24E9BA0010BDDF /* CSSInputStream.h */; };
629AA11F1BDD9C5E00D5CC98 /* CSSNthExpressionParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 62F501631BC9576B0069F47B /* CSSNthExpressionParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
62AE593619F97D880043F069 /* HTMLParseErrorToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE593419F97D880043F069 /* HTMLParseErrorToken.m */; };
62AE593719F97D880043F069 /* HTMLParseErrorToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE593419F97D880043F069 /* HTMLParseErrorToken.m */; };
62AE593A19F97E1C0043F069 /* HTMLDOCTYPEToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE593819F97E1C0043F069 /* HTMLDOCTYPEToken.h */; };
62AE593B19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE593919F97E1C0043F069 /* HTMLDOCTYPEToken.m */; };
62AE593C19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE593919F97E1C0043F069 /* HTMLDOCTYPEToken.m */; };
62AE593F19F9907C0043F069 /* HTMLTagToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE593D19F9907C0043F069 /* HTMLTagToken.h */; };
62AE594019F9907C0043F069 /* HTMLTagToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE593E19F9907C0043F069 /* HTMLTagToken.m */; };
62AE594119F9907C0043F069 /* HTMLTagToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE593E19F9907C0043F069 /* HTMLTagToken.m */; };
62AE594419F992F30043F069 /* HTMLCommentToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE594219F992F30043F069 /* HTMLCommentToken.h */; };
62AE594519F992F30043F069 /* HTMLCommentToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE594319F992F30043F069 /* HTMLCommentToken.m */; };
62AE594619F992F30043F069 /* HTMLCommentToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE594319F992F30043F069 /* HTMLCommentToken.m */; };
62AE594919F9948A0043F069 /* HTMLCharacterToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE594719F9948A0043F069 /* HTMLCharacterToken.h */; };
62AE594A19F9948A0043F069 /* HTMLCharacterToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE594819F9948A0043F069 /* HTMLCharacterToken.m */; };
62AE594B19F9948A0043F069 /* HTMLCharacterToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE594819F9948A0043F069 /* HTMLCharacterToken.m */; };
62D8345919FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 62D8345819FB1AC4009205A9 /* HTML5LibTokenizerTest.m */; };
62D8345A19FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 62D8345819FB1AC4009205A9 /* HTML5LibTokenizerTest.m */; };
62F31FDD19E9DCCF007F0657 /* HTMLTokenizerEntities.h in Headers */ = {isa = PBXBuildFile; fileRef = 62F31FDB19E9DCCF007F0657 /* HTMLTokenizerEntities.h */; };
62EC7AE61AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62EC7AE51AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m */; };
62EC7AE71AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62EC7AE51AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m */; };
62F31FDE19E9DCCF007F0657 /* HTMLTokenizerEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F31FDC19E9DCCF007F0657 /* HTMLTokenizerEntities.m */; };
62F31FDF19E9DCCF007F0657 /* HTMLTokenizerEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F31FDC19E9DCCF007F0657 /* HTMLTokenizerEntities.m */; };
62F873EB19E088C90062683C /* HTMLParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 62F873E919E088C90062683C /* HTMLParser.h */; };
62F501661BC9576B0069F47B /* CSSNthExpressionParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F501641BC9576B0069F47B /* CSSNthExpressionParser.m */; };
62F501671BC9576B0069F47B /* CSSNthExpressionParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F501641BC9576B0069F47B /* CSSNthExpressionParser.m */; };
62F658701BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F6586F1BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m */; };
62F658711BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F6586F1BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m */; };
62F873EC19E088C90062683C /* HTMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F873EA19E088C90062683C /* HTMLParser.m */; };
62F873ED19E088C90062683C /* HTMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F873EA19E088C90062683C /* HTMLParser.m */; };
62FA04E41BCC360D009ABF98 /* CSSCombinatorSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FA04E21BCC360D009ABF98 /* CSSCombinatorSelector.m */; };
62FA04E51BCC360D009ABF98 /* CSSCombinatorSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FA04E21BCC360D009ABF98 /* CSSCombinatorSelector.m */; };
62FA98041BD57B6200A2AF86 /* CSSSelectors.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FA98031BD57B6200A2AF86 /* CSSSelectors.m */; };
62FA98051BD57B6200A2AF86 /* CSSSelectors.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FA98031BD57B6200A2AF86 /* CSSSelectors.m */; };
62FA98091BD5A7E200A2AF86 /* CSSSelectorBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FA98071BD5A7E200A2AF86 /* CSSSelectorBlock.m */; };
62FA980A1BD5A7E200A2AF86 /* CSSSelectorBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FA98071BD5A7E200A2AF86 /* CSSSelectorBlock.m */; };
62FC60461BD0519B0042BBE7 /* CSSSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FC60451BD0519B0042BBE7 /* CSSSelector.m */; };
62FC60471BD0519B0042BBE7 /* CSSSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FC60451BD0519B0042BBE7 /* CSSSelector.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -155,7 +238,19 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
62079BE71AF56F1E00D3B402 /* CSSSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSSelector.h; sourceTree = "<group>"; };
620C87791BD44CBE00FB3EEE /* CSSCompoundSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSCompoundSelector.h; sourceTree = "<group>"; };
620C877A1BD44CBE00FB3EEE /* CSSCompoundSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSCompoundSelector.m; sourceTree = "<group>"; };
620EE9471BC46F2A0028ED34 /* CSSPseudoClassSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSPseudoClassSelector.h; sourceTree = "<group>"; };
620EE9481BC46F2A0028ED34 /* CSSPseudoClassSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSPseudoClassSelector.m; sourceTree = "<group>"; };
62132E571C01F83200084175 /* CSSSelectorTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSSelectorTest.h; sourceTree = "<group>"; };
62132E581C01F83200084175 /* CSSSelectorTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSSelectorTest.m; sourceTree = "<group>"; };
62132E5B1C021FF200084175 /* css-tests */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "css-tests"; sourceTree = "<group>"; };
621FBE591BDAD68700BC9555 /* CSSSelectorParserTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSSelectorParserTests.m; sourceTree = "<group>"; };
621FBE5C1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSCombinatorSelectorTests.m; sourceTree = "<group>"; };
6223211D1A969B9300BACED5 /* HTMLElementTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLElementTypes.h; sourceTree = "<group>"; };
622BF57F1BBF2FD700363583 /* CSSSelectorParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSSelectorParser.h; sourceTree = "<group>"; };
622BF5801BBF2FD700363583 /* CSSSelectorParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSSelectorParser.m; sourceTree = "<group>"; };
623406DF1ADB04F9004677A3 /* HTMLTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLTemplate.h; sourceTree = "<group>"; };
623406E01ADB04F9004677A3 /* HTMLTemplate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLTemplate.m; sourceTree = "<group>"; };
623406E41ADB05AD004677A3 /* HTMLDocumentFragment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLDocumentFragment.h; sourceTree = "<group>"; };
@@ -173,7 +268,7 @@
6235CE9F1AA5170A0026937B /* HTMLMarker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLMarker.m; sourceTree = "<group>"; };
62362A3A1A9FA70400301989 /* HTMLText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLText.h; sourceTree = "<group>"; };
62362A3B1A9FA70400301989 /* HTMLText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLText.m; sourceTree = "<group>"; };
62362A3F1A9FDE8A00301989 /* HTMLNodes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLNodes.h; sourceTree = "<group>"; };
62362A3F1A9FDE8A00301989 /* HTMLDOM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLDOM.h; sourceTree = "<group>"; };
62363C3B1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLListOfActiveFormattingElements.h; sourceTree = "<group>"; };
62363C3C1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLListOfActiveFormattingElements.m; sourceTree = "<group>"; };
6236738D1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitTokenizerPerformance.m; sourceTree = "<group>"; };
@@ -186,11 +281,9 @@
623857951A9E8606003A45D9 /* HTMLDocumentType.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLDocumentType.m; sourceTree = "<group>"; };
623857991A9E8934003A45D9 /* HTMLComment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLComment.h; sourceTree = "<group>"; };
6238579A1A9E8934003A45D9 /* HTMLComment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLComment.m; sourceTree = "<group>"; };
6238C9831AB8D6330006512E /* HTMLKitExceptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLKitExceptions.h; sourceTree = "<group>"; };
6238C9841AB8D6330006512E /* HTMLKitExceptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitExceptions.m; sourceTree = "<group>"; };
623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeTreeEnumerator.h; sourceTree = "<group>"; };
623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeTreeEnumerator.m; sourceTree = "<group>"; };
623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitNodeTreeEnumratorTests.m; sourceTree = "<group>"; };
6238C9831AB8D6330006512E /* HTMLKitDOMExceptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLKitDOMExceptions.h; sourceTree = "<group>"; };
6238C9841AB8D6330006512E /* HTMLKitDOMExceptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitDOMExceptions.m; sourceTree = "<group>"; };
623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitNodeIteratorTests.m; sourceTree = "<group>"; };
623975591AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitTreeConstructionTests.m; sourceTree = "<group>"; };
6239755C1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTML5LibTreeConstructionTest.h; sourceTree = "<group>"; };
6239755D1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTML5LibTreeConstructionTest.m; sourceTree = "<group>"; };
@@ -200,12 +293,34 @@
624493A919CCE84A00BCDDF4 /* HTMLTokenizerStates.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLTokenizerStates.h; sourceTree = "<group>"; };
624493AA19CD0CBE00BCDDF4 /* HTMLToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLToken.h; sourceTree = "<group>"; };
624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLToken.m; sourceTree = "<group>"; };
624717161B22333200C11912 /* HTMLNodeTraversal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeTraversal.h; sourceTree = "<group>"; };
624717171B22333200C11912 /* HTMLNodeTraversal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeTraversal.m; sourceTree = "<group>"; };
6247171B1B2240B800C11912 /* HTMLTreeWalkerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLTreeWalkerTests.m; sourceTree = "<group>"; };
624717B71B21FE5400B38302 /* HTMLNodeFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeFilter.m; sourceTree = "<group>"; };
624717BA1B22009200B38302 /* HTMLTreeWalker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLTreeWalker.h; sourceTree = "<group>"; };
624717BB1B22009200B38302 /* HTMLTreeWalker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLTreeWalker.m; sourceTree = "<group>"; };
6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeIterator.h; sourceTree = "<group>"; };
6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeIterator.m; sourceTree = "<group>"; };
6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeFilter.h; sourceTree = "<group>"; };
624AB3151B04EA4200F3830D /* CSSSelectors.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSSSelectors.h; sourceTree = "<group>"; };
624AB3161B0508AE00F3830D /* CSSTypeSelectorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSTypeSelectorTests.m; sourceTree = "<group>"; };
624AB3191B050A4D00F3830D /* CSSAttributeSelectorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSAttributeSelectorTests.m; sourceTree = "<group>"; };
624AC8FE19FBF4F700BD3C4A /* HTMLTokens.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLTokens.h; sourceTree = "<group>"; };
624AC90019FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitTokenizerTests.m; sourceTree = "<group>"; };
624AC90419FBFE8A00BD3C4A /* html5lib-tests */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "html5lib-tests"; sourceTree = "<group>"; };
624AC90D19FC702E00BD3C4A /* HTML Standard.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "HTML Standard.html"; sourceTree = "<group>"; };
624B28B61B03DA820048D328 /* CSSTypeSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSTypeSelector.h; sourceTree = "<group>"; };
624B28B71B03DA820048D328 /* CSSTypeSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSTypeSelector.m; sourceTree = "<group>"; };
624B28BD1B0413200048D328 /* CSSAttributeSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSAttributeSelector.h; sourceTree = "<group>"; };
624B28BE1B0413200048D328 /* CSSAttributeSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSAttributeSelector.m; sourceTree = "<group>"; };
624B28C21B04190D0048D328 /* NSCharacterSet+HTMLKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSCharacterSet+HTMLKit.h"; sourceTree = "<group>"; };
624B28C31B04190D0048D328 /* NSCharacterSet+HTMLKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSCharacterSet+HTMLKit.m"; sourceTree = "<group>"; };
624B371C1B24E9BA0010BDDF /* CSSInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSInputStream.h; sourceTree = "<group>"; };
624B371D1B24E9BA0010BDDF /* CSSInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSInputStream.m; sourceTree = "<group>"; };
624B37211B250BB50010BDDF /* CSSCodePoints.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSSCodePoints.h; sourceTree = "<group>"; };
624B9FB11AE0313300646C4C /* HTMLKitStringCategoryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitStringCategoryTests.m; sourceTree = "<group>"; };
624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitOrderedDictionaryTests.m; sourceTree = "<group>"; };
624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitNodesTests.m; sourceTree = "<group>"; };
625A14AC19C7829400AD0C32 /* HTMLKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = HTMLKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
625A14AF19C7829400AD0C32 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
625A14B219C7829400AD0C32 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
@@ -215,7 +330,6 @@
625A14B919C7829400AD0C32 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
625A14BB19C7829400AD0C32 /* HTMLKit-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "HTMLKit-Prefix.pch"; sourceTree = "<group>"; };
625A14BC19C7829400AD0C32 /* HTMLKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLKit.h; sourceTree = "<group>"; };
625A14BD19C7829400AD0C32 /* HTMLKit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HTMLKit.m; sourceTree = "<group>"; };
625A14C319C7829400AD0C32 /* HTMLKitFrameworkTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HTMLKitFrameworkTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
625A14C419C7829400AD0C32 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
625A14CC19C7829400AD0C32 /* HTMLKitTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "HTMLKitTests-Info.plist"; sourceTree = "<group>"; };
@@ -225,10 +339,18 @@
625A14EC19C7834100AD0C32 /* HTMLKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HTMLKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
625A150619C78ABA00AD0C32 /* HTMLInputStreamReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLInputStreamReader.h; sourceTree = "<group>"; };
625A150719C78ABA00AD0C32 /* HTMLInputStreamReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLInputStreamReader.m; sourceTree = "<group>"; };
626652F81C03D30F00C3F121 /* HTMLKitErrorDomain.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLKitErrorDomain.h; sourceTree = "<group>"; };
6279F87119E17DC700F12EE5 /* HTMLParserInsertionModes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLParserInsertionModes.h; sourceTree = "<group>"; };
6279F87219E1808D00F12EE5 /* HTMLElement.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.objc; fileEncoding = 4; path = HTMLElement.h; sourceTree = "<group>"; };
6279F87319E1808D00F12EE5 /* HTMLElement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLElement.m; sourceTree = "<group>"; };
628AF62E1BC99A6C00496128 /* CSSNthExpressionsParserTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSNthExpressionsParserTests.m; sourceTree = "<group>"; };
628AF6311BC9A63D00496128 /* CSSNthExpressionSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSNthExpressionSelector.h; sourceTree = "<group>"; };
628AF6321BC9A63D00496128 /* CSSNthExpressionSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSNthExpressionSelector.m; sourceTree = "<group>"; };
628AF63C1BC9D6FB00496128 /* CSSStructuralPseudoSelectors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSStructuralPseudoSelectors.h; sourceTree = "<group>"; };
628AF63D1BC9D6FB00496128 /* CSSStructuralPseudoSelectors.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSStructuralPseudoSelectors.m; sourceTree = "<group>"; };
628B7CE61A080E1000602C87 /* HTMLNamespaces.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLNamespaces.h; sourceTree = "<group>"; };
62ACB8DB1BC5C13E00E283C1 /* CSSPseudoFunctionSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSPseudoFunctionSelector.h; sourceTree = "<group>"; };
62ACB8DC1BC5C13E00E283C1 /* CSSPseudoFunctionSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSPseudoFunctionSelector.m; sourceTree = "<group>"; };
62AE593319F97D880043F069 /* HTMLParseErrorToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLParseErrorToken.h; sourceTree = "<group>"; };
62AE593419F97D880043F069 /* HTMLParseErrorToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLParseErrorToken.m; sourceTree = "<group>"; };
62AE593819F97E1C0043F069 /* HTMLDOCTYPEToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLDOCTYPEToken.h; sourceTree = "<group>"; };
@@ -242,10 +364,20 @@
62D8345719FB1AC4009205A9 /* HTML5LibTokenizerTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTML5LibTokenizerTest.h; sourceTree = "<group>"; };
62D8345819FB1AC4009205A9 /* HTML5LibTokenizerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTML5LibTokenizerTest.m; sourceTree = "<group>"; };
62E7CAAE19CDFFB500465A83 /* HTMLTokenizerCharacters.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLTokenizerCharacters.h; sourceTree = "<group>"; };
62EC7AE51AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitMutationAlgorithmsTests.m; sourceTree = "<group>"; };
62F31FDB19E9DCCF007F0657 /* HTMLTokenizerEntities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLTokenizerEntities.h; sourceTree = "<group>"; };
62F31FDC19E9DCCF007F0657 /* HTMLTokenizerEntities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLTokenizerEntities.m; sourceTree = "<group>"; };
62F501631BC9576B0069F47B /* CSSNthExpressionParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSNthExpressionParser.h; sourceTree = "<group>"; };
62F501641BC9576B0069F47B /* CSSNthExpressionParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSNthExpressionParser.m; sourceTree = "<group>"; };
62F6586F1BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSNThExpressionSelectorTests.m; sourceTree = "<group>"; };
62F873E919E088C90062683C /* HTMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLParser.h; sourceTree = "<group>"; };
62F873EA19E088C90062683C /* HTMLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLParser.m; sourceTree = "<group>"; };
62FA04E11BCC360D009ABF98 /* CSSCombinatorSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSCombinatorSelector.h; sourceTree = "<group>"; };
62FA04E21BCC360D009ABF98 /* CSSCombinatorSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSCombinatorSelector.m; sourceTree = "<group>"; };
62FA98031BD57B6200A2AF86 /* CSSSelectors.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSSelectors.m; sourceTree = "<group>"; };
62FA98061BD5A7E200A2AF86 /* CSSSelectorBlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSSelectorBlock.h; sourceTree = "<group>"; };
62FA98071BD5A7E200A2AF86 /* CSSSelectorBlock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSSelectorBlock.m; sourceTree = "<group>"; };
62FC60451BD0519B0042BBE7 /* CSSSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSSelector.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -288,6 +420,42 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
62079BE61AF5656600D3B402 /* Selectors */ = {
isa = PBXGroup;
children = (
624AB3151B04EA4200F3830D /* CSSSelectors.h */,
62FA98031BD57B6200A2AF86 /* CSSSelectors.m */,
624B37211B250BB50010BDDF /* CSSCodePoints.h */,
624B371C1B24E9BA0010BDDF /* CSSInputStream.h */,
624B371D1B24E9BA0010BDDF /* CSSInputStream.m */,
622BF57F1BBF2FD700363583 /* CSSSelectorParser.h */,
622BF5801BBF2FD700363583 /* CSSSelectorParser.m */,
62F501631BC9576B0069F47B /* CSSNthExpressionParser.h */,
62F501641BC9576B0069F47B /* CSSNthExpressionParser.m */,
62079BE71AF56F1E00D3B402 /* CSSSelector.h */,
62FC60451BD0519B0042BBE7 /* CSSSelector.m */,
62FA98061BD5A7E200A2AF86 /* CSSSelectorBlock.h */,
62FA98071BD5A7E200A2AF86 /* CSSSelectorBlock.m */,
624B28B61B03DA820048D328 /* CSSTypeSelector.h */,
624B28B71B03DA820048D328 /* CSSTypeSelector.m */,
624B28BD1B0413200048D328 /* CSSAttributeSelector.h */,
624B28BE1B0413200048D328 /* CSSAttributeSelector.m */,
620EE9471BC46F2A0028ED34 /* CSSPseudoClassSelector.h */,
620EE9481BC46F2A0028ED34 /* CSSPseudoClassSelector.m */,
628AF63C1BC9D6FB00496128 /* CSSStructuralPseudoSelectors.h */,
628AF63D1BC9D6FB00496128 /* CSSStructuralPseudoSelectors.m */,
628AF6311BC9A63D00496128 /* CSSNthExpressionSelector.h */,
628AF6321BC9A63D00496128 /* CSSNthExpressionSelector.m */,
62ACB8DB1BC5C13E00E283C1 /* CSSPseudoFunctionSelector.h */,
62ACB8DC1BC5C13E00E283C1 /* CSSPseudoFunctionSelector.m */,
62FA04E11BCC360D009ABF98 /* CSSCombinatorSelector.h */,
62FA04E21BCC360D009ABF98 /* CSSCombinatorSelector.m */,
620C87791BD44CBE00FB3EEE /* CSSCompoundSelector.h */,
620C877A1BD44CBE00FB3EEE /* CSSCompoundSelector.m */,
);
name = Selectors;
sourceTree = "<group>";
};
6236738C1AC0CD2400FF89B3 /* Tokenizer */ = {
isa = PBXGroup;
children = (
@@ -299,31 +467,40 @@
name = Tokenizer;
sourceTree = "<group>";
};
623719441AA1472B002E03C8 /* Nodes */ = {
623719441AA1472B002E03C8 /* DOM */ = {
isa = PBXGroup;
children = (
623719431AA12EE8002E03C8 /* HTMLQuirksMode.h */,
62362A3F1A9FDE8A00301989 /* HTMLNodes.h */,
62362A3F1A9FDE8A00301989 /* HTMLDOM.h */,
6234584C1A9D2FA4009BD491 /* HTMLNode.h */,
6234584D1A9D2FA4009BD491 /* HTMLNode.m */,
6279F87219E1808D00F12EE5 /* HTMLElement.h */,
6279F87319E1808D00F12EE5 /* HTMLElement.m */,
6238578F1A9E772B003A45D9 /* HTMLDocument.h */,
623857901A9E772B003A45D9 /* HTMLDocument.m */,
623857941A9E8606003A45D9 /* HTMLDocumentType.h */,
623857951A9E8606003A45D9 /* HTMLDocumentType.m */,
623406E41ADB05AD004677A3 /* HTMLDocumentFragment.h */,
623406E51ADB05AD004677A3 /* HTMLDocumentFragment.m */,
6279F87219E1808D00F12EE5 /* HTMLElement.h */,
6279F87319E1808D00F12EE5 /* HTMLElement.m */,
623857991A9E8934003A45D9 /* HTMLComment.h */,
6238579A1A9E8934003A45D9 /* HTMLComment.m */,
62362A3A1A9FA70400301989 /* HTMLText.h */,
62362A3B1A9FA70400301989 /* HTMLText.m */,
623406DF1ADB04F9004677A3 /* HTMLTemplate.h */,
623406E01ADB04F9004677A3 /* HTMLTemplate.m */,
623406E41ADB05AD004677A3 /* HTMLDocumentFragment.h */,
623406E51ADB05AD004677A3 /* HTMLDocumentFragment.m */,
623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */,
623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */,
6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */,
6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */,
624717BA1B22009200B38302 /* HTMLTreeWalker.h */,
624717BB1B22009200B38302 /* HTMLTreeWalker.m */,
624717161B22333200C11912 /* HTMLNodeTraversal.h */,
624717171B22333200C11912 /* HTMLNodeTraversal.m */,
6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */,
624717B71B21FE5400B38302 /* HTMLNodeFilter.m */,
6238C9831AB8D6330006512E /* HTMLKitDOMExceptions.h */,
6238C9841AB8D6330006512E /* HTMLKitDOMExceptions.m */,
628B7CE61A080E1000602C87 /* HTMLNamespaces.h */,
623719431AA12EE8002E03C8 /* HTMLQuirksMode.h */,
);
name = Nodes;
name = DOM;
sourceTree = "<group>";
};
623975581AC362A5007E26F1 /* Tree Construction */ = {
@@ -337,13 +514,15 @@
name = "Tree Construction";
sourceTree = "<group>";
};
624B9FB71AE072CB00646C4C /* Nodes */ = {
624B9FB71AE072CB00646C4C /* DOM */ = {
isa = PBXGroup;
children = (
623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */,
624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */,
623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */,
6247171B1B2240B800C11912 /* HTMLTreeWalkerTests.m */,
624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */,
62EC7AE51AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m */,
);
name = Nodes;
name = DOM;
sourceTree = "<group>";
};
624B9FB81AE072D500646C4C /* Categories */ = {
@@ -354,6 +533,14 @@
name = Categories;
sourceTree = "<group>";
};
624E1A2D1B1D1C8A00E66AAC /* Structures */ = {
isa = PBXGroup;
children = (
624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */,
);
name = Structures;
sourceTree = "<group>";
};
625A14A219C7829400AD0C32 = {
isa = PBXGroup;
children = (
@@ -400,14 +587,13 @@
isa = PBXGroup;
children = (
625A14BC19C7829400AD0C32 /* HTMLKit.h */,
625A14BD19C7829400AD0C32 /* HTMLKit.m */,
6238C9831AB8D6330006512E /* HTMLKitExceptions.h */,
6238C9841AB8D6330006512E /* HTMLKitExceptions.m */,
626652F81C03D30F00C3F121 /* HTMLKitErrorDomain.h */,
62AE593219F97CCA0043F069 /* Tokenizing */,
628E16EC1ADAE71700B15A06 /* Parsing */,
623719441AA1472B002E03C8 /* Nodes */,
623719441AA1472B002E03C8 /* DOM */,
628E16ED1ADAE73700B15A06 /* Categories */,
628E16EE1ADAE75300B15A06 /* Structures */,
62079BE61AF5656600D3B402 /* Selectors */,
625A14B619C7829400AD0C32 /* Supporting Files */,
);
name = "Source Code";
@@ -429,8 +615,10 @@
children = (
6236738C1AC0CD2400FF89B3 /* Tokenizer */,
623975581AC362A5007E26F1 /* Tree Construction */,
624B9FB71AE072CB00646C4C /* Nodes */,
624B9FB71AE072CB00646C4C /* DOM */,
624B9FB81AE072D500646C4C /* Categories */,
624E1A2D1B1D1C8A00E66AAC /* Structures */,
62FF516C1C0A430A009BFDFE /* Selectors */,
625A14CB19C7829400AD0C32 /* Supporting Files */,
);
name = Tests;
@@ -440,6 +628,7 @@
625A14CB19C7829400AD0C32 /* Supporting Files */ = {
isa = PBXGroup;
children = (
62132E5B1C021FF200084175 /* css-tests */,
624AC90D19FC702E00BD3C4A /* HTML Standard.html */,
624AC90419FBFE8A00BD3C4A /* html5lib-tests */,
625A14CC19C7829400AD0C32 /* HTMLKitTests-Info.plist */,
@@ -457,7 +646,6 @@
6234BEED1AABBF1400DEB15F /* HTMLStackOfOpenElements.m */,
62363C3B1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h */,
62363C3C1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m */,
628B7CE61A080E1000602C87 /* HTMLNamespaces.h */,
6223211D1A969B9300BACED5 /* HTMLElementTypes.h */,
6234C3361AB3BF710046F527 /* HTMLElementAdjustment.h */,
6279F87119E17DC700F12EE5 /* HTMLParserInsertionModes.h */,
@@ -472,6 +660,8 @@
children = (
6235CE991AA509430026937B /* NSString+HTMLKit.h */,
6235CE9A1AA509430026937B /* NSString+HTMLKit.m */,
624B28C21B04190D0048D328 /* NSCharacterSet+HTMLKit.h */,
624B28C31B04190D0048D328 /* NSCharacterSet+HTMLKit.m */,
);
name = Categories;
sourceTree = "<group>";
@@ -515,6 +705,21 @@
name = Tokenizing;
sourceTree = "<group>";
};
62FF516C1C0A430A009BFDFE /* Selectors */ = {
isa = PBXGroup;
children = (
62132E571C01F83200084175 /* CSSSelectorTest.h */,
62132E581C01F83200084175 /* CSSSelectorTest.m */,
621FBE591BDAD68700BC9555 /* CSSSelectorParserTests.m */,
628AF62E1BC99A6C00496128 /* CSSNthExpressionsParserTests.m */,
624AB3161B0508AE00F3830D /* CSSTypeSelectorTests.m */,
624AB3191B050A4D00F3830D /* CSSAttributeSelectorTests.m */,
62F6586F1BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m */,
621FBE5C1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m */,
);
name = Selectors;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@@ -522,34 +727,61 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
6235CE9B1AA509430026937B /* NSString+HTMLKit.h in Headers */,
62F873EB19E088C90062683C /* HTMLParser.h in Headers */,
624AC8FF19FBF59800BD3C4A /* HTMLTokens.h in Headers */,
623916C31AC707250066B4FE /* HTMLNodeTreeEnumerator.h in Headers */,
62AE594419F992F30043F069 /* HTMLCommentToken.h in Headers */,
624493AC19CD0CBE00BCDDF4 /* HTMLToken.h in Headers */,
6235CEA01AA5170A0026937B /* HTMLMarker.h in Headers */,
62F31FDD19E9DCCF007F0657 /* HTMLTokenizerEntities.h in Headers */,
623406E11ADB04F9004677A3 /* HTMLTemplate.h in Headers */,
6234BEEE1AABBF1400DEB15F /* HTMLStackOfOpenElements.h in Headers */,
6238C9851AB8D6330006512E /* HTMLKitExceptions.h in Headers */,
6238039F1AB63A8C008A53D0 /* HTMLEOFToken.h in Headers */,
6279F87419E1808D00F12EE5 /* HTMLElement.h in Headers */,
62AE593F19F9907C0043F069 /* HTMLTagToken.h in Headers */,
623857911A9E772B003A45D9 /* HTMLDocument.h in Headers */,
624493A619CCC54100BCDDF4 /* HTMLTokenizer.h in Headers */,
62AE593A19F97E1C0043F069 /* HTMLDOCTYPEToken.h in Headers */,
623424881AB467B200726190 /* HTMLOrderedDictionary.h in Headers */,
625A150419C783EB00AD0C32 /* HTMLKit.h in Headers */,
625A150819C78ABA00AD0C32 /* HTMLInputStreamReader.h in Headers */,
6238579B1A9E8934003A45D9 /* HTMLComment.h in Headers */,
62AE593519F97D880043F069 /* HTMLParseErrorToken.h in Headers */,
62AE594919F9948A0043F069 /* HTMLCharacterToken.h in Headers */,
623857961A9E8606003A45D9 /* HTMLDocumentType.h in Headers */,
623406E61ADB05AD004677A3 /* HTMLDocumentFragment.h in Headers */,
62363C3D1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h in Headers */,
62362A3C1A9FA70400301989 /* HTMLText.h in Headers */,
6234584E1A9D2FA4009BD491 /* HTMLNode.h in Headers */,
629AA0EA1BDD9C2900D5CC98 /* HTMLKit.h in Headers */,
626652F91C03D7AC00C3F121 /* HTMLKitErrorDomain.h in Headers */,
629AA0EB1BDD9C2900D5CC98 /* HTMLParser.h in Headers */,
629AA0EC1BDD9C2900D5CC98 /* HTMLDOM.h in Headers */,
629AA0ED1BDD9C2900D5CC98 /* HTMLNode.h in Headers */,
629AA0EE1BDD9C2900D5CC98 /* HTMLDocument.h in Headers */,
629AA0EF1BDD9C2900D5CC98 /* HTMLDocumentType.h in Headers */,
629AA0F01BDD9C2900D5CC98 /* HTMLDocumentFragment.h in Headers */,
629AA0F11BDD9C2900D5CC98 /* HTMLElement.h in Headers */,
629AA0F21BDD9C2900D5CC98 /* HTMLComment.h in Headers */,
629AA0F31BDD9C2900D5CC98 /* HTMLText.h in Headers */,
629AA0F41BDD9C2A00D5CC98 /* HTMLTemplate.h in Headers */,
629AA0F51BDD9C2A00D5CC98 /* HTMLNodeIterator.h in Headers */,
629AA0F61BDD9C2A00D5CC98 /* HTMLTreeWalker.h in Headers */,
629AA0F71BDD9C2A00D5CC98 /* HTMLNodeTraversal.h in Headers */,
629AA0F81BDD9C2A00D5CC98 /* HTMLNodeFilter.h in Headers */,
629AA0F91BDD9C2A00D5CC98 /* HTMLKitDOMExceptions.h in Headers */,
629AA0FA1BDD9C2A00D5CC98 /* HTMLNamespaces.h in Headers */,
629AA0FB1BDD9C2A00D5CC98 /* HTMLQuirksMode.h in Headers */,
629AA0FC1BDD9C2A00D5CC98 /* NSString+HTMLKit.h in Headers */,
629AA0FD1BDD9C2A00D5CC98 /* NSCharacterSet+HTMLKit.h in Headers */,
629AA0FE1BDD9C2A00D5CC98 /* HTMLOrderedDictionary.h in Headers */,
629AA0FF1BDD9C2A00D5CC98 /* CSSSelectors.h in Headers */,
629AA1001BDD9C2A00D5CC98 /* CSSSelectorParser.h in Headers */,
629AA11F1BDD9C5E00D5CC98 /* CSSNthExpressionParser.h in Headers */,
629AA1011BDD9C2A00D5CC98 /* CSSSelector.h in Headers */,
629AA1021BDD9C2B00D5CC98 /* CSSSelectorBlock.h in Headers */,
629AA1031BDD9C2B00D5CC98 /* CSSTypeSelector.h in Headers */,
629AA1041BDD9C2B00D5CC98 /* CSSAttributeSelector.h in Headers */,
629AA1051BDD9C2B00D5CC98 /* CSSPseudoClassSelector.h in Headers */,
629AA1061BDD9C2B00D5CC98 /* CSSStructuralPseudoSelectors.h in Headers */,
629AA1071BDD9C2B00D5CC98 /* CSSNthExpressionSelector.h in Headers */,
629AA1081BDD9C2B00D5CC98 /* CSSPseudoFunctionSelector.h in Headers */,
629AA1091BDD9C2B00D5CC98 /* CSSCombinatorSelector.h in Headers */,
629AA10A1BDD9C2B00D5CC98 /* CSSCompoundSelector.h in Headers */,
629AA10B1BDD9C5200D5CC98 /* HTMLTokenizer.h in Headers */,
629AA10C1BDD9C5200D5CC98 /* HTMLTokenizerStates.h in Headers */,
629AA10D1BDD9C5300D5CC98 /* HTMLTokenizerCharacters.h in Headers */,
629AA10E1BDD9C5300D5CC98 /* HTMLTokenizerEntities.h in Headers */,
629AA10F1BDD9C5300D5CC98 /* HTMLTokens.h in Headers */,
629AA1101BDD9C5300D5CC98 /* HTMLToken.h in Headers */,
629AA1111BDD9C5300D5CC98 /* HTMLCharacterToken.h in Headers */,
629AA1121BDD9C5300D5CC98 /* HTMLCommentToken.h in Headers */,
629AA1131BDD9C5300D5CC98 /* HTMLDOCTYPEToken.h in Headers */,
629AA1141BDD9C5400D5CC98 /* HTMLParseErrorToken.h in Headers */,
629AA1151BDD9C5400D5CC98 /* HTMLTagToken.h in Headers */,
629AA1161BDD9C5400D5CC98 /* HTMLEOFToken.h in Headers */,
629AA1171BDD9C5400D5CC98 /* HTMLStackOfOpenElements.h in Headers */,
629AA1181BDD9C5400D5CC98 /* HTMLListOfActiveFormattingElements.h in Headers */,
629AA1191BDD9C5400D5CC98 /* HTMLElementTypes.h in Headers */,
629AA11A1BDD9C5400D5CC98 /* HTMLElementAdjustment.h in Headers */,
629AA11B1BDD9C5500D5CC98 /* HTMLParserInsertionModes.h in Headers */,
629AA11C1BDD9C5500D5CC98 /* HTMLMarker.h in Headers */,
629AA11D1BDD9C5500D5CC98 /* CSSCodePoints.h in Headers */,
629AA11E1BDD9C5500D5CC98 /* CSSInputStream.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -633,7 +865,7 @@
625A14A319C7829400AD0C32 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0610;
LastUpgradeCheck = 0700;
ORGANIZATIONNAME = BrainCookie;
TargetAttributes = {
625A14C219C7829400AD0C32 = {
@@ -679,6 +911,7 @@
files = (
624AC90F19FC702E00BD3C4A /* HTML Standard.html in Resources */,
624AC90519FBFE9A00BD3C4A /* html5lib-tests in Resources */,
62132E5D1C021FF200084175 /* css-tests in Resources */,
625A14CF19C7829400AD0C32 /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -687,6 +920,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
62132E5C1C021FF200084175 /* css-tests in Resources */,
624AC90619FBFEA000BD3C4A /* html5lib-tests in Resources */,
624AC90E19FC702E00BD3C4A /* HTML Standard.html in Resources */,
);
@@ -699,31 +933,48 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
62F501671BC9576B0069F47B /* CSSNthExpressionParser.m in Sources */,
62362A3E1A9FA70400301989 /* HTMLText.m in Sources */,
622BF5831BBF2FD700363583 /* CSSSelectorParser.m in Sources */,
6235CE9D1AA509430026937B /* NSString+HTMLKit.m in Sources */,
6247A9451B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */,
6238579D1A9E8934003A45D9 /* HTMLComment.m in Sources */,
6234BEF01AABBF1400DEB15F /* HTMLStackOfOpenElements.m in Sources */,
624493AE19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */,
6235CEA21AA5170A0026937B /* HTMLMarker.m in Sources */,
62FA98051BD57B6200A2AF86 /* CSSSelectors.m in Sources */,
6234248A1AB467B200726190 /* HTMLOrderedDictionary.m in Sources */,
624717BE1B22009200B38302 /* HTMLTreeWalker.m in Sources */,
62AE594119F9907C0043F069 /* HTMLTagToken.m in Sources */,
624B37201B24E9BA0010BDDF /* CSSInputStream.m in Sources */,
62AE594619F992F30043F069 /* HTMLCommentToken.m in Sources */,
62363C3F1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */,
623916C51AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */,
62AE593C19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */,
623406E31ADB04F9004677A3 /* HTMLTemplate.m in Sources */,
6238C9871AB8D6330006512E /* HTMLKitExceptions.m in Sources */,
62FA04E51BCC360D009ABF98 /* CSSCombinatorSelector.m in Sources */,
6238C9871AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */,
62FA980A1BD5A7E200A2AF86 /* CSSSelectorBlock.m in Sources */,
628AF6401BC9D6FB00496128 /* CSSStructuralPseudoSelectors.m in Sources */,
6247171A1B22333200C11912 /* HTMLNodeTraversal.m in Sources */,
62F873ED19E088C90062683C /* HTMLParser.m in Sources */,
623803A11AB63A8C008A53D0 /* HTMLEOFToken.m in Sources */,
624B28C61B04190D0048D328 /* NSCharacterSet+HTMLKit.m in Sources */,
6279F87619E1808D00F12EE5 /* HTMLElement.m in Sources */,
620C877D1BD44CBE00FB3EEE /* CSSCompoundSelector.m in Sources */,
625A150A19C78ABA00AD0C32 /* HTMLInputStreamReader.m in Sources */,
628AF6351BC9A63D00496128 /* CSSNthExpressionSelector.m in Sources */,
623857931A9E772B003A45D9 /* HTMLDocument.m in Sources */,
623406E81ADB05AD004677A3 /* HTMLDocumentFragment.m in Sources */,
624717B91B21FE5400B38302 /* HTMLNodeFilter.m in Sources */,
620EE94B1BC46F2A0028ED34 /* CSSPseudoClassSelector.m in Sources */,
624493A819CCC54100BCDDF4 /* HTMLTokenizer.m in Sources */,
625A14BE19C7829400AD0C32 /* HTMLKit.m in Sources */,
624B28BA1B03DA820048D328 /* CSSTypeSelector.m in Sources */,
62FC60471BD0519B0042BBE7 /* CSSSelector.m in Sources */,
623857981A9E8606003A45D9 /* HTMLDocumentType.m in Sources */,
620C877F1BD4519A00FB3EEE /* CSSPseudoFunctionSelector.m in Sources */,
62AE594B19F9948A0043F069 /* HTMLCharacterToken.m in Sources */,
623458501A9D2FA4009BD491 /* HTMLNode.m in Sources */,
624B28C11B0413200048D328 /* CSSAttributeSelector.m in Sources */,
62F31FDF19E9DCCF007F0657 /* HTMLTokenizerEntities.m in Sources */,
62AE593719F97D880043F069 /* HTMLParseErrorToken.m in Sources */,
);
@@ -733,15 +984,25 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
62132E5A1C01F83200084175 /* CSSSelectorTest.m in Sources */,
6239755B1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */,
62F658711BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m in Sources */,
623CAF9E1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */,
6247171D1B2240B800C11912 /* HTMLTreeWalkerTests.m in Sources */,
62EC7AE71AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m in Sources */,
624AB31B1B050A4D00F3830D /* CSSAttributeSelectorTests.m in Sources */,
624B9FB61AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */,
624AB3181B0508AE00F3830D /* CSSTypeSelectorTests.m in Sources */,
6236738F1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */,
624AC90219FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */,
624B9FB31AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */,
623916C81AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */,
623916C81AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m in Sources */,
62D8345A19FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */,
6239755F1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */,
624FC37B1AE591D80015DDF9 /* HTMLKitNodesTests.m in Sources */,
621FBE5B1BDAD68700BC9555 /* CSSSelectorParserTests.m in Sources */,
621FBE5E1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m in Sources */,
628AF6301BC99A6C00496128 /* CSSNthExpressionsParserTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -750,30 +1011,47 @@
buildActionMask = 2147483647;
files = (
62362A3D1A9FA70400301989 /* HTMLText.m in Sources */,
622BF5821BBF2FD700363583 /* CSSSelectorParser.m in Sources */,
6235CE9C1AA509430026937B /* NSString+HTMLKit.m in Sources */,
6247A9441B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */,
6238579C1A9E8934003A45D9 /* HTMLComment.m in Sources */,
6234BEEF1AABBF1400DEB15F /* HTMLStackOfOpenElements.m in Sources */,
624493AD19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */,
6235CEA11AA5170A0026937B /* HTMLMarker.m in Sources */,
623424891AB467B200726190 /* HTMLOrderedDictionary.m in Sources */,
62FA98041BD57B6200A2AF86 /* CSSSelectors.m in Sources */,
624717BD1B22009200B38302 /* HTMLTreeWalker.m in Sources */,
62AE594019F9907C0043F069 /* HTMLTagToken.m in Sources */,
62F501661BC9576B0069F47B /* CSSNthExpressionParser.m in Sources */,
624B371F1B24E9BA0010BDDF /* CSSInputStream.m in Sources */,
62AE594519F992F30043F069 /* HTMLCommentToken.m in Sources */,
62363C3E1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */,
623916C41AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */,
62AE593B19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */,
623406E21ADB04F9004677A3 /* HTMLTemplate.m in Sources */,
6238C9861AB8D6330006512E /* HTMLKitExceptions.m in Sources */,
62FA04E41BCC360D009ABF98 /* CSSCombinatorSelector.m in Sources */,
628AF63F1BC9D6FB00496128 /* CSSStructuralPseudoSelectors.m in Sources */,
62FA98091BD5A7E200A2AF86 /* CSSSelectorBlock.m in Sources */,
6238C9861AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */,
624717191B22333200C11912 /* HTMLNodeTraversal.m in Sources */,
62F873EC19E088C90062683C /* HTMLParser.m in Sources */,
623803A01AB63A8C008A53D0 /* HTMLEOFToken.m in Sources */,
624B28C51B04190D0048D328 /* NSCharacterSet+HTMLKit.m in Sources */,
6279F87519E1808D00F12EE5 /* HTMLElement.m in Sources */,
620C877C1BD44CBE00FB3EEE /* CSSCompoundSelector.m in Sources */,
625A150919C78ABA00AD0C32 /* HTMLInputStreamReader.m in Sources */,
623857921A9E772B003A45D9 /* HTMLDocument.m in Sources */,
623406E71ADB05AD004677A3 /* HTMLDocumentFragment.m in Sources */,
624717B81B21FE5400B38302 /* HTMLNodeFilter.m in Sources */,
620EE94A1BC46F2A0028ED34 /* CSSPseudoClassSelector.m in Sources */,
624493A719CCC54100BCDDF4 /* HTMLTokenizer.m in Sources */,
625A150219C783DE00AD0C32 /* HTMLKit.m in Sources */,
624B28B91B03DA820048D328 /* CSSTypeSelector.m in Sources */,
623857971A9E8606003A45D9 /* HTMLDocumentType.m in Sources */,
62FC60461BD0519B0042BBE7 /* CSSSelector.m in Sources */,
62AE594A19F9948A0043F069 /* HTMLCharacterToken.m in Sources */,
620C877E1BD4519A00FB3EEE /* CSSPseudoFunctionSelector.m in Sources */,
6234584F1A9D2FA4009BD491 /* HTMLNode.m in Sources */,
624B28C01B0413200048D328 /* CSSAttributeSelector.m in Sources */,
628AF6341BC9A63D00496128 /* CSSNthExpressionSelector.m in Sources */,
62F31FDE19E9DCCF007F0657 /* HTMLTokenizerEntities.m in Sources */,
62AE593619F97D880043F069 /* HTMLParseErrorToken.m in Sources */,
);
@@ -783,15 +1061,25 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
62132E591C01F83200084175 /* CSSSelectorTest.m in Sources */,
6239755A1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */,
62F658701BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m in Sources */,
623CAF9D1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */,
6247171C1B2240B800C11912 /* HTMLTreeWalkerTests.m in Sources */,
62EC7AE61AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m in Sources */,
624AB31A1B050A4D00F3830D /* CSSAttributeSelectorTests.m in Sources */,
624B9FB51AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */,
624AB3171B0508AE00F3830D /* CSSTypeSelectorTests.m in Sources */,
6236738E1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */,
624AC90119FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */,
624B9FB21AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */,
623916C71AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */,
623916C71AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m in Sources */,
62D8345919FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */,
6239755E1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */,
624FC37A1AE591D80015DDF9 /* HTMLKitNodesTests.m in Sources */,
621FBE5A1BDAD68700BC9555 /* CSSSelectorParserTests.m in Sources */,
621FBE5D1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m in Sources */,
628AF62F1BC99A6C00496128 /* CSSNthExpressionsParserTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -847,6 +1135,7 @@
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
@@ -904,12 +1193,14 @@
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_VERSION = A;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch";
INFOPLIST_FILE = "HTMLKit/HTMLKit-Info.plist";
PRODUCT_BUNDLE_IDENTIFIER = "com.braincookie.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = HTMLKit;
WRAPPER_EXTENSION = framework;
};
@@ -919,12 +1210,14 @@
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_VERSION = A;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch";
INFOPLIST_FILE = "HTMLKit/HTMLKit-Info.plist";
PRODUCT_BUNDLE_IDENTIFIER = "com.braincookie.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = HTMLKit;
WRAPPER_EXTENSION = framework;
};
@@ -946,6 +1239,7 @@
"$(inherited)",
);
INFOPLIST_FILE = "HTMLKitTests/HTMLKitTests-Info.plist";
PRODUCT_BUNDLE_IDENTIFIER = "com.braincookie.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
@@ -964,6 +1258,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch";
INFOPLIST_FILE = "HTMLKitTests/HTMLKitTests-Info.plist";
PRODUCT_BUNDLE_IDENTIFIER = "com.braincookie.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
@@ -973,6 +1268,7 @@
625A14FD19C7834100AD0C32 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
DEFINES_MODULE = YES;
DSTROOT = /tmp/HTMLKit.dst;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch";
@@ -991,6 +1287,7 @@
625A14FE19C7834100AD0C32 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
DEFINES_MODULE = YES;
DSTROOT = /tmp/HTMLKit.dst;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch";
@@ -1008,7 +1305,6 @@
buildSettings = {
BUNDLE_LOADER = "";
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
"$(DEVELOPER_FRAMEWORKS_DIR)",
);
@@ -1020,6 +1316,8 @@
);
INFOPLIST_FILE = "HTMLKitTests/HTMLKitTests-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 7.1;
OTHER_LDFLAGS = " -ObjC";
PRODUCT_BUNDLE_IDENTIFIER = "com.braincookie.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
TEST_HOST = "$(BUNDLE_LOADER)";
@@ -1032,7 +1330,6 @@
buildSettings = {
BUNDLE_LOADER = "";
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
"$(DEVELOPER_FRAMEWORKS_DIR)",
);
@@ -1040,6 +1337,8 @@
GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch";
INFOPLIST_FILE = "HTMLKitTests/HTMLKitTests-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 7.1;
OTHER_LDFLAGS = " -ObjC";
PRODUCT_BUNDLE_IDENTIFIER = "com.braincookie.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
TEST_HOST = "$(BUNDLE_LOADER)";
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:HTMLKit.xcodeproj">
</FileRef>
</Workspace>
+30
View File
@@ -0,0 +1,30 @@
//
// CSSAttributeSelector.h
// HTMLKit
//
// Created by Iska on 14/05/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "CSSSelector.h"
NS_ASSUME_NONNULL_BEGIN
@interface CSSAttributeSelector : CSSSelector
@property (nonatomic, assign) CSSAttributeSelectorType type;
@property (nonatomic, strong, readonly) NSString *name;
@property (nonatomic, strong, readonly) NSString *value;
+ (instancetype)classSelector:(NSString *)className;
+ (instancetype)idSelector:(NSString *)elementId;
+ (instancetype)hasAttributeSelector:(NSString *)attributeName;
- (instancetype)initWithType:(CSSAttributeSelectorType)type
attributeName:(NSString *)name
attrbiuteValue:(NSString *)value;
@end
NS_ASSUME_NONNULL_END
+111
View File
@@ -0,0 +1,111 @@
//
// CSSAttributeSelector.m
// HTMLKit
//
// Created by Iska on 14/05/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import "CSSAttributeSelector.h"
#import "HTMLElement.h"
#import "NSCharacterSet+HTMLKit.h"
@interface CSSAttributeSelector ()
{
CSSAttributeSelectorType _type;
NSString *_name;
NSString *_value;
}
@end
@implementation CSSAttributeSelector
+ (instancetype)classSelector:(NSString *)className
{
return [[self alloc] initWithType:CSSAttributeSelectorIncludes attributeName:@"class" attrbiuteValue:className];
}
+ (instancetype)idSelector:(NSString *)elementId
{
return [[self alloc] initWithType:CSSAttributeSelectorExactMatch attributeName:@"id" attrbiuteValue:elementId];
}
+ (instancetype)hasAttributeSelector:(NSString *)attributeName
{
return [[self alloc] initWithType:CSSAttributeSelectorExists attributeName:attributeName attrbiuteValue:@""];
}
- (instancetype)initWithType:(CSSAttributeSelectorType)type
attributeName:(NSString *)name
attrbiuteValue:(NSString *)value
{
self = [super init];
if (self) {
self.type = type;
_name = [name copy];
_value = value ? [value copy]: @"";
}
return self;
}
- (BOOL)acceptElement:(HTMLElement *)element
{
switch (_type) {
case CSSAttributeSelectorExists:
{
return !!element[_name];
}
case CSSAttributeSelectorExactMatch:
{
return [element[_name] isEqualToString:_value];
}
case CSSAttributeSelectorIncludes:
{
NSArray *components = [element[_name] componentsSeparatedByCharactersInSet:[NSCharacterSet HTMLWhitespaceCharacterSet]];
return [components containsObject:_value];
}
case CSSAttributeSelectorBegins:
{
return [element[_name] hasPrefix:_value];
}
case CSSAttributeSelectorEnds:
{
return [element[_name] hasSuffix:_value];
}
case CSSAttributeSelectorContains:
{
return [element[_name] containsString:_value];
}
case CSSAttributeSelectorHyphen:
{
return [element[_name] isEqualToString:_value] || [element[_name] hasPrefix:[_value stringByAppendingString:@"-"]];
}
case CSSAttributeSelectorNot:
{
return ![element[_name] isEqualToString:_value];
}
default:
return NO;
}
}
#pragma mark - Description
- (NSString *)debugDescription
{
if (self.type == CSSAttributeSelectorExists) {
return [NSString stringWithFormat:@"[%@]", self.name];
}
NSString *matcher = @{@(CSSAttributeSelectorExactMatch): @"=",
@(CSSAttributeSelectorIncludes): @"~=",
@(CSSAttributeSelectorBegins): @"^=",
@(CSSAttributeSelectorEnds): @"$=",
@(CSSAttributeSelectorContains): @"*=",
@(CSSAttributeSelectorHyphen): @"|=",
@(CSSAttributeSelectorNot): @"!="}[@(self.type)];
return [NSString stringWithFormat:@"[%@%@'%@']", self.name, matcher, self.value];
}
@end
+140
View File
@@ -0,0 +1,140 @@
//
// CSSTokenizer CODEPOINTs.h
// HTMLKit
//
// Created by Iska on 08/06/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#define CODEPOINTS \
CODEPOINT( CONTROL, 0x0080 ) \
CODEPOINT( CHARACTER_TABULATION, 0x0009 ) \
CODEPOINT( LINE_FEED, 0x000A ) \
CODEPOINT( SPACE, 0x0020 ) \
CODEPOINT( QUOTATION_MARK, 0x0022 ) \
CODEPOINT( NUMBER_SIGN, 0x0023 ) \
CODEPOINT( DOLLAR_SIGN, 0x0024 ) \
CODEPOINT( APOSTROPHE, 0x0027 ) \
CODEPOINT( LEFT_PARENTHESIS, 0x0028 ) \
CODEPOINT( RIGHT_PARENTHESIS, 0x0029 ) \
CODEPOINT( ASTERIX, 0x002A ) \
CODEPOINT( PLUS_SIGN, 0x002B ) \
CODEPOINT( COMMA, 0x002C ) \
CODEPOINT( HYPHEN_MINUS, 0x002D ) \
CODEPOINT( FULL_STOP, 0x002E ) \
CODEPOINT( DIGIT_ZERO, 0x0030 ) \
CODEPOINT( DIGIT_NINE, 0x0039 ) \
CODEPOINT( COLON, 0x003A ) \
CODEPOINT( EQUALS_SIGN, 0x003D ) \
CODEPOINT( GREATER_THAN_SIGN, 0x003E ) \
CODEPOINT( LATIN_CAPITAL_LETTER_A, 0x0041 ) \
CODEPOINT( LATIN_CAPITAL_LETTER_F, 0x0046 ) \
CODEPOINT( LATIN_CAPITAL_LETTER_Z, 0x005A ) \
CODEPOINT( LEFT_SQUARE_BRACKET, 0x005B ) \
CODEPOINT( REVERSE_SOLIDUS, 0x005C ) \
CODEPOINT( RIGHT_SQUARE_BRACKET, 0x005D ) \
CODEPOINT( CIRCUMFLEX_ACCENT, 0x005E ) \
CODEPOINT( LOW_LINE, 0x005F ) \
CODEPOINT( LATIN_SMALL_LETTER_A, 0x0061 ) \
CODEPOINT( LATIN_SMALL_LETTER_F, 0x0066 ) \
CODEPOINT( LATIN_SMALL_LETTER_Z, 0x007A ) \
CODEPOINT( VERTICAL_LINE, 0x007C ) \
CODEPOINT( TILDE, 0x007E ) \
CODEPOINT( REPLACEMENT_CHARACTER, 0xFFFD )
#define CODEPOINT( name, value ) static UniChar const name = value;
CODEPOINTS
#undef CODEPOINT
NS_INLINE BOOL isWhitespace(UTF32Char codePoint)
{
return (codePoint == CHARACTER_TABULATION ||
codePoint == LINE_FEED ||
codePoint == SPACE);
}
NS_INLINE BOOL isCombinator(UTF32Char codePoint)
{
return (codePoint == SPACE ||
codePoint == PLUS_SIGN ||
codePoint == COMMA ||
codePoint == GREATER_THAN_SIGN ||
codePoint == TILDE);
}
NS_INLINE BOOL isDigit(UTF32Char codePoint)
{
return codePoint >= DIGIT_ZERO && codePoint <= DIGIT_NINE;
}
NS_INLINE BOOL isHexDigit(UTF32Char codePoint)
{
return ((codePoint >= DIGIT_ZERO && codePoint <= DIGIT_NINE) ||
(codePoint >= LATIN_CAPITAL_LETTER_A && codePoint <= LATIN_CAPITAL_LETTER_F) ||
(codePoint >= LATIN_SMALL_LETTER_A && codePoint <= LATIN_SMALL_LETTER_F));
}
NS_INLINE BOOL isQuote(UTF32Char codePoint)
{
return codePoint == QUOTATION_MARK || codePoint == APOSTROPHE;
}
NS_INLINE BOOL isNewLine(UTF32Char codePoint)
{
return codePoint == LINE_FEED;
}
NS_INLINE BOOL isNameStart(UTF32Char codePoint)
{
return ((codePoint >= LATIN_CAPITAL_LETTER_A && codePoint <= LATIN_CAPITAL_LETTER_Z) ||
(codePoint >= LATIN_SMALL_LETTER_A && codePoint <= LATIN_SMALL_LETTER_Z) ||
codePoint >= CONTROL ||
codePoint == LOW_LINE);
}
NS_INLINE BOOL isName(UTF32Char codePoint)
{
return isNameStart(codePoint) || isDigit(codePoint) || codePoint == HYPHEN_MINUS;
}
NS_INLINE BOOL isValidEscape(UTF32Char first, UTF32Char second)
{
if (first != REVERSE_SOLIDUS || isNewLine(second)) {
return false;
}
return YES;
}
NS_INLINE BOOL isValidEscapedCodePoint(UTF32Char codePoint)
{
return (codePoint != 0 &&
!(codePoint >= 0xD800 && codePoint <= 0x0DFFF) &&
codePoint <= 0x10FFFF);
}
NS_INLINE BOOL isValidIdentifierStart(UTF32Char first, UTF32Char second, UTF32Char third)
{
if (first == HYPHEN_MINUS) {
if (isNameStart(second) ||
second == HYPHEN_MINUS ||
isValidEscape(second, third)) {
return YES;
} else {
return NO;
}
} else if (isNameStart(first)) {
return YES;
} else if (first == REVERSE_SOLIDUS) {
return isValidEscape(first, second);
} else {
return NO;
}
}
NS_INLINE void AppendCodePoint(CFMutableStringRef string, UTF32Char codePoint)
{
UniChar pair[2];
Boolean isPair = CFStringGetSurrogatePairForLongCharacter(codePoint, pair);
CFStringAppendCharacters(string, pair, isPair ? 2 : 1);
}
+22
View File
@@ -0,0 +1,22 @@
//
// CSSCombinatorSelector.h
// HTMLKit
//
// Created by Iska on 12/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import "CSSSelector.h"
NS_ASSUME_NONNULL_BEGIN
@interface CSSCombinatorSelector : CSSSelector
+ (instancetype)childOfElementCombinator:(CSSSelector *)selector;
+ (instancetype)descendantOfElementCombinator:(CSSSelector *)selector;
+ (instancetype)adjacentSiblingCombinator:(CSSSelector *)selector;
+ (instancetype)generalSiblingCombinator:(CSSSelector *)selector;
@end
NS_ASSUME_NONNULL_END
+154
View File
@@ -0,0 +1,154 @@
//
// CSSCombinatorSelector.m
// HTMLKit
//
// Created by Iska on 12/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import "CSSCombinatorSelector.h"
#import "HTMLElement.h"
#pragma mark - Declarations
@interface CSSChildOfElementCombinatorSelector : CSSCombinatorSelector
@end
@interface CSSDecendantOfElementCombinatorSelector : CSSCombinatorSelector
@end
@interface CSSAdjacentSiblingCombinatorSelector : CSSCombinatorSelector
@end
@interface CSSGeneralSiblingCombinatorSelector : CSSCombinatorSelector
@end
#pragma mark - Base Combinator
@interface CSSCombinatorSelector ()
{
CSSSelector *_selector;
}
@property (nonatomic, strong, readonly) CSSSelector *selector;
@end
@implementation CSSCombinatorSelector
@synthesize selector = _selector;
+ (instancetype)childOfElementCombinator:(CSSSelector *)selector
{
return [[CSSChildOfElementCombinatorSelector alloc] initWithSelector:selector];
}
+ (instancetype)descendantOfElementCombinator:(CSSSelector *)selector
{
return [[CSSDecendantOfElementCombinatorSelector alloc] initWithSelector:selector];
}
+ (instancetype)adjacentSiblingCombinator:(CSSSelector *)selector
{
return [[CSSAdjacentSiblingCombinatorSelector alloc] initWithSelector:selector];
}
+ (instancetype)generalSiblingCombinator:(CSSSelector *)selector
{
return [[CSSGeneralSiblingCombinatorSelector alloc] initWithSelector:selector];
}
- (instancetype)initWithSelector:(CSSSelector *)selector
{
self = [super init];
if (self) {
_selector = selector;
}
return self;
}
@end
#pragma mark - Child OfElement Combinator
@implementation CSSChildOfElementCombinatorSelector
- (BOOL)acceptElement:(HTMLElement *)element
{
HTMLElement *parent = element.parentElement;
return parent != nil && [self.selector acceptElement:parent];
}
- (NSString *)debugDescription
{
return [NSString stringWithFormat:@"%@ > ", self.selector.debugDescription];
}
@end
#pragma mark - Decendant Of Element Combinator
@implementation CSSDecendantOfElementCombinatorSelector
- (BOOL)acceptElement:(HTMLElement *)element
{
HTMLElement *parent = element.parentElement;
while (parent != nil) {
if ([self.selector acceptElement:parent]) {
return YES;
}
parent = parent.parentElement;
}
return NO;
}
- (NSString *)debugDescription
{
return [NSString stringWithFormat:@"%@ ", self.selector.debugDescription];
}
@end
#pragma mark - Adjacent Sibling Combinator
@implementation CSSAdjacentSiblingCombinatorSelector
- (BOOL)acceptElement:(HTMLElement *)element
{
HTMLNode *previous = element.previousSiblingElement;
if (previous == nil || previous.nodeType != HTMLNodeElement) {
return NO;
}
return [self.selector acceptElement:previous.asElement];
}
- (NSString *)debugDescription
{
return [NSString stringWithFormat:@"%@ + ", self.selector.debugDescription];
}
@end
#pragma mark - General Sibling Combinator
@implementation CSSGeneralSiblingCombinatorSelector
- (BOOL)acceptElement:(HTMLElement *)element
{
HTMLNode *previous = element.previousSiblingElement;
while (previous != nil && previous.nodeType == HTMLNodeElement) {
if ([self.selector acceptElement:previous.asElement]) {
return YES;
}
previous = previous.previousSiblingElement;
}
return NO;
}
- (NSString *)debugDescription
{
return [NSString stringWithFormat:@"%@ ~ ", self.selector.debugDescription];
}
@end
+22
View File
@@ -0,0 +1,22 @@
//
// CSSCompoundSelector.h
// HTMLKit
//
// Created by Iska on 18/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import "CSSSelector.h"
NS_ASSUME_NONNULL_BEGIN
@interface CSSCompoundSelector : CSSSelector
+ (instancetype)andSelector:(NSArray<CSSSelector *> *)selectors;
+ (instancetype)orSelector:(NSArray<CSSSelector *> *)selectors;
- (void)addSelector:(CSSSelector *)selector;
@end
NS_ASSUME_NONNULL_END
+99
View File
@@ -0,0 +1,99 @@
//
// CSSCompoundSelector.m
// HTMLKit
//
// Created by Iska on 18/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import "CSSCompoundSelector.h"
#pragma mark - Declarations
@interface CSSAndCompoundSelector : CSSCompoundSelector
@end
@interface CSSOrCompoundSelector : CSSCompoundSelector
@end
#pragma mark - Base Combinator
@interface CSSCompoundSelector ()
{
NSMutableArray *_selectors;
}
@property (nonatomic, strong, readonly) NSArray *selectors;
@end
@implementation CSSCompoundSelector
@synthesize selectors = _selectors;
+ (instancetype)andSelector:(NSArray *)selectors
{
return [[CSSAndCompoundSelector alloc] initWithSelectors:selectors];
}
+ (instancetype)orSelector:(NSArray *)selectors
{
return [[CSSOrCompoundSelector alloc] initWithSelectors:selectors];
}
- (instancetype)initWithSelectors:(NSArray *)selectors
{
self = [super init];
if (self) {
_selectors = [[NSMutableArray alloc] initWithArray:selectors];
}
return self;
}
- (void)addSelector:(CSSSelector *)selector
{
[_selectors addObject:selector];
}
@end
#pragma mark - And Compound Selector
@implementation CSSAndCompoundSelector
- (BOOL)acceptElement:(HTMLElement *)element
{
for (CSSSelector *selector in self.selectors) {
if (![selector acceptElement:element]) {
return NO;
}
}
return YES;
}
- (NSString *)debugDescription
{
NSArray *descriptions = [self.selectors valueForKey:@"debugDescription"];
return [descriptions componentsJoinedByString:@""];
}
@end
#pragma mark - Or Compound Selector
@implementation CSSOrCompoundSelector
- (BOOL)acceptElement:(HTMLElement *)element
{
for (CSSSelector *selector in self.selectors) {
if ([selector acceptElement:element]) {
return YES;
}
}
return NO;
}
- (NSString *)debugDescription
{
NSArray *descriptions = [self.selectors valueForKey:@"debugDescription"];
return [descriptions componentsJoinedByString:@","];
}
@end
+20
View File
@@ -0,0 +1,20 @@
//
// CSSInputStream.h
// HTMLKit
//
// Created by Iska on 07/06/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "HTMLInputStreamReader.h"
@interface CSSInputStream : HTMLInputStreamReader
- (void)consumeWhitespace;
- (NSString *)consumeIdentifier;
- (NSString *)consumeStringWithEndingCodePoint:(UTF32Char)endingCodePoint;
- (UTF32Char)consumeEscapedCodePoint;
- (NSString *)consumeCombinator;
@end
+128
View File
@@ -0,0 +1,128 @@
//
// CSSInputStream.m
// HTMLKit
//
// Created by Iska on 07/06/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import "CSSInputStream.h"
#import "CSSCodePoints.h"
@interface HTMLInputStreamReader ()
- (void)emitParseError:(NSString *)reason;
@end
@implementation CSSInputStream
- (void)consumeWhitespace
{
while (isWhitespace(self.nextInputCharacter)) {
[self consumeNextInputCharacter];
}
}
- (NSString *)consumeIdentifier
{
CFMutableStringRef value = CFStringCreateMutable(kCFAllocatorDefault, 0);
if (!isValidIdentifierStart([self inputCharacterPointAtOffset:0],
[self inputCharacterPointAtOffset:1],
[self inputCharacterPointAtOffset:2])) {
return nil;
}
while (YES) {
UTF32Char codePoint = [self consumeNextInputCharacter];
if (codePoint == EOF) {
break;
} else if (isName(codePoint)) {
AppendCodePoint(value, codePoint);
} else if (isValidEscape(codePoint, [self inputCharacterPointAtOffset:1])) {
UTF32Char escapedCodePoint = [self consumeEscapedCodePoint];
AppendCodePoint(value, escapedCodePoint);
} else {
[self reconsumeCurrentInputCharacter];
break;
}
}
return (__bridge NSString *)(CFStringGetLength(value) > 0 ? value : nil);
}
- (NSString *)consumeStringWithEndingCodePoint:(UTF32Char)endingCodePoint
{
CFMutableStringRef value = CFStringCreateMutable(kCFAllocatorDefault, 0);
while (YES) {
UTF32Char codePoint = [self consumeNextInputCharacter];
if (codePoint == endingCodePoint) {
break;
}
switch (codePoint) {
case EOF:
break;
case LINE_FEED:
[self emitParseError:@"New-line character (0x000A) in CSS attribute value"];
[self reconsumeCurrentInputCharacter];
break;
case REVERSE_SOLIDUS:
{
UTF32Char next = self.nextInputCharacter;
if (next == EOF) {
continue;
} else if (next == LINE_FEED) {
[self consumeNextInputCharacter];
} else {
UTF32Char escapedCodePoint = [self consumeNextInputCharacter];
AppendCodePoint(value, escapedCodePoint);
}
}
default:
AppendCodePoint(value, codePoint);
break;
}
}
return (__bridge NSString *)(CFStringGetLength(value) > 0 ? value : nil);
}
- (UTF32Char)consumeEscapedCodePoint
{
UTF32Char codePoint = [self consumeNextInputCharacter];
if (isHexDigit(codePoint)) {
CFMutableStringRef hexString = CFStringCreateMutable(kCFAllocatorDefault, 6);
AppendCodePoint(hexString, codePoint);
while (isHexDigit(self.nextInputCharacter) && CFStringGetLength(hexString) <= 6) {
UniChar codePoint = [self consumeNextInputCharacter];
CFStringAppendCharacters(hexString, &codePoint, 1);
}
if (isWhitespace(self.nextInputCharacter)) {
[self consumeNextInputCharacter];
}
NSScanner *scanner = [NSScanner scannerWithString:(__bridge NSString *)(hexString)];
UTF32Char number;
[scanner scanHexInt:&number];
return isValidEscapedCodePoint(number) ? number : REPLACEMENT_CHARACTER;
} else if (codePoint == EOF) {
return REPLACEMENT_CHARACTER;
}
return codePoint;
}
- (NSString *)consumeCombinator
{
NSString *combinator = [self consumeCharactersInString:@" >+~"];
combinator = [combinator stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
return combinator;
}
@end
+16
View File
@@ -0,0 +1,16 @@
//
// CSSNthExpression.h
// HTMLKit
//
// Created by Iska on 10/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "CSSSelectors.h"
@interface CSSNthExpressionParser : NSObject
+ (CSSNthExpression)parseExpression:(NSString *)expression;
@end
+50
View File
@@ -0,0 +1,50 @@
//
// CSSNthExpression.m
// HTMLKit
//
// Created by Iska on 10/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import "CSSNthExpressionParser.h"
#import "CSSCodePoints.h"
#import "NSString+HTMLKit.h"
#import "NSCharacterSet+HTMLKit.h"
@implementation CSSNthExpressionParser
+ (CSSNthExpression)parseExpression:(NSString *)expression
{
NSCharacterSet *whitespace = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSString *string = [expression.lowercaseString copy];
string = [[string stringByTrimmingCharactersInSet:whitespace] copy];
if ([string isEqualToStringIgnoringCase:@"odd"]) {
return CSSNthExpressionOdd;
} else if ([string isEqualToStringIgnoringCase:@"even"]) {
return CSSNthExpressionEven;
}
NSCharacterSet *set = [[NSCharacterSet CSSNthExpressionCharacterSet] invertedSet];
if ([string rangeOfCharacterFromSet:set].location != NSNotFound) {
return CSSNthExpressionMake(0, 0);
}
NSArray *parts = [string componentsSeparatedByString:@"n"];
if (parts.count == 1) {
NSInteger b = [parts[0] integerValue];
return CSSNthExpressionMake(0, b);
} else if (parts.count == 2) {
NSInteger a = [parts[0] integerValue];
if (a == 0) {
a = [parts[0] isEqualToString:@"-"] ? -1 : 1;
}
NSInteger b = [parts[1] integerValue];
return CSSNthExpressionMake(a, b);
} else {
return CSSNthExpressionMake(0, 0);
}
}
@end
+25
View File
@@ -0,0 +1,25 @@
//
// CSSNthExpressionSelector.h
// HTMLKit
//
// Created by Iska on 10/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import "CSSSelector.h"
NS_ASSUME_NONNULL_BEGIN
@interface CSSNthExpressionSelector : CSSSelector
@property (nonatomic, strong, readonly) NSString *className;
@property (nonatomic, assign, readonly) CSSNthExpression expression;
+ (instancetype)nthChildSelector:(CSSNthExpression)expression;
+ (instancetype)nthLastChildSelector:(CSSNthExpression)expression;
+ (instancetype)nthOfTypeSelector:(CSSNthExpression)expression;
+ (instancetype)nthLastOfTypeSelector:(CSSNthExpression)expression;
@end
NS_ASSUME_NONNULL_END
+130
View File
@@ -0,0 +1,130 @@
//
// CSSNthExpressionSelector.m
// HTMLKit
//
// Created by Iska on 10/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import "CSSNthExpressionSelector.h"
#import "HTMLElement.h"
#pragma mark - Nth-Expression
const CSSNthExpression CSSNthExpressionOdd = (CSSNthExpression) {
.an = 2, .b = 1
};
const CSSNthExpression CSSNthExpressionEven = (CSSNthExpression) {
.an = 2, .b = 0
};
NSString * _Nonnull NSStringFromNthExpression(CSSNthExpression expression)
{
if (expression.an == 0 && expression.b == 0) {
return @"invalid";
}
if (expression.an == 0) {
return [NSString stringWithFormat:@"%ld", expression.b];
}
if (expression.b == 0) {
return [NSString stringWithFormat:@"%ldn", expression.an];
}
return [NSString stringWithFormat:@"%ldn%+ld", expression.an, expression.b];
}
#pragma mark - Implementation
NSInteger computeIndex(NSEnumerator *enumerator, HTMLElement *element)
{
NSInteger index = 0;
for (HTMLNode *node in enumerator) {
if (node.nodeType != HTMLNodeElement) {
continue;
}
if ([node.asElement.tagName isEqualToString:element.tagName]) {
index++;
}
if (node == element) {
break;
}
}
return index;
}
@interface CSSNthExpressionSelector ()
{
NSString *_className;
CSSNthExpression _expression;
NSInteger (^ _computeIndex)(HTMLElement *);
}
@end
@implementation CSSNthExpressionSelector
@synthesize expression = _expression;
@synthesize className = _className;
+ (instancetype)nthChildSelector:(CSSNthExpression)expression
{
return [[self alloc] initWithClassName:@"nth-child" expression:expression block:^NSInteger(HTMLElement *element) {
return [element.parentElement indexOfChildElement:element] + 1;
}];
}
+ (instancetype)nthLastChildSelector:(CSSNthExpression)expression
{
return [[self alloc] initWithClassName:@"nth-last-child" expression:expression block:^NSInteger(HTMLElement *element) {
return element.parentElement.childElementsCount - [element.parentElement indexOfChildElement:element];
}];
}
+ (instancetype)nthOfTypeSelector:(CSSNthExpression)expression
{
return [[self alloc] initWithClassName:@"nth-of-type" expression:expression block:^NSInteger(HTMLElement *element) {
return computeIndex(element.parentElement.childNodes.array.objectEnumerator, element);
}];
}
+ (instancetype)nthLastOfTypeSelector:(CSSNthExpression)expression
{
return [[self alloc] initWithClassName:@"nth-last-of-type" expression:expression block:^NSInteger(HTMLElement *element) {
return computeIndex(element.parentElement.childNodes.array.reverseObjectEnumerator, element);
}];
}
- (instancetype)initWithClassName:(NSString *)className
expression:(CSSNthExpression)expression
block:(NSInteger (^)(HTMLElement *element))block
{
self = [super init];
if (self) {
_className = [className copy];
_expression = expression;
_computeIndex = [block copy];
}
return self;
}
- (BOOL)acceptElement:(HTMLElement *)element
{
NSInteger index = _computeIndex(element);
if (_expression.an == 0) {
return index == _expression.b;
} else {
NSInteger diff = (index - _expression.b);
return (diff * _expression.an >= 0) && (diff % _expression.an == 0);
}
}
- (NSString *)debugDescription
{
return [NSString stringWithFormat:@":%@(%@)", self.className, NSStringFromNthExpression(self.expression)];
}
@end
+21
View File
@@ -0,0 +1,21 @@
//
// CSSPseudoClassSelector.h
// HTMLKit
//
// Created by Iska on 06/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import "CSSSelector.h"
NS_ASSUME_NONNULL_BEGIN
@interface CSSPseudoClassSelector : CSSSelector
@property (nonatomic, strong, readonly) NSString *className;
- (instancetype)initWithClassName:(NSString *)className selector:(CSSSelector *)selector;
@end
NS_ASSUME_NONNULL_END
+46
View File
@@ -0,0 +1,46 @@
//
// CSSPseudoClassSelector.m
// HTMLKit
//
// Created by Iska on 06/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import "CSSPseudoClassSelector.h"
@interface CSSPseudoClassSelector ()
{
NSString *_className;
CSSSelector *_selector;
}
@end
@implementation CSSPseudoClassSelector
@synthesize className = _className;
- (instancetype)initWithClassName:(NSString *)className selector:(CSSSelector *)selector
{
self = [super init];
if (self) {
_className = [className copy];
_selector = selector;
}
return self;
}
-(BOOL)acceptElement:(HTMLElement *)element
{
return [_selector acceptElement:element];
}
- (NSString *)debugDescription
{
return [NSString stringWithFormat:@":%@", self.className];
}
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p '%@'>", self.class, self, self.debugDescription];
}
@end
+20
View File
@@ -0,0 +1,20 @@
//
// CSSPseudoFunctionSelector.h
// HTMLKit
//
// Created by Iska on 07/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import "CSSSelector.h"
NS_ASSUME_NONNULL_BEGIN
@interface CSSPseudoFunctionSelector : CSSSelector
+ (instancetype)notSelector:(CSSSelector *)selector;
+ (instancetype)hasSelector:(CSSSelector *)selector;
@end
NS_ASSUME_NONNULL_END
+89
View File
@@ -0,0 +1,89 @@
//
// CSSPseudoFunctionSelector.m
// HTMLKit
//
// Created by Iska on 07/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import "CSSPseudoFunctionSelector.h"
#import "HTMLElement.h"
#pragma mark - Declarations
@interface CSSNotSelector : CSSPseudoFunctionSelector
@end
@interface CSSHasSelector : CSSPseudoFunctionSelector
@end
#pragma mark - Base Function Selector
@interface CSSPseudoFunctionSelector ()
{
CSSSelector *_selector;
}
@property (nonatomic, strong, readonly) CSSSelector *selector;
@end
@implementation CSSPseudoFunctionSelector
@synthesize selector = _selector;
+ (instancetype)notSelector:(CSSSelector *)selector
{
return [[CSSNotSelector alloc] initWithSelector:selector];
}
+ (instancetype)hasSelector:(CSSSelector *)selector
{
return [[CSSHasSelector alloc] initWithSelector:selector];
}
- (instancetype)initWithSelector:(CSSSelector *)selector
{
self = [super init];
if (self) {
_selector = selector;
}
return self;
}
@end
#pragma mark - Not Selector
@implementation CSSNotSelector
- (BOOL)acceptElement:(HTMLElement *)element
{
return ![self.selector acceptElement:element];
}
- (NSString *)debugDescription
{
return [NSString stringWithFormat:@":not(%@)", self.selector.debugDescription];
}
@end
#pragma mark - Has Selector
@implementation CSSHasSelector
- (BOOL)acceptElement:(HTMLElement *)element
{
for (HTMLNode *child in element.childNodes) {
if (child.nodeType == HTMLNodeElement && [self.selector acceptElement:child.asElement]) {
return YES;
}
}
return NO;
}
- (NSString *)debugDescription
{
return [NSString stringWithFormat:@":has(%@)", self.selector.debugDescription];
}
@end
+49
View File
@@ -0,0 +1,49 @@
//
// HTMLSelector.h
// HTMLKit
//
// Created by Iska on 02/05/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
#pragma mark - Attribute Selector Type
typedef NS_ENUM(NSUInteger, CSSAttributeSelectorType)
{
CSSAttributeSelectorExists,
CSSAttributeSelectorExactMatch,
CSSAttributeSelectorIncludes,
CSSAttributeSelectorBegins,
CSSAttributeSelectorEnds,
CSSAttributeSelectorContains,
CSSAttributeSelectorHyphen,
CSSAttributeSelectorNot
};
#pragma mark - CSS Nth-Expression
typedef struct CSSNthExpression
{
NSInteger an;
NSInteger b;
} CSSNthExpression;
NS_INLINE CSSNthExpression CSSNthExpressionMake(NSInteger an, NSInteger b) {
return (CSSNthExpression){ .an = an, .b = b };
}
extern const CSSNthExpression CSSNthExpressionOdd;
extern const CSSNthExpression CSSNthExpressionEven;
extern NSString * _Nonnull NSStringFromNthExpression(CSSNthExpression expression);
#pragma mark - Base Selector Class
@class HTMLElement;
@interface CSSSelector : NSObject
- (BOOL)acceptElement:(nonnull HTMLElement *)element;
@end
+33
View File
@@ -0,0 +1,33 @@
//
// CSSSelector.m
// HTMLKit
//
// Created by Iska on 15/10/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "CSSSelector.h"
@implementation CSSSelector
- (BOOL)acceptElement:(HTMLElement *)element
{
[self doesNotRecognizeSelector:_cmd];
return NO;
}
#pragma mark - Description
- (NSString *)debugDescription
{
[self doesNotRecognizeSelector:_cmd];
return @"";
}
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p '%@'>", self.class, self, self.debugDescription];
}
@end
+21
View File
@@ -0,0 +1,21 @@
//
// CSSSelectorBlock.h
// HTMLKit
//
// Created by Iska on 20/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import "CSSSelector.h"
NS_ASSUME_NONNULL_BEGIN
@class HTMLElement;
@interface CSSSelectorBlock : CSSSelector
- (instancetype)initWithName:(NSString *)name block:(BOOL (^)(HTMLElement *))block;
@end
NS_ASSUME_NONNULL_END
+40
View File
@@ -0,0 +1,40 @@
//
// CSSSelectorBlock.m
// HTMLKit
//
// Created by Iska on 20/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import "CSSSelectorBlock.h"
@interface CSSSelectorBlock ()
{
NSString *_name;
BOOL (^ _acceptBlock)(HTMLElement *);
}
@end
@implementation CSSSelectorBlock
- (instancetype)initWithName:(NSString *)name block:(BOOL (^)(HTMLElement *))block
{
self = [super init];
if (self) {
_name = [name copy];
_acceptBlock = [block copy];
}
return self;
}
- (BOOL)acceptElement:(HTMLElement *)element
{
return _acceptBlock ? _acceptBlock(element) : NO;
}
- (NSString *)debugDescription
{
return _name;
}
@end
+17
View File
@@ -0,0 +1,17 @@
//
// CSSSelectorParser.h
// HTMLKit
//
// Created by Iska on 02/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
@class CSSSelector;
@interface CSSSelectorParser : NSObject
+ (CSSSelector *)parseSelector:(NSString *)string error:(NSError **)error;
@end
+367
View File
@@ -0,0 +1,367 @@
//
// CSSSelectorParser.m
// HTMLKit
//
// Created by Iska on 02/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import "CSSSelectorParser.h"
#import "CSSInputStream.h"
#import "CSSCodePoints.h"
#import "CSSSelectors.h"
#import "NSString+HTMLKit.h"
#import "NSCharacterSet+HTMLKit.h"
#import "CSSNthExpressionParser.h"
#import "CSSCompoundSelector.h"
#import "HTMLKitErrorDomain.h"
@interface CSSSelectorParser ()
{
NSString *_string;
CSSInputStream *_inputStream;
NSUInteger _location;
NSMutableArray *_selectors;
}
@end
@implementation CSSSelectorParser
+ (CSSSelector *)parseSelector:(NSString *)string error:(NSError * __autoreleasing *)error
{
CSSSelectorParser *parser = [[CSSSelectorParser alloc] initWithString:string];
CSSSelector *selector = [parser parse:error];
return selector;
}
#pragma mark - Init
- (instancetype)initWithString:(NSString *)string
{
self = [super init];
if (self) {
_string = [self preprocessInput:string];
_location = 0;
}
return self;
}
- (NSString *)preprocessInput:(NSString *)string
{
string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
string = [string stringByReplacingOccurrencesOfString:@"\r\n" withString:@"\n"];
string = [string stringByReplacingOccurrencesOfString:@"\r" withString:@"\n"];
string = [string stringByReplacingOccurrencesOfString:@"\f" withString:@"\n"];
string = [string stringByReplacingOccurrencesOfString:@"\0" withString:@"\uFFFD"];
return string;
}
#pragma mark - Errors
- (void)emitError:(NSError * __autoreleasing *)error reason:(NSString *)reason location:(NSUInteger)location
{
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey: @"Error parsing selector",
NSLocalizedFailureReasonErrorKey: reason,
CSSSelectorStringKey: _string,
CSSSelectorErrorLocationKey: @(location)
};
if(error) {
*error = [NSError errorWithDomain:HTMLKitSelectorErrorDomain code:HTMLKitSelectorParseError userInfo:userInfo];
}
}
#pragma mark - Parsing
- (CSSSelector *)parse:(NSError * __autoreleasing *)error
{
if (_string.length == 0) {
[self emitError:error reason:@"Empty selector" location:0];
return nil;
}
NSArray *allSubSelectors = [_string componentsSeparatedByString:@","];
NSMutableArray *parsed = [NSMutableArray array];
for (NSString *subSelector in allSubSelectors) {
if ([subSelector isEqualToString:@""]) {
[self emitError:error reason:@"Empty selector" location:_location];
break;
}
CSSSelector *selector = [self parseSelector:subSelector error:error];
if (selector == nil) {
break;
}
[parsed addObject:selector];
_location += subSelector.length;
}
if (*error != nil) {
return nil;
}
if (parsed.count > 1) {
return anyOf(parsed);
}
return parsed.firstObject;
}
- (CSSSelector *)parseSelector:(NSString *)selectorString error:(NSError * __autoreleasing *)error
{
_inputStream = [[CSSInputStream alloc] initWithString:selectorString];
[_inputStream consumeWhitespace];
CSSSelector *result = nil;
while (YES) {
CSSSelector *selector = [self parseSequenceOfSimpleSelectors:error];
if (selector == nil) {
break;
}
result = result ? allOf(@[result, selector]) : selector;
UTF32Char next = _inputStream.nextInputCharacter;
if (isCombinator(next)) {
NSString *combinator = [_inputStream consumeCombinator];
if ([combinator isEqualToString:@""]) {
result = descendantOfElementSelector(result);
} else if ([combinator isEqualToString:@">"]) {
result = childOfElementSelector(result);
} else if ([combinator isEqualToString:@"+"]) {
result = adjacentSiblingSelector(result);
} else if ([combinator isEqualToString:@"~"]) {
result = generalSiblingSelector(result);
}
}
}
return result;
}
- (CSSSelector *)parseSequenceOfSimpleSelectors:(NSError * __autoreleasing *)error
{
NSMutableArray *selectors = [NSMutableArray array];
CSSSelector *typeSelector = [self parseTypeSelector:error];
if (typeSelector != nil) {
[selectors addObject:typeSelector];
}
while (YES) {
UTF32Char next = _inputStream.nextInputCharacter;
if (next == EOF || isCombinator(next)) {
break;
}
CSSSelector *simpleSelector = [self parseSimpleSelector:error];
if (simpleSelector == nil) {
return nil;
}
[selectors addObject:simpleSelector];
}
if (selectors.count > 1) {
return allOf(selectors);
}
return selectors.firstObject;
}
- (CSSSelector *)parseTypeSelector:(NSError * __autoreleasing *)error
{
NSString *identifier = [_inputStream consumeIdentifier];
if (identifier != nil) {
return typeSelector(identifier);
}
if ([_inputStream consumeCharacter:ASTERIX]) {
return universalSelector();
}
return nil;
}
- (CSSSelector *)parseSimpleSelector:(NSError * __autoreleasing *)error
{
CSSSelector *typeSelector = [self parseTypeSelector:error];
if (typeSelector != nil) {
return typeSelector;
}
UTF32Char codePoint = [_inputStream consumeNextInputCharacter];
switch (codePoint) {
case NUMBER_SIGN:
{
NSString *elementId = [_inputStream consumeIdentifier];
if (elementId == nil) {
[self emitError:error reason:@"Invalid character" location:_location + _inputStream.currentLocation];
return nil;
}
return idSelector(elementId);
}
case FULL_STOP:
{
NSString *className = [_inputStream consumeIdentifier];
if (className == nil) {
[self emitError:error reason:@"Invalid character" location:_location + _inputStream.currentLocation];
return nil;
}
return classSelector(className);
}
case LEFT_SQUARE_BRACKET:
{
return [self parseAttributeSelector:error];
}
case COLON:
{
return [self parsePseudoSelector:error];
}
default:
{
[self emitError:error reason:@"Invalid character" location:_location + _inputStream.currentLocation];
return nil;
}
}
}
- (CSSSelector *)parseAttributeSelector:(NSError * __autoreleasing *)error
{
NSString *attribute = [_inputStream consumeIdentifier];
if (attribute == nil) {
[self emitError:error reason:@"Invalid character" location:_location + _inputStream.currentLocation + 1];
return nil;
}
[_inputStream consumeWhitespace];
CSSAttributeSelectorType type = CSSAttributeSelectorExists;
NSString *operator = [_inputStream consumeCharactersInString:@"=~|^$*!"];
if ([operator isEqualToString:@"="]) {
type = CSSAttributeSelectorExactMatch;
} else if ([operator isEqualToString:@"~="]) {
type = CSSAttributeSelectorIncludes;
} else if ([operator isEqualToString:@"|="]) {
type = CSSAttributeSelectorHyphen;
} else if ([operator isEqualToString:@"^="]) {
type = CSSAttributeSelectorBegins;
} else if ([operator isEqualToString:@"$="]) {
type = CSSAttributeSelectorEnds;
} else if ([operator isEqualToString:@"*="]) {
type = CSSAttributeSelectorContains;
} else if ([operator isEqualToString:@"!="]) {
type = CSSAttributeSelectorNot;
}
NSString *value = nil;
[_inputStream consumeWhitespace];
UTF32Char next = _inputStream.nextInputCharacter;
if (isQuote(next)) {
UTF32Char quote = [_inputStream consumeNextInputCharacter];
value = [_inputStream consumeStringWithEndingCodePoint:quote];
} else {
value = [_inputStream consumeIdentifier];
}
[_inputStream consumeWhitespace];
// Consume RIGHT_SQUARE_BRACKET
if (![_inputStream consumeCharacter:RIGHT_SQUARE_BRACKET]) {
[self emitError:error reason:@"Expected closing right square bracket ']'" location:_location + _inputStream.currentLocation];
}
if (type == CSSAttributeSelectorExists) {
return hasAttributeSelector(attribute);
}
return attributeSelector(type, attribute, value);
}
- (CSSSelector *)parsePseudoSelector:(NSError * __autoreleasing *)error
{
NSString *pseudoClass = [_inputStream consumeIdentifier];
if ([pseudoClass hasPrefix:@"nth"]) {
[_inputStream consumeWhitespace];
if (![_inputStream consumeCharacter:LEFT_PARENTHESIS]) {
[self emitError:error reason:@"Expected opening left parenthesis '('" location:_location + _inputStream.currentLocation];
}
NSString *functionExpression = [_inputStream consumeCharactersUpToString:@")"];
CSSNthExpression expression = [CSSNthExpressionParser parseExpression:functionExpression];
[_inputStream consumeWhitespace];
if (![_inputStream consumeCharacter:RIGHT_PARENTHESIS]) {
[self emitError:error reason:@"Expected closing right parenthesis ')'" location:_location + _inputStream.currentLocation];
}
if ([pseudoClass isEqualToString:@"nth-child"]) {
return nthChildSelector(expression);
} else if ([pseudoClass isEqualToString:@"nth-last-child"]) {
return nthLastChildSelector(expression);
} else if ([pseudoClass isEqualToString:@"nth-of-type"]) {
return nthOfTypeSelector(expression);
} else if ([pseudoClass isEqualToString:@"nth-last-of-type"]) {
return nthLastOfTypeSelector(expression);
}
} else if ([pseudoClass isEqualToString:@"not"]) {
[_inputStream consumeWhitespace];
if (![_inputStream consumeCharacter:LEFT_PARENTHESIS]) {
[self emitError:error reason:@"Expected opening left parenthesis '('" location:_location + _inputStream.currentLocation];
}
CSSSelector *subSelector = [self parseSimpleSelector:error];
[_inputStream consumeWhitespace];
if (![_inputStream consumeCharacter:RIGHT_PARENTHESIS]) {
[self emitError:error reason:@"Expected closing right parenthesis ')'" location:_location + _inputStream.currentLocation];
}
return nay(subSelector);
} else {
if ([pseudoClass isEqualToString:@"even"]) {
return evenSlector();
} else if ([pseudoClass isEqualToString:@"odd"]) {
return oddSelector();
} else if ([pseudoClass isEqualToString:@"first-child"]) {
return firstChildSelector();
} else if ([pseudoClass isEqualToString:@"last-child"]) {
return lastChildSelector();
} else if ([pseudoClass isEqualToString:@"first-of-type"]) {
return firstOfTypeSelector();
} else if ([pseudoClass isEqualToString:@"last-of-type"]) {
return lastOfTypeSelector();
} else if ([pseudoClass isEqualToString:@"only-child"]) {
return onlyChildSelector();
} else if ([pseudoClass isEqualToString:@"only-of-type"]) {
return onlyOfTypeSelector();
} else if ([pseudoClass isEqualToString:@"root"]) {
return rootSelector();
} else if ([pseudoClass isEqualToString:@"empty"]) {
return emptySelector();
} else if ([pseudoClass isEqualToString:@"link"]) {
return linkSelector();
} else if ([pseudoClass isEqualToString:@"enabled"]) {
return enabledSelector();
} else if ([pseudoClass isEqualToString:@"disabled"]) {
return disabledSelector();
} else if ([pseudoClass isEqualToString:@"checked"]) {
return checkedSelector();
}
}
NSString *reason = [NSString stringWithFormat:@"Unknown pseudo class: %@", pseudoClass];
[self emitError:error reason:reason location:_location + _inputStream.currentLocation];
return nil;
}
@end
+71
View File
@@ -0,0 +1,71 @@
//
// CSSSelectors.h
// HTMLKit
//
// Created by Iska on 14/05/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import "CSSSelector.h"
#import "CSSStructuralPseudoSelectors.h"
NS_ASSUME_NONNULL_BEGIN
#pragma mark - Type Selectors
extern CSSSelector * universalSelector();
extern CSSSelector * typeSelector(NSString *type);
#pragma mark - Atribute Selectors
extern CSSSelector * idSelector(NSString *elementId);
extern CSSSelector * classSelector(NSString *className);
extern CSSSelector * hasAttributeSelector(NSString *attribute);
extern CSSSelector * attributeSelector(CSSAttributeSelectorType type,
NSString *attribute,
NSString *value);
#pragma mark - Nth-Expression Selectors
extern CSSSelector * nthChildSelector(CSSNthExpression expression);
extern CSSSelector * nthLastChildSelector(CSSNthExpression expression);
extern CSSSelector * nthOfTypeSelector(CSSNthExpression expression);
extern CSSSelector * nthLastOfTypeSelector(CSSNthExpression expression);
extern CSSSelector * oddSelector();
extern CSSSelector * evenSlector();
extern CSSSelector * firstChildSelector();
extern CSSSelector * lastChildSelector();
extern CSSSelector * firstOfTypeSelector();
extern CSSSelector * lastOfTypeSelector();
extern CSSSelector * onlyChildSelector();
extern CSSSelector * onlyOfTypeSelector();
#pragma mark - Combinators
extern CSSSelector * childOfElementSelector(CSSSelector *selector);
extern CSSSelector * descendantOfElementSelector(CSSSelector *selector);
extern CSSSelector * adjacentSiblingSelector(CSSSelector *selector);
extern CSSSelector * generalSiblingSelector(CSSSelector *selector);
#pragma mark - Pseudo Functions
extern CSSSelector * nay(CSSSelector *selector);
extern CSSSelector * has(CSSSelector *selector);
#pragma mark - Compound Selectors
extern CSSSelector * allOf(NSArray<CSSSelector *> *selectors);
extern CSSSelector * anyOf(NSArray<CSSSelector *> *selectors);
#pragma mark - Pseudo
extern CSSSelector * namedPseudoSelector(NSString *name, CSSSelector *selector);
#pragma mark - Block
extern CSSSelector * namedBlockSelector(NSString *name, BOOL (^ acceptBlock)(HTMLElement *element));
NS_ASSUME_NONNULL_END
+177
View File
@@ -0,0 +1,177 @@
//
// CSSSelectors.m
// HTMLKit
//
// Created by Iska on 19/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import "CSSSelectors.h"
#import "CSSTypeSelector.h"
#import "CSSAttributeSelector.h"
#import "CSSPseudoClassSelector.h"
#import "CSSPseudoFunctionSelector.h"
#import "CSSNthExpressionSelector.h"
#import "CSSCombinatorSelector.h"
#import "CSSCompoundSelector.h"
#import "CSSSelectorBlock.h"
#pragma mark - Type Selectors
CSSSelector * universalSelector()
{
return [CSSTypeSelector universalSelector];
}
CSSSelector * typeSelector(NSString *type)
{
return [[CSSTypeSelector alloc] initWithType:type];
}
#pragma mark - Atribute Selectors
CSSSelector * idSelector(NSString *elementId)
{
return [CSSAttributeSelector idSelector:elementId];
}
CSSSelector * classSelector(NSString *className)
{
return [CSSAttributeSelector classSelector:className];
}
CSSSelector * hasAttributeSelector(NSString *attribute)
{
return [CSSAttributeSelector hasAttributeSelector:attribute];
}
CSSSelector * attributeSelector(CSSAttributeSelectorType type,
NSString *attribute,
NSString *value)
{
return [[CSSAttributeSelector alloc] initWithType:type attributeName:attribute attrbiuteValue:value];
}
#pragma mark - Nth-Expression Selectors
CSSSelector * nthChildSelector(CSSNthExpression expression)
{
return [CSSNthExpressionSelector nthChildSelector:expression];
}
CSSSelector * nthLastChildSelector(CSSNthExpression expression)
{
return [CSSNthExpressionSelector nthLastChildSelector:expression];
}
CSSSelector * nthOfTypeSelector(CSSNthExpression expression)
{
return [CSSNthExpressionSelector nthOfTypeSelector:expression];
}
CSSSelector * nthLastOfTypeSelector(CSSNthExpression expression)
{
return [CSSNthExpressionSelector nthLastOfTypeSelector:expression];
}
#pragma mark - Nth-Expression Shorthand
CSSSelector * oddSelector()
{
return namedPseudoSelector(@"odd", nthChildSelector(CSSNthExpressionOdd));
}
CSSSelector * evenSlector()
{
return namedPseudoSelector(@"even", nthChildSelector(CSSNthExpressionEven));
}
CSSSelector * firstChildSelector()
{
return namedPseudoSelector(@"first-child", nthChildSelector(CSSNthExpressionMake(0, 1)));
}
CSSSelector * lastChildSelector()
{
return namedPseudoSelector(@"last-child", nthLastChildSelector(CSSNthExpressionMake(0, 1)));
}
CSSSelector * firstOfTypeSelector()
{
return namedPseudoSelector(@"first-of-type", nthOfTypeSelector(CSSNthExpressionMake(0, 1)));
}
CSSSelector * lastOfTypeSelector()
{
return namedPseudoSelector(@"last-of-type", nthLastOfTypeSelector(CSSNthExpressionMake(0, 1)));
}
CSSSelector * onlyChildSelector()
{
return namedPseudoSelector(@"only-child", allOf(@[firstChildSelector(), lastChildSelector()]));
}
CSSSelector * onlyOfTypeSelector()
{
return namedPseudoSelector(@"only-of-type", allOf(@[firstOfTypeSelector(), lastOfTypeSelector()]));
}
#pragma mark - Combinators
CSSSelector * childOfElementSelector(CSSSelector *selector)
{
return [CSSCombinatorSelector childOfElementCombinator:selector];
}
CSSSelector * descendantOfElementSelector(CSSSelector *selector)
{
return [CSSCombinatorSelector descendantOfElementCombinator:selector];
}
CSSSelector * adjacentSiblingSelector(CSSSelector *selector)
{
return [CSSCombinatorSelector adjacentSiblingCombinator:selector];
}
CSSSelector * generalSiblingSelector(CSSSelector *selector)
{
return [CSSCombinatorSelector generalSiblingCombinator:selector];
}
#pragma mark - Pseudo Functions
CSSSelector * nay(CSSSelector *selector)
{
return [CSSPseudoFunctionSelector notSelector:selector];
}
CSSSelector * has(CSSSelector *selector)
{
return [CSSPseudoFunctionSelector hasSelector:selector];
}
#pragma mark - Compound Selectors
CSSSelector * allOf( NSArray<CSSSelector *> * selectors)
{
return [CSSCompoundSelector andSelector:selectors];
}
CSSSelector * anyOf( NSArray<CSSSelector *> * selectors)
{
return [CSSCompoundSelector orSelector:selectors];
}
#pragma mark - Pseudo
CSSSelector * namedPseudoSelector(NSString *name, CSSSelector *selector)
{
return [[CSSPseudoClassSelector alloc] initWithClassName:name selector:selector];
}
#pragma mark - Block
CSSSelector * namedBlockSelector(NSString *name, BOOL (^ acceptBlock)(HTMLElement *element))
{
return [[CSSSelectorBlock alloc] initWithName:name block:acceptBlock];
}
+40
View File
@@ -0,0 +1,40 @@
//
// CSSStructuralPseudoSelector.h
// HTMLKit
//
// Created by Iska on 11/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
@class CSSSelector;
NS_ASSUME_NONNULL_BEGIN
extern CSSSelector * rootSelector();
extern CSSSelector * emptySelector();
extern CSSSelector * parentSelector();
extern CSSSelector * buttonSelector();
extern CSSSelector * checkboxSelector();
extern CSSSelector * fileSelector();
extern CSSSelector * headerSelector();
extern CSSSelector * imageSelector();
extern CSSSelector * inputSelector();
extern CSSSelector * linkSelector();
extern CSSSelector * passwordSelector();
extern CSSSelector * radioSelector();
extern CSSSelector * resetSelector();
extern CSSSelector * submitSelector();
extern CSSSelector * textSelector();
extern CSSSelector * enabledSelector();
extern CSSSelector * disabledSelector();
extern CSSSelector * checkedSelector();
extern CSSSelector * optionalSelector();
extern CSSSelector * requiredSelector();
extern CSSSelector * ltSelector();
extern CSSSelector * gtSelector();
extern CSSSelector * eqSelector();
NS_ASSUME_NONNULL_END
+301
View File
@@ -0,0 +1,301 @@
//
// CSSStructuralPseudoSelector.m
// HTMLKit
//
// Created by Iska on 11/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import "CSSStructuralPseudoSelectors.h"
#import "CSSSelectors.h"
#import "HTMLElement.h"
#import "NSString+HTMLKit.h"
#pragma mark - Elements
CSSSelector * rootSelector()
{
return namedBlockSelector(@":root", ^BOOL(HTMLElement * element) {
return element.parentElement == nil;
});
}
CSSSelector * emptySelector()
{
return namedBlockSelector(@":empty", ^BOOL(HTMLElement * element) {
for (HTMLNode *child in element.childNodes) {
if (child.nodeType == HTMLNodeElement) {
return NO;
} else if (child.nodeType == HTMLNodeText && child.textContent.length > 0) {
return NO;
}
}
return YES;
});
}
CSSSelector * parentSelector()
{
return namedBlockSelector(@":parent", ^BOOL(HTMLElement * element) {
return element.childNodesCount > 0;
});
}
CSSSelector * buttonSelector()
{
return namedBlockSelector(@":button", ^BOOL(HTMLElement * _Nonnull element) {
if ([element.tagName isEqualToString:@"button"]) {
return YES;
}
if ([element.tagName isEqualToString:@"input"] && [element[@"type"] isEqualToString:@"button"]) {
return YES;
}
return NO;
});
}
CSSSelector * checkboxSelector()
{
return namedBlockSelector(@":checkbox", ^BOOL(HTMLElement * _Nonnull element) {
if ([element[@"type"] isEqualToString:@"checkbox"]) {
return YES;
}
return NO;
});
}
CSSSelector * fileSelector()
{
return namedBlockSelector(@":file", ^BOOL(HTMLElement * _Nonnull element) {
if ([element[@"type"] isEqualToString:@"file"]) {
return YES;
}
return NO;
});
}
CSSSelector * headerSelector()
{
return namedBlockSelector(@":header", ^BOOL(HTMLElement * _Nonnull element) {
if ([element.tagName isEqualToAny:@"h1", @"h2", @"h3", @"h4", @"h5", @"h6", nil]) {
return YES;
}
return NO;
});
}
CSSSelector * imageSelector()
{
return namedBlockSelector(@":image", ^BOOL(HTMLElement * _Nonnull element) {
if ([element[@"type"] isEqualToString:@"image"]) {
return YES;
}
return NO;
});
}
CSSSelector * inputSelector()
{
return namedBlockSelector(@":input", ^BOOL(HTMLElement * _Nonnull element) {
if ([element.tagName isEqualToAny:@"button", @"input", @"select", @"textarea", nil]) {
return YES;
}
return NO;
});
}
CSSSelector * linkSelector()
{
// https://html.spec.whatwg.org/multipage/scripting.html#selector-link
return namedBlockSelector(@":link", ^BOOL(HTMLElement * element) {
if ([element hasAttribute:@"href"]) {
return [element.tagName isEqualToAny:@"a", @"area", @"link", nil];
}
return NO;
});
}
CSSSelector * passwordSelector()
{
return namedBlockSelector(@":password", ^BOOL(HTMLElement * _Nonnull element) {
if ([element[@"type"] isEqualToString:@"password"]) {
return YES;
}
return NO;
});
}
CSSSelector * radioSelector()
{
return namedBlockSelector(@":radio", ^BOOL(HTMLElement * _Nonnull element) {
if ([element[@"type"] isEqualToString:@"radio"]) {
return YES;
}
return NO;
});
}
CSSSelector * resetSelector()
{
return namedBlockSelector(@":reset", ^BOOL(HTMLElement * _Nonnull element) {
if ([element[@"type"] isEqualToString:@"reset"]) {
return YES;
}
return NO;
});
}
CSSSelector * submitSelector()
{
return namedBlockSelector(@":submit", ^BOOL(HTMLElement * _Nonnull element) {
if ([element.tagName isEqualToString:@"input"] && [element[@"type"] isEqualToString:@"submit"]) {
return YES;
}
if ([element.tagName isEqualToString:@"button"] && [element[@"type"] isEqualToString:@"submit"]) {
return YES;
}
return NO;
});
}
CSSSelector * textSelector()
{
return namedBlockSelector(@":text", ^BOOL(HTMLElement * _Nonnull element) {
if ([element[@"type"] isEqualToString:@"text"]) {
return YES;
}
return NO;
});
}
#pragma mark - State
CSSSelector * enabledSelector()
{
// https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled
CSSSelector *candiate = anyOf(@[
typeSelector(@"button"),
typeSelector(@"input"),
typeSelector(@"select"),
typeSelector(@"textarea"),
typeSelector(@"optgroup"),
typeSelector(@"option"),
typeSelector(@"menuitem"),
typeSelector(@"fieldset"),
]);
return namedPseudoSelector(@"enabled", allOf(@[candiate, nay(disabledSelector())]));
}
CSSSelector * disabledSelector()
{
// https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
CSSSelector *disabledAttribute = hasAttributeSelector(@"disabled");
// https://html.spec.whatwg.org/multipage/forms.html#concept-fieldset-disabled
CSSSelector *disabledFieldset = allOf(@[typeSelector(@"fieldset"), disabledAttribute]);
CSSSelector *firstLegend = allOf(@[typeSelector(@"legend"), firstOfTypeSelector()]);
CSSSelector *firstLegendDecendantDisabledFieldSet = allOf(@[firstLegend, descendantOfElementSelector(disabledFieldset)]);
// https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled
CSSSelector *disabledForm = anyOf(@[
anyOf(@[
allOf(@[typeSelector(@"button"), disabledAttribute]),
allOf(@[typeSelector(@"input"), disabledAttribute]),
allOf(@[typeSelector(@"select"), disabledAttribute]),
allOf(@[typeSelector(@"textarea"), disabledAttribute])
]),
allOf(@[
descendantOfElementSelector(disabledFieldset),
nay(firstLegendDecendantDisabledFieldSet)
])
]);
// https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
CSSSelector *disabledMenuItem = allOf(@[typeSelector(@"menuitem"), disabledAttribute]);
CSSSelector *disabledOptgroup = allOf(@[typeSelector(@"optgroup"), disabledAttribute]);
// https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled
CSSSelector *disabledOption = allOf(@[
typeSelector(@"option"),
anyOf(@[
disabledAttribute,
descendantOfElementSelector(disabledOptgroup)])
]);
return namedPseudoSelector(@"disabled",
anyOf(@[disabledOption, disabledOptgroup, disabledMenuItem, disabledForm, disabledFieldset]));
}
CSSSelector * checkedSelector()
{
// https://html.spec.whatwg.org/multipage/scripting.html#selector-checked
CSSSelector *candidate = anyOf(@[
typeSelector(@"input"),
typeSelector(@"option"),
typeSelector(@"menutitem")
]);
CSSSelector *hasAttribute = anyOf(@[
hasAttributeSelector(@"checked"),
hasAttributeSelector(@"selected")
]);
return namedPseudoSelector(@"checked", allOf(@[candidate, hasAttribute]));
}
CSSSelector * optionalSelector()
{
// https://html.spec.whatwg.org/multipage/scripting.html#selector-optional
CSSSelector *candidate = anyOf(@[
typeSelector(@"input"),
typeSelector(@"select"),
typeSelector(@"textarea")
]);
CSSSelector *noAttribute = nay(hasAttributeSelector(@"required"));
return namedPseudoSelector(@"optional", allOf(@[candidate, noAttribute]));
}
CSSSelector * requiredSelector()
{
// https://html.spec.whatwg.org/multipage/scripting.html#selector-required
// https://html.spec.whatwg.org/multipage/forms.html#concept-input-required
CSSSelector *candidate = anyOf(@[
typeSelector(@"input"),
typeSelector(@"select"),
typeSelector(@"textarea")
]);
CSSSelector *hasAttribute = hasAttributeSelector(@"required");
return namedPseudoSelector(@"required", allOf(@[candidate, hasAttribute]));
}
#pragma mark - Positional
CSSSelector * ltSelector(NSUInteger index)
{
NSString *name = [NSString stringWithFormat:@":lt(%lu)", (unsigned long)index];
return namedBlockSelector(name, ^BOOL(HTMLElement * _Nonnull element) {
return [element.parentElement indexOfChildNode:element] < index;
});
}
CSSSelector * gtSelector(NSUInteger index)
{
NSString *name = [NSString stringWithFormat:@":gt(%lu)", (unsigned long)index];
return namedBlockSelector(name, ^BOOL(HTMLElement * _Nonnull element) {
return [element.parentElement indexOfChildNode:element] > index;
});
}
CSSSelector * eqSelector(NSInteger index)
{
NSString *name = [NSString stringWithFormat:@":eq(%lu)", (unsigned long)index];
return namedBlockSelector(name, ^BOOL(HTMLElement * _Nonnull element) {
NSUInteger elementIndex = [element.parentElement indexOfChildNode:element];
if (index > 0) {
return elementIndex == index;
} else {
return elementIndex == element.parentElement.childNodesCount - index - 1;
}
});
}
+24
View File
@@ -0,0 +1,24 @@
//
// CSSTypeSelector.h
// HTMLKit
//
// Created by Iska on 13/05/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "CSSSelector.h"
NS_ASSUME_NONNULL_BEGIN
@interface CSSTypeSelector : CSSSelector
@property (nonatomic, strong, readonly) NSString *type;
+ (instancetype)universalSelector;
- (instancetype)initWithType:(NSString *)type;
@end
NS_ASSUME_NONNULL_END
+50
View File
@@ -0,0 +1,50 @@
//
// CSSTypeSelector.m
// HTMLKit
//
// Created by Iska on 13/05/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import "CSSTypeSelector.h"
#import "HTMLElement.h"
#import "NSString+HTMLKit.h"
@interface CSSTypeSelector ()
{
NSString *_type;
}
@end
@implementation CSSTypeSelector
+ (instancetype)universalSelector
{
return [[self alloc] initWithType:@"*"];
}
- (instancetype)initWithType:(NSString *)type
{
self = [super init];
if (self) {
_type = [type copy];
}
return self;
}
- (BOOL)acceptElement:(HTMLElement *)element
{
if ([_type isEqualToString:@"*"] || [_type isEqualToStringIgnoringCase:element.tagName]) {
return YES;
}
return NO;
}
#pragma mark - Description
- (NSString *)debugDescription
{
return self.type;
}
@end
+5
View File
@@ -10,6 +10,11 @@
@implementation HTMLComment
- (instancetype)init
{
return [self initWithData:nil];
}
- (instancetype)initWithData:(NSString *)data
{
self = [super initWithName:@"#comment" type:HTMLNodeComment];
+4 -1
View File
@@ -9,8 +9,11 @@
#import "HTMLNode.h"
#import "HTMLDocument.h"
#import "HTMLDocumentType.h"
#import "HTMLDocumentFragment.h"
#import "HTMLElement.h"
#import "HTMLComment.h"
#import "HTMLText.h"
#import "HTMLTemplate.h"
#import "HTMLDocumentFragment.h"
#import "HTMLNodeIterator.h"
#import "HTMLTreeWalker.h"
#import "HTMLNodeFilter.h"
+10
View File
@@ -27,6 +27,16 @@ typedef NS_ENUM(short, HTMLDocumentReadyState)
@property (nonatomic, assign, readonly) HTMLDocumentReadyState readyState;
@property (nonatomic, strong) HTMLElement *rootElement;
@property (nonatomic, strong) HTMLElement *documentElement;
@property (nonatomic, strong) HTMLElement *head;
@property (nonatomic, strong) HTMLElement *body;
+ (instancetype)documentWithString:(NSString *)string;
- (HTMLNode *)adoptNode:(HTMLNode *)node;
- (HTMLDocument *)associatedInertTemplateDocument;
+103 -2
View File
@@ -7,16 +7,25 @@
//
#import "HTMLDocument.h"
#import "HTMLKitExceptions.h"
#import "HTMLParser.h"
#import "HTMLNodeIterator.h"
#import "HTMLKitDOMExceptions.h"
@interface HTMLNode (Private)
@property (nonatomic, weak) HTMLDocument *ownerDocument;
@property (nonatomic, weak) HTMLNode *parentNode;
@end
@interface HTMLNodeIterator (Private)
- (void)runRemovingStepsForNode:(HTMLNode *)oldNode
withOldParent:(HTMLNode *)oldParent
andOldPreviousSibling:(HTMLNode *)oldPreviousSibling;
@end
@interface HTMLDocument ()
{
HTMLDocument *_inertTemplateDocument;
NSMutableArray *_nodeIterators;
}
@property (nonatomic, assign) HTMLDocumentReadyState readyState;
@end
@@ -25,11 +34,18 @@
#pragma mark - Init
+ (instancetype)documentWithString:(NSString *)string
{
HTMLParser *parser = [[HTMLParser alloc] initWithString:string];
return [parser parseDocument];
}
- (instancetype)init
{
self = [super initWithName:@"#document" type:HTMLNodeDocument];
if (self) {
_readyState = HTMLDocumentLoading;
_nodeIterators = [NSMutableArray new];
}
return self;
}
@@ -57,6 +73,91 @@
}
}
#pragma mark -
- (HTMLElement *)rootElement
{
for (HTMLNode *node = self.firstChild; node; node = node.nextSibling) {
if (node.nodeType == HTMLNodeElement) {
return node.asElement;
}
}
return nil;
}
- (void)setRootElement:(HTMLElement *)rootElement
{
[self replaceChildNode:self.rootElement withNode:rootElement];
}
- (HTMLElement *)documentElement
{
for (HTMLNode *node in [self nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]) {
if ([node.asElement.tagName isEqualToString:@"html"]) {
return node.asElement;
}
}
return nil;
}
- (void)setDocumentElement:(HTMLElement *)documentElement
{
[self replaceChildNode:self.documentElement withNode:documentElement];
}
- (HTMLElement *)head
{
for (HTMLNode *node in [self nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]) {
if ([node.asElement.tagName isEqualToString:@"head"]) {
return node.asElement;
}
}
return nil;
}
- (void)setHead:(HTMLElement *)head
{
[self replaceChildNode:self.head withNode:head];
}
- (HTMLElement *)body
{
for (HTMLNode *node in [self nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]) {
if ([node.asElement.tagName isEqualToString:@"body"]) {
return node.asElement;
}
}
return nil;
}
- (void)setBody:(HTMLElement *)body
{
[self replaceChildNode:self.body withNode:body];
}
#pragma mark - Node Iterators
- (void)attachNodeIterator:(HTMLNodeIterator *)iterator
{
[_nodeIterators addObject:iterator];
}
- (void)detachNodeIterator:(HTMLNodeIterator *)iterator
{
[_nodeIterators removeObject:iterator];
}
- (void)runRemovingStepsForNode:(HTMLNode *)oldNode
withOldParent:(HTMLNode *)oldParent
andOldPreviousSibling:(HTMLNode *)oldPreviousSibling
{
for (HTMLNodeIterator *iterator in _nodeIterators) {
[iterator runRemovingStepsForNode:oldNode
withOldParent:oldParent
andOldPreviousSibling:oldPreviousSibling];
}
}
#pragma mark - Mutation Algorithms
- (HTMLNode *)adoptNode:(HTMLNode *)node
@@ -65,7 +166,7 @@
return nil;
}
if (node.type == HTMLNodeDocument) {
if (node.nodeType == HTMLNodeDocument) {
[NSException raise:HTMLKitNotSupportedError
format:@"%@: Not Fount Error, adopting a document node. The operation is not supported.", NSStringFromSelector(_cmd)];
}
+7 -2
View File
@@ -15,6 +15,11 @@
@implementation HTMLDocumentFragment
- (instancetype)init
{
return [self initWithDocument:nil];
}
- (instancetype)initWithDocument:(HTMLDocument *)document
{
self = [super initWithName:@"#document-fragment" type:HTMLNodeDocumentFragment];
@@ -27,8 +32,8 @@
- (NSString *)textContent
{
NSMutableString *content = [NSMutableString string];
for (HTMLNode *node in self.treeEnumerator) {
if (node.type == HTMLNodeText) {
for (HTMLNode *node in self.nodeIterator) {
if (node.nodeType == HTMLNodeText) {
[content appendString:[(HTMLText *)node data]];
}
}
+5
View File
@@ -22,6 +22,11 @@ NS_INLINE BOOL nilOrEqual(id first, id second) {
@implementation HTMLDocumentType
- (instancetype)init
{
return [self initWithName:@"html" publicIdentifier:nil systemIdentifier:nil];
}
- (instancetype)initWithName:(NSString *)name
publicIdentifier:(NSString *)publicIdentifier
systemIdentifier:(NSString *)systemIdentifier
+1 -1
View File
@@ -24,7 +24,7 @@
- (instancetype)initWithTagName:(NSString *)tagName;
- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes;
- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes namespace:(HTMLNamespace)htmlNamespace;
- (instancetype)initWithTagName:(NSString *)tagName namespace:(HTMLNamespace)htmlNamespace attributes:(NSDictionary *)attributes;
- (BOOL)hasAttribute:(NSString *)name;
- (NSString *)objectForKeyedSubscript:(NSString *)name;
+20 -6
View File
@@ -7,6 +7,7 @@
//
#import "HTMLElement.h"
#import "HTMLParser.h"
#import "HTMLDocument.h"
#import "HTMLText.h"
@@ -35,10 +36,10 @@
- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes
{
return [self initWithTagName:tagName attributes:attributes namespace:HTMLNamespaceHTML];
return [self initWithTagName:tagName namespace:HTMLNamespaceHTML attributes:attributes];
}
- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes namespace:(HTMLNamespace)htmlNamespace
- (instancetype)initWithTagName:(NSString *)tagName namespace:(HTMLNamespace)htmlNamespace attributes:(NSDictionary *)attributes
{
self = [super initWithName:tagName type:HTMLNodeElement];
if (self) {
@@ -87,8 +88,8 @@
- (NSString *)textContent
{
NSMutableString *content = [NSMutableString string];
for (HTMLNode *node in self.treeEnumerator) {
if (node.type == HTMLNodeText) {
for (HTMLNode *node in self.nodeIterator) {
if (node.nodeType == HTMLNodeText) {
[content appendString:[(HTMLText *)node data]];
}
}
@@ -101,6 +102,14 @@
[self replaceAllChildNodesWithNode:node];
}
- (void)setInnerHTML:(NSString *)innerHTML
{
HTMLParser *parser = [[HTMLParser alloc] initWithString:innerHTML];
NSArray *fragmentNodes = [parser parseFragmentWithContextElement:self];
[self removeAllChildNodes];
[self appendNodes:fragmentNodes];
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
@@ -137,8 +146,8 @@
return result;
}
if ([self.tagName isEqualToAny:@"pre", @"textarea", @"listing", nil] && self.firstChiledNode.type == HTMLNodeText) {
HTMLText *textNode = (HTMLText *)self.firstChiledNode;
if ([self.tagName isEqualToAny:@"pre", @"textarea", @"listing", nil] && self.firstChild.nodeType == HTMLNodeText) {
HTMLText *textNode = (HTMLText *)self.firstChild;
if ([textNode.data hasPrefix:@"\n"]) {
[result appendString:@"\n"];
}
@@ -171,4 +180,9 @@
return description;
}
- (NSString *)debugDescription
{
return self.description;
}
@end
+2
View File
@@ -24,6 +24,7 @@ typedef void (^ HTMLStreamReaderErrorCallback)(NSString *reason);
- (UTF32Char)currentInputCharacter;
- (UTF32Char)nextInputCharacter;
- (UTF32Char)inputCharacterPointAtOffset:(NSUInteger)offset;
- (UTF32Char)consumeNextInputCharacter;
- (void)reconsumeCurrentInputCharacter;
@@ -35,6 +36,7 @@ typedef void (^ HTMLStreamReaderErrorCallback)(NSString *reason);
- (BOOL)consumeString:(NSString *)string caseSensitive:(BOOL)caseSensitive;
- (NSString *)consumeCharactersUpToCharactersInString:(NSString *)characters;
- (NSString *)consumeCharactersUpToString:(NSString *)string;
- (NSString *)consumeCharactersInString:(NSString *)characters;
- (NSString *)consumeAlphanumericCharacters;
- (void)markCurrentLocation;
+34 -5
View File
@@ -8,6 +8,7 @@
#import "HTMLInputStreamReader.h"
#import "HTMLTokenizerCharacters.h"
#import "NSCharacterSet+HTMLKit.h"
#pragma mark - HTMLInputStreamReader
@@ -39,6 +40,7 @@
if (self) {
_string = [string copy];
_scanner = [[NSScanner alloc] initWithString:string];
_scanner.charactersToBeSkipped = nil;
CFStringInitInlineBuffer((CFStringRef)_string, &_buffer, CFRangeMake(0, _string.length));
}
return self;
@@ -63,7 +65,6 @@
- (UTF32Char)nextInputCharacter
{
if (_reconsume) {
_reconsume = NO;
return _currentInputCharacter;
}
@@ -106,6 +107,11 @@
return nextInputCharacter;
}
- (UTF32Char)inputCharacterPointAtOffset:(NSUInteger)offset
{
return CFStringGetCharacterFromInlineBuffer(&_buffer, _location + offset);
}
- (UTF32Char)consumeNextInputCharacter
{
if (_reconsume) {
@@ -124,9 +130,12 @@
{
UTF32Char nextInputCharacter = [self nextInputCharacter];
if (nextInputCharacter == character) {
_location += _consume;
_scanner.scanLocation = _location;
_currentInputCharacter = nextInputCharacter;
if (!_reconsume) {
_location += _consume;
_scanner.scanLocation = _location;
_currentInputCharacter = nextInputCharacter;
}
_reconsume = NO;
return YES;
}
return NO;
@@ -145,7 +154,7 @@
- (BOOL)consumeHexNumber:(unsigned long long *)result
{
NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEFabcdef"];
NSCharacterSet *set = [NSCharacterSet HTMLHexNumberCharacterSet];
NSString *string = nil;
BOOL success = [_scanner scanCharactersFromSet:set intoString:&string];
@@ -193,6 +202,26 @@
return consumed;
}
- (NSString *)consumeCharactersInString:(NSString *)characters
{
NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:characters];
if (_reconsume) {
_scanner.scanLocation--;
}
NSString *string = nil;
BOOL success = [_scanner scanCharactersFromSet:set intoString:&string];
if (success == NO) {
_scanner.scanLocation++;
return nil;
}
_reconsume = NO;
_location = _scanner.scanLocation;
return string;
}
- (NSString *)consumeAlphanumericCharacters
{
NSCharacterSet *set = [NSCharacterSet alphanumericCharacterSet];
+4 -5
View File
@@ -6,8 +6,7 @@
// Copyright (c) 2014 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface HTMLKit : NSObject
@end
#import "HTMLParser.h"
#import "HTMLDOM.h"
#import "CSSSelectors.h"
#import "HTMLKitErrorDomain.h"
-13
View File
@@ -1,13 +0,0 @@
//
// HTMLKit.m
// HTMLKit
//
// Created by Iska on 15/09/14.
// Copyright (c) 2014 BrainCookie. All rights reserved.
//
#import "HTMLKit.h"
@implementation HTMLKit
@end
@@ -6,7 +6,7 @@
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import "HTMLKitExceptions.h"
#import "HTMLKitDOMExceptions.h"
NSString * const HTMLKitHierarchyRequestError = @"HierarchyRequestError";
NSString * const HTMLKitNotFoundError = @"NotFoundError";
+23
View File
@@ -0,0 +1,23 @@
//
// HTMLKitErrorDomain.h
// HTMLKit
//
// Created by Iska on 24/11/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#ifndef HTMLKitErrorDomain_h
#define HTMLKitErrorDomain_h
static NSString *const HTMLKitErrorDomain = @"HTMLKit";
static NSString *const HTMLKitSelectorErrorDomain = @"HTMLKitSelector";
static NSString *const CSSSelectorStringKey = @"CSSSelectorString";
static NSString *const CSSSelectorErrorLocationKey = @"CSSSelectorErrorLocation";
NS_ENUM(NSInteger)
{
HTMLKitSelectorParseError = 4200
};
#endif /* HTMLKitErrorDomain_h */
+1 -1
View File
@@ -52,7 +52,7 @@
}
if (node.htmlNamespace == element.htmlNamespace &&
[node.tagName isEqualToString:element.tagName] &&
[node.attributes isEqualTo:element.attributes]) {
[node.attributes isEqual:element.attributes]) {
existing++;
}
if (existing == 3) {
+53 -11
View File
@@ -7,6 +7,7 @@
//
#import <Foundation/Foundation.h>
#import "HTMLNodeIterator.h"
typedef NS_ENUM(short, HTMLNodeType)
{
@@ -24,37 +25,59 @@ typedef NS_ENUM(short, HTMLNodeType)
HTMLNodeNotation = 12 // historical
};
typedef NS_ENUM(unsigned short, HTMLDocumentPosition)
{
HTMLDocumentPositionEquivalent = 0x0,
HTMLDocumentPositionDisconnected = 0x01,
HTMLDocumentPositionPreceding = 0x02,
HTMLDocumentPositionFollowing = 0x04,
HTMLDocumentPositionContains = 0x08,
HTMLDocumentPositionContainedBy = 0x10,
HTMLDocumentPositionImplementationSpecific = 0x20
};
@class HTMLDocument;
@class HTMLElement;
@class CSSSelector;
@interface HTMLNode : NSObject <NSCopying>
@property (nonatomic, assign, readonly) HTMLNodeType type;
@property (nonatomic, assign, readonly) HTMLNodeType nodeType;
@property (nonatomic, strong, readonly) NSString *name;
@property (nonatomic, weak, readonly) HTMLDocument *ownerDocument;
@property (nonatomic, strong, readonly) NSString *baseURI;
@property (nonatomic, weak, readonly) HTMLNode *parentNode;
@property (nonatomic, weak, readonly) HTMLElement *parentElement;
@property (nonatomic, strong, readonly) NSOrderedSet *childNodes;
@property (nonatomic, strong, readonly) HTMLNode *firstChiledNode;
@property (nonatomic, strong, readonly) HTMLNode *firstChild;
@property (nonatomic, strong, readonly) HTMLNode *lastChildNode;
@property (nonatomic, strong, readonly) HTMLNode *lastChild;
@property (nonatomic, strong, readonly) HTMLNode *previousSibling;
@property (nonatomic, strong, readonly) HTMLNode *nextSibling;
@property (nonatomic, strong, readonly) HTMLElement *previousSiblingElement;
@property (nonatomic, strong, readonly) HTMLElement *nextSiblingElement;
@property (nonatomic, copy) NSString *textContent;
@property (nonatomic, strong, readonly) NSString *outerHTML;
@property (nonatomic, copy) NSString *innerHTML;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithName:(NSString *)name type:(HTMLNodeType)type;
- (HTMLElement *)asElement;
- (BOOL)hasChildNodes;
- (BOOL)hasChildNodeOfType:(HTMLNodeType)type;
@@ -65,16 +88,28 @@ typedef NS_ENUM(short, HTMLNodeType)
- (NSUInteger)indexOfChildNode:(HTMLNode *)node;
- (HTMLNode *)insertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child;
- (NSUInteger)childElementsCount;
- (HTMLElement *)childElementAtIndex:(NSUInteger)index;
- (NSUInteger)indexOfChildElement:(HTMLElement *)element;
- (HTMLNode *)prependNode:(HTMLNode *)node;
- (void)prependNodes:(NSArray *)nodes;
- (HTMLNode *)appendNode:(HTMLNode *)node;
- (void)appendNodes:(NSArray *)nodes;
- (HTMLNode *)insertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child;
- (HTMLNode *)replaceChildNode:(HTMLNode *)node withNode:(HTMLNode *)replacement;
- (void)replaceAllChildNodesWithNode:(HTMLNode *)node;
- (void)removeFromParentNode;
- (HTMLNode *)removeChildNode:(HTMLNode *)node;
- (HTMLNode *)removeChildNodeAtIndex:(NSUInteger)index;
@@ -83,15 +118,22 @@ typedef NS_ENUM(short, HTMLNodeType)
- (void)removeAllChildNodes;
- (HTMLDocumentPosition)compareDocumentPositionWithNode:(HTMLNode *)node;
- (BOOL)isDescendantOfNode:(HTMLNode *)node;
- (BOOL)containsNode:(HTMLNode *)node;
- (void)enumerateChildNodesUsingBlock:(void (^)(HTMLNode *node, NSUInteger idx, BOOL *stop))block;
- (NSEnumerator *)treeEnumerator;
- (void)enumerateChildElementsUsingBlock:(void (^)(HTMLElement *element, NSUInteger idx, BOOL *stop))block;
- (NSEnumerator *)reverseTreeEnumerator;
- (HTMLNodeIterator *)nodeIterator;
- (HTMLNodeIterator *)nodeIteratorWithShowOptions:(HTMLNodeFilterShowOptions)showOptions
filter:(id<HTMLNodeFilter>)filter;
- (HTMLNodeIterator *)nodeIteratorWithShowOptions:(HTMLNodeFilterShowOptions)showOptions
filterBlock:(HTMLNodeFilterValue (^)(HTMLNode *node))filter;
- (NSString *)outerHTML;
- (NSString *)innerHTML;
- (HTMLElement *)firstElementMatchingSelector:(CSSSelector *)selector;
- (NSArray<HTMLElement *> *)elementsMatchingSelector:(CSSSelector *)selector;
- (NSString *)treeDescription;
+316 -100
View File
@@ -12,8 +12,14 @@
#import "HTMLElement.h"
#import "HTMLText.h"
#import "HTMLComment.h"
#import "HTMLKitExceptions.h"
#import "HTMLNodeTreeEnumerator.h"
#import "HTMLKitDOMExceptions.h"
#import "CSSSelector.h"
@interface HTMLDocument (Private)
- (void)runRemovingStepsForNode:(HTMLNode *)oldNode
withOldParent:(HTMLNode *)oldParent
andOldPreviousSibling:(HTMLNode *)oldPreviousSibling;
@end
@interface HTMLNode ()
{
@@ -32,7 +38,7 @@
self = [super init];
if (self) {
_name = name;
_type = type;
_nodeType = type;
_childNodes = [NSMutableOrderedSet new];
}
return self;
@@ -42,7 +48,7 @@
- (HTMLDocument *)ownerDocument
{
if (_type == HTMLNodeDocument) {
if (_nodeType == HTMLNodeDocument) {
return (HTMLDocument *)self;
} else {
return _ownerDocument;
@@ -55,12 +61,6 @@
[self.childNodes.array makeObjectsPerformSelector:@selector(setOwnerDocument:) withObject:ownerDocument];
}
- (void)setBaseURI:(NSString *)baseURI
{
_baseURI = [baseURI copy];
[self.childNodes.array makeObjectsPerformSelector:@selector(setBaseURI:) withObject:baseURI];
}
- (void)setParentNode:(HTMLNode *)parentNode
{
_parentNode = parentNode;
@@ -68,15 +68,15 @@
- (HTMLElement *)parentElement
{
return _parentNode.type == HTMLNodeElement ? (HTMLElement *)_parentNode : nil;
return _parentNode.nodeType == HTMLNodeElement ? (HTMLElement *)_parentNode : nil;
}
- (HTMLNode *)firstChiledNode
- (HTMLNode *)firstChild
{
return self.childNodes.firstObject;
}
- (HTMLNode *)lastChildNode
- (HTMLNode *)lastChild
{
return self.childNodes.lastObject;
}
@@ -99,11 +99,36 @@
return [_parentNode childNodeAtIndex:index + 1];
}
- (HTMLElement *)previousSiblingElement
{
HTMLNode *node = self.previousSibling;
while (node && node.nodeType != HTMLNodeElement) {
node = node.previousSibling;
}
return node.asElement;
}
- (HTMLElement *)nextSiblingElement
{
HTMLNode *node = self.previousSibling;
while (node && node.nodeType != HTMLNodeElement) {
node = node.nextSibling;
}
return node.asElement;
}
- (NSString *)textContent
{
return nil;
}
#pragma mark - Cast
- (HTMLElement *)asElement
{
return (HTMLElement *)self;
}
#pragma mark - Child Nodes
- (BOOL)hasChildNodes
@@ -114,7 +139,7 @@
- (BOOL)hasChildNodeOfType:(HTMLNodeType)type
{
NSUInteger index = [self.childNodes indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
if ([(HTMLNode *)obj type] == type) {
if ([(HTMLNode *)obj nodeType] == type) {
*stop = YES;
return YES;
}
@@ -134,40 +159,105 @@
return [self.childNodes objectAtIndex:index];
}
- (NSUInteger)childElementsCount
{
return [self.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];
}
- (HTMLNode *)insertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child
- (HTMLElement *)childElementAtIndex:(NSUInteger)index
{
node = [self preInsertNode:node beforeChildNode:child];
node.parentNode = self;
return node;
NSUInteger counter = 0;
for (HTMLNode *node in self.childNodes) {
if (node.nodeType == HTMLNodeElement) {
if (counter == index) {
return node.asElement;
}
counter++;
}
}
return nil;
}
- (NSUInteger)indexOfChildElement:(HTMLElement *)element
{
NSUInteger counter = 0;
for (HTMLNode *node in self.childNodes) {
if (node.nodeType == HTMLNodeElement) {
if (node == element) {
return counter;
}
counter++;
}
}
return NSNotFound;
}
- (HTMLNode *)prependNode:(HTMLNode *)node
{
return [self insertNode:node beforeChildNode:self.firstChild];
}
- (void)prependNodes:(NSArray *)nodes
{
for (id node in nodes.reverseObjectEnumerator) {
[self insertNode:node beforeChildNode:self.firstChild];
}
}
- (HTMLNode *)appendNode:(HTMLNode *)node
{
node = [self preInsertNode:node beforeChildNode:nil];
node.parentNode = self;
return node;
return [self insertNode:node beforeChildNode:nil];
}
- (void)appendNodes:(NSArray *)nodes
{
for (id node in nodes) {
[self appendNode:node];
[self insertNode:node beforeChildNode:nil];
}
}
- (HTMLNode *)insertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child
{
#ifndef HTMLKIT_NO_DOM_CHECKS
[self ensurePreInsertionValidityOfNode:node beforeChildNode:child];
#endif
[self.ownerDocument adoptNode:node];
NSArray *nodes = node.nodeType == HTMLNodeDocumentFragment ? [NSArray arrayWithArray:node.childNodes.array] : @[node];
NSUInteger index = [self indexOfChildNode:child];
if (index != NSNotFound) {
NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, nodes.count)];
[(NSMutableOrderedSet *)self.childNodes insertObjects:nodes atIndexes:indexes];
} else {
[(NSMutableOrderedSet *)self.childNodes addObjectsFromArray:nodes];
}
if (node.nodeType == HTMLNodeDocumentFragment) {
[node removeAllChildNodes];
}
[nodes makeObjectsPerformSelector:@selector(setParentNode:) withObject:self];
return node;
}
- (HTMLNode *)replaceChildNode:(HTMLNode *)child withNode:(HTMLNode *)node
{
#ifndef HTMLKIT_NO_DOM_CHECKS
[self ensureReplacementValidityOfChildNode:child withNode:node];
#endif
[self.ownerDocument adoptNode:node];
NSUInteger index = [self indexOfChildNode:child];
node.parentNode = self;
[(NSMutableOrderedSet *)self.childNodes replaceObjectAtIndex:index withObject:node];
[self insertNode:node beforeChildNode:child];
[child removeFromParentNode];
return child;
}
@@ -181,6 +271,11 @@
}
}
- (void)removeFromParentNode
{
[self.parentNode removeChildNode:self];
}
- (HTMLNode *)removeChildNode:(HTMLNode *)child
{
if (child.parentNode != self) {
@@ -189,8 +284,16 @@
NSStringFromSelector(_cmd), child];
}
HTMLNode *oldNode = child;
HTMLNode *oldParent = child.parentNode;
HTMLNode *oldPreviousSibling = child.previousSibling;
[(NSMutableOrderedSet *)self.childNodes removeObject:child];
child.parentNode = nil;
[self.ownerDocument runRemovingStepsForNode:oldNode
withOldParent:oldParent
andOldPreviousSibling:oldPreviousSibling];
return child;
}
@@ -205,14 +308,95 @@
for (HTMLNode *child in self.childNodes.array) {
[node appendNode:child];
}
[self removeAllChildNodes];
[(NSMutableOrderedSet *)self.childNodes removeAllObjects];
}
- (void)removeAllChildNodes
{
[self.childNodes.array makeObjectsPerformSelector:@selector(setParentNode:) withObject:nil];
[(NSMutableOrderedSet *)self.childNodes removeAllObjects];
}
- (HTMLDocumentPosition)compareDocumentPositionWithNode:(HTMLNode *)otherNode
{
if (otherNode == nil) {
return HTMLDocumentPositionDisconnected;
}
if (self == otherNode) {
return HTMLDocumentPositionEquivalent;
}
NSArray * (^ ancestorNodes) (HTMLNode *) = ^ NSArray * (HTMLNode *node) {
NSMutableArray *ancestors = [NSMutableArray array];
for (HTMLNode *node = self; node; node = node.parentNode) {
[ancestors addObject:node];
}
return ancestors;
};
NSArray *ancestors1 = ancestorNodes(self);
NSArray *ancestors2 = ancestorNodes(otherNode);
if (ancestors1.lastObject != ancestors2.lastObject) {
return HTMLDocumentPositionDisconnected |
HTMLDocumentPositionImplementationSpecific |
HTMLDocumentPositionFollowing;
}
for (NSUInteger i = MIN(ancestors1.count - 1, ancestors2.count - 1); i; --i) {
HTMLNode *child1 = ancestors1[i];
HTMLNode *child2 = ancestors2[i];
if (child1 != child2) {
for (HTMLNode *sibling = child1.nextSibling; sibling; sibling = sibling.nextSibling) {
if (sibling == child2) {
return HTMLDocumentPositionFollowing;
}
}
return HTMLDocumentPositionPreceding;
}
}
if (ancestors1.count < ancestors2.count) {
return HTMLDocumentPositionContainedBy | HTMLDocumentPositionFollowing;
} else {
return HTMLDocumentPositionContains | HTMLDocumentPositionPreceding;
}
}
- (BOOL)isDescendantOfNode:(HTMLNode *)otherNode
{
if (otherNode == nil) {
return NO;
}
if (self.ownerDocument != otherNode.ownerDocument) {
return NO;
}
if (!otherNode.hasChildNodes) {
return NO;
}
if (otherNode.nodeType == HTMLNodeDocument) {
return self.nodeType != HTMLNodeDocument && self.ownerDocument == otherNode;
}
for (HTMLNode *parentNode = self.parentNode; parentNode; parentNode = parentNode.parentNode) {
if (parentNode == otherNode) {
return YES;
}
}
return NO;
}
- (BOOL)containsNode:(HTMLNode *)otherNode
{
return self == otherNode || [otherNode isDescendantOfNode:self];
}
#pragma mark - Enumeration
- (void)enumerateChildNodesUsingBlock:(void (^)(HTMLNode *node, NSUInteger idx, BOOL *stop))block
@@ -226,40 +410,71 @@
}];
}
- (NSEnumerator *)treeEnumerator
- (void)enumerateChildElementsUsingBlock:(void (^)(HTMLElement *element, NSUInteger idx, BOOL *stop))block
{
return [[HTMLNodeTreeEnumerator alloc] initWithNode:self reverse:NO];
}
- (NSEnumerator *)reverseTreeEnumerator
{
return [[HTMLNodeTreeEnumerator alloc] initWithNode:self reverse:YES];
}
#pragma mark - Mutation Algorithms
- (HTMLNode *)preInsertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child
{
[self ensurePreInsertionValidityOfNode:node beforeChildNode:child];
[self.ownerDocument adoptNode:node];
NSUInteger index = [self indexOfChildNode:child];
if (index != NSNotFound) {
[(NSMutableOrderedSet *)self.childNodes insertObject:node atIndex:index];
} else {
[(NSMutableOrderedSet *)self.childNodes addObject:node];
if (block == nil) {
return;
}
return node;
[self.childNodes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[HTMLElement class]]) {
block([obj asElement], idx, stop);
}
}];
}
NS_INLINE void CheckParentValid(HTMLNode *node, NSString *cmd)
- (HTMLNodeIterator *)nodeIterator
{
if (node.type != HTMLNodeDocument &&
node.type != HTMLNodeDocumentFragment &&
node.type != HTMLNodeElement) {
return [self nodeIteratorWithShowOptions:HTMLNodeFilterShowAll filter:nil];
}
- (HTMLNodeIterator *)nodeIteratorWithShowOptions:(HTMLNodeFilterShowOptions)showOptions
filter:(id<HTMLNodeFilter>)filter
{
return [[HTMLNodeIterator alloc] initWithNode:self showOptions:showOptions filter:filter];
}
- (HTMLNodeIterator *)nodeIteratorWithShowOptions:(HTMLNodeFilterShowOptions)showOptions
filterBlock:(HTMLNodeFilterValue (^)(HTMLNode *node))filter
{
return [HTMLNodeIterator iteratorWithNode:self showOptions:showOptions filter:filter];
}
#pragma mark - Selectors
- (HTMLElement *)firstElementMatchingSelector:(CSSSelector *)selector
{
for (HTMLElement *element in [self nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]) {
if ([selector acceptElement:element]) {
return element;
}
}
return nil;
}
- (NSArray<HTMLElement *> *)elementsMatchingSelector:(CSSSelector *)selector
{
NSMutableArray *result = [NSMutableArray array];
for (HTMLElement *element in [self nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]) {
if ([selector acceptElement:element]) {
[result addObject:element];
}
}
return result;
}
#ifndef HTMLKIT_NO_DOM_CHECKS
#pragma mark - Validity Checks
NS_INLINE void CheckParentValid(HTMLNode *parent, NSString *cmd)
{
if (parent.nodeType != HTMLNodeDocument &&
parent.nodeType != HTMLNodeDocumentFragment &&
parent.nodeType != HTMLNodeElement) {
[NSException raise:HTMLKitHierarchyRequestError
format:@"%@: Hierarchy Request Error, inserting into %@ is not allowed. The operation would yield an incorrect node tree.",
cmd, node.name];
cmd, parent.name];
}
}
@@ -275,11 +490,11 @@ NS_INLINE void CheckChildsParent(HTMLNode *parent, HTMLNode *child, NSString *cm
NS_INLINE void CheckInsertedNodeValid(HTMLNode *node, NSString *cmd)
{
if (node.type != HTMLNodeDocumentFragment &&
node.type != HTMLNodeDocumentType &&
node.type != HTMLNodeElement &&
node.type != HTMLNodeText &&
node.type != HTMLNodeComment) {
if (node.nodeType != HTMLNodeDocumentFragment &&
node.nodeType != HTMLNodeDocumentType &&
node.nodeType != HTMLNodeElement &&
node.nodeType != HTMLNodeText &&
node.nodeType != HTMLNodeComment) {
[NSException raise:HTMLKitHierarchyRequestError
format:@"%@: Hierarchy Request Error, inserting a %@ is not allowed. The operation would yield an incorrect node tree.",
cmd, node.name];
@@ -288,13 +503,13 @@ NS_INLINE void CheckInsertedNodeValid(HTMLNode *node, NSString *cmd)
NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSString *cmd)
{
if (node.type == HTMLNodeText && parent.type == HTMLNodeDocument) {
if (node.nodeType == HTMLNodeText && parent.nodeType == HTMLNodeDocument) {
[NSException raise:HTMLKitHierarchyRequestError
format:@"%@: Hierarchy Request Error, inserting a text node %@ into docuement is not allowed. The operation would yield an incorrect node tree.",
cmd, parent.name];
}
if (node.type == HTMLNodeDocumentType && parent.type != HTMLNodeDocument) {
if (node.nodeType == HTMLNodeDocumentType && parent.nodeType != HTMLNodeDocument) {
[NSException raise:HTMLKitHierarchyRequestError
format:@"%@: Hierarchy Request Error, inserting a doctype %@ into a non-document node is not allowed. The operation would yield an incorrect node tree.",
cmd, parent.name];
@@ -313,28 +528,28 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin
void (^ hierarchyError)() = ^{
[NSException raise:HTMLKitHierarchyRequestError
format:@"%@: Hierarchy Request Error. The operation would yield an incorrect node tree.",
NSStringFromSelector(_cmd)];
format:@"%@: Hierarchy Request Error, inserting (%@) into (%@). The operation would yield an incorrect node tree.",
NSStringFromSelector(_cmd), self, node];
};
if (self.type == HTMLNodeDocument) {
switch (node.type) {
if (self.nodeType == HTMLNodeDocument) {
switch (node.nodeType) {
case HTMLNodeDocumentFragment:
if (self.childNodesCount > 1 ||
[self hasChildNodeOfType:HTMLNodeText]) {
if (node.childNodesCount > 1 ||
[node hasChildNodeOfType:HTMLNodeText]) {
hierarchyError();
} else if (self.childNodesCount == 1) {
if (self.hasChildNodes ||
child.type == HTMLNodeDocumentType ||
child.nextSibling.type == HTMLNodeDocumentType) {
} else if (node.childNodesCount == 1) {
if ([self hasChildNodeOfType:HTMLNodeElement] ||
child.nodeType == HTMLNodeDocumentType ||
child.nextSibling.nodeType == HTMLNodeDocumentType) {
hierarchyError();
}
}
break;
case HTMLNodeElement:
if ([self hasChildNodeOfType:HTMLNodeElement] ||
child.type == HTMLNodeDocumentType ||
(child != nil && child.nextSibling.type == HTMLNodeDocumentType)) {
child.nodeType == HTMLNodeDocumentType ||
(child != nil && child.nextSibling.nodeType == HTMLNodeDocumentType)) {
hierarchyError();
}
break;
@@ -367,43 +582,42 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin
NSStringFromSelector(_cmd)];
};
if (self.type == HTMLNodeDocument) {
switch (node.type) {
void (^ checkParentHasAnotherChildOfType)(HTMLNodeType) = ^ void (HTMLNodeType type) {
[self enumerateChildNodesUsingBlock:^(HTMLNode *node, NSUInteger idx, BOOL *stop) {
if (node.nodeType == type && node != child) {
*stop = YES;
hierarchyError();
}
}];
};
if (self.nodeType == HTMLNodeDocument) {
switch (node.nodeType) {
case HTMLNodeDocumentFragment:
if (self.childNodesCount > 1 ||
[self hasChildNodeOfType:HTMLNodeText]) {
if (node.childNodesCount > 1 ||
[node hasChildNodeOfType:HTMLNodeText]) {
hierarchyError();
} else if (self.childNodesCount == 1) {
if (self.firstChiledNode != node ||
child.nextSibling.type == HTMLNodeDocumentType) {
} else if (node.childNodesCount == 1) {
if (child.nextSibling.nodeType == HTMLNodeDocumentType) {
hierarchyError();
}
checkParentHasAnotherChildOfType(HTMLNodeElement);
}
break;
case HTMLNodeElement:
{
if (child.nextSibling.type == HTMLNodeDocumentType) {
if (child.nextSibling.nodeType == HTMLNodeDocumentType) {
hierarchyError();
}
[self enumerateChildNodesUsingBlock:^(HTMLNode *node, NSUInteger idx, BOOL *stop) {
if (node.type == HTMLNodeElement && node != child) {
*stop = YES;
hierarchyError();
}
}];
checkParentHasAnotherChildOfType(HTMLNodeElement);
break;
}
case HTMLNodeDocumentType:
{
if (child.previousSibling.type == HTMLNodeElement) {
if (child.previousSibling.nodeType == HTMLNodeElement) {
hierarchyError();
}
[self enumerateChildNodesUsingBlock:^(HTMLNode *node, NSUInteger idx, BOOL *stop) {
if (node.type == HTMLNodeDocument && node != child) {
*stop = YES;
hierarchyError();
}
}];
checkParentHasAnotherChildOfType(HTMLNodeDocumentType);
break;
}
default:
@@ -412,11 +626,13 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin
}
}
#endif
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
{
HTMLNode *copy = [[self.class alloc] initWithName:self.name type:self.type];
HTMLNode *copy = [[self.class alloc] initWithName:self.name type:self.nodeType];
return copy;
}
@@ -433,6 +649,11 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin
return [[self.childNodes.array valueForKey:@"outerHTML"] componentsJoinedByString:@""];
}
- (void)setInnerHTML:(NSString *)outerHTML
{
[self doesNotRecognizeSelector:_cmd];
}
#pragma mark - Description
- (NSString *)treeDescription
@@ -466,12 +687,7 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p %@>", self.class, self, self.name];
}
- (NSString *)debugDescription
{
return self.treeDescription;
return [NSString stringWithFormat:@"<%@: %p '%@'>", self.class, self, self.name];
}
- (id)debugQuickLookObject
+56
View File
@@ -0,0 +1,56 @@
//
// HTMLNodeFilter.h
// HTMLKit
//
// Created by Iska on 27/05/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef NS_ENUM(unsigned short, HTMLNodeFilterValue)
{
HTMLNodeFilterAccept = 1,
HTMLNodeFilterReject = 2,
HTMLNodeFilterSkip = 3
};
typedef NS_OPTIONS(unsigned long, HTMLNodeFilterShowOptions)
{
HTMLNodeFilterShowAll = 0xFFFFFFFF,
HTMLNodeFilterShowElement = 0x1,
HTMLNodeFilterShowText = 0x4,
HTMLNodeFilterShowComment = 0x80,
HTMLNodeFilterShowDocument = 0x100,
HTMLNodeFilterShowDocumentType = 0x200,
HTMLNodeFilterShowDocumentFragment = 0x400
};
#pragma mark - Node Filter
@class HTMLNode;
@protocol HTMLNodeFilter <NSObject>
- (HTMLNodeFilterValue)acceptNode:(HTMLNode *)node;
@end
#pragma mark - Block Filter
@interface HTMLNodeFilterBlock : NSObject <HTMLNodeFilter>
+ (instancetype)filterWithBlock:(HTMLNodeFilterValue (^)(HTMLNode *node))block;
@end
#pragma mark - CSS Selector Filter
@class CSSSelector;
@interface HTMLSelectorNodeFilter : NSObject <HTMLNodeFilter>
+ (instancetype)filterWithSelector:(CSSSelector *)selector;
@end
+85
View File
@@ -0,0 +1,85 @@
//
// HTMLNodeFilter.m
// HTMLKit
//
// Created by Iska on 05/06/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import "HTMLNodeFilter.h"
#import "HTMLNode.h"
#import "CSSSelector.h"
#pragma mark - Block Filter
@interface HTMLNodeFilterBlock ()
{
BOOL (^ _block)(HTMLNode *);
}
@end
@implementation HTMLNodeFilterBlock
+ (instancetype)filterWithBlock:(HTMLNodeFilterValue (^)(HTMLNode *))block
{
return [[self alloc] initWithBlock:block];
}
- (instancetype)initWithBlock:(HTMLNodeFilterValue (^)(HTMLNode *))block
{
self = [super init];
if (self) {
_block = [block copy];
}
return self;
}
- (HTMLNodeFilterValue)acceptNode:(HTMLNode *)node
{
if (!_block) {
return HTMLNodeFilterSkip;
}
return _block(node);
}
@end
#pragma mark - CSS Selector Filter
@interface HTMLSelectorNodeFilter ()
{
CSSSelector *_selector;
}
@end
@implementation HTMLSelectorNodeFilter
+ (instancetype)filterWithSelector:(CSSSelector *)selector
{
return [[self alloc] initWithSelector:selector];
}
- (instancetype)initWithSelector:(CSSSelector *)selector
{
self = [super init];
if (self) {
_selector = selector;
}
return self;
}
- (HTMLNodeFilterValue)acceptNode:(HTMLNode *)node
{
if (node.nodeType != HTMLNodeElement) {
return HTMLNodeFilterSkip;
}
if ([_selector acceptElement:node.asElement]) {
return HTMLNodeFilterAccept;
}
return HTMLNodeFilterSkip;
}
@end
+36
View File
@@ -0,0 +1,36 @@
//
// HTMLNodeIterator.h
// HTMLKit
//
// Created by Iska on 27/05/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "HTMLNodeFilter.h"
@class HTMLNode;
@interface HTMLNodeIterator : NSEnumerator
@property (nonatomic, strong, readonly) HTMLNode *root;
@property (nonatomic, strong, readonly) HTMLNode *referenceNode;
@property (nonatomic, assign, readonly) BOOL pointerBeforeReferenceNode;
@property (nonatomic, assign, readonly) HTMLNodeFilterShowOptions whatToShow;
@property (nonatomic, strong, readonly) id<HTMLNodeFilter> filter;
+ (instancetype)iteratorWithNode:(HTMLNode *)node
showOptions:(HTMLNodeFilterShowOptions)showOptions
filter:(HTMLNodeFilterValue (^)(HTMLNode *node))filter;
- (instancetype)initWithNode:(HTMLNode *)node;
- (instancetype)initWithNode:(HTMLNode *)node
filter:(id<HTMLNodeFilter>)filter;
- (instancetype)initWithNode:(HTMLNode *)node
showOptions:(HTMLNodeFilterShowOptions)showOptions
filter:(id<HTMLNodeFilter>)filter;
- (HTMLNode *)nextNode;
- (HTMLNode *)previousNode;
@end
+166
View File
@@ -0,0 +1,166 @@
//
// HTMLNodeIterator.m
// HTMLKit
//
// Created by Iska on 27/05/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import "HTMLNodeIterator.h"
#import "HTMLDocument.h"
#import "HTMLNode.h"
#import "HTMLNodeFilter.h"
#import "HTMLNodeTraversal.h"
typedef NS_ENUM(short, TraverseDirection)
{
TraverseDirectionNext,
TraverseDirectionPrevious
};
@interface HTMLDocument (Private)
- (void)attachNodeIterator:(HTMLNodeIterator *)iterator;
- (void)detachNodeIterator:(HTMLNodeIterator *)iterator;
@end
@interface HTMLNodeIterator ()
{
HTMLNode *_root;
}
@end
@implementation HTMLNodeIterator
#pragma mark - Lifecycle
+ (instancetype)iteratorWithNode:(HTMLNode *)node
showOptions:(HTMLNodeFilterShowOptions)showOptions
filter:(HTMLNodeFilterValue (^)(HTMLNode *))filter
{
return [[self alloc] initWithNode:node
showOptions:showOptions
filter:[HTMLNodeFilterBlock filterWithBlock:filter]];
}
- (instancetype)initWithNode:(HTMLNode *)node
{
return [self initWithNode:node filter:nil];
}
- (instancetype)initWithNode:(HTMLNode *)node
filter:(id<HTMLNodeFilter>)filter
{
return [self initWithNode:node showOptions:HTMLNodeFilterShowAll filter:filter];
}
- (instancetype)initWithNode:(HTMLNode *)node
showOptions:(HTMLNodeFilterShowOptions)showOptions
filter:(id<HTMLNodeFilter>)filter
{
self = [super init];
if (self) {
_root = node;
_filter = filter;
_whatToShow = showOptions;
_referenceNode = _root;
_pointerBeforeReferenceNode = YES;
[_root.ownerDocument attachNodeIterator:self];
}
return self;
}
- (void)dealloc
{
[_root.ownerDocument detachNodeIterator:self];
}
#pragma mark - Removing Steps
- (void)runRemovingStepsForNode:(HTMLNode *)oldNode
withOldParent:(HTMLNode *)oldParent
andOldPreviousSibling:(HTMLNode *)oldPreviousSibling
{
if ([oldNode containsNode:_root]) {
return;
}
if (![oldNode containsNode:_referenceNode]) {
return;
}
if (_pointerBeforeReferenceNode) {
HTMLNode *nextSibling = oldPreviousSibling != nil ? oldPreviousSibling.nextSibling : oldParent.firstChild;
if (nextSibling != nil) {
_referenceNode = nextSibling;
return;
}
HTMLNode *next = FollowingNode(oldParent, _root);
if ([_root containsNode:next]) {
_referenceNode = next;
return;
}
_pointerBeforeReferenceNode = NO;
}
HTMLNode * (^ lastInclusiveDescendant) (HTMLNode *) = ^ HTMLNode * (HTMLNode *node) {
while (node.lastChild) {
node = node.lastChild;
}
return node;
};
_referenceNode = oldPreviousSibling != nil ? lastInclusiveDescendant(oldPreviousSibling) : oldParent;
}
#pragma mark - Traversal
- (HTMLNode *)traverseInDirection:(TraverseDirection)direction
{
HTMLNode *node = self.referenceNode;
BOOL beforeNode = self.pointerBeforeReferenceNode;
do {
if (direction == TraverseDirectionNext) {
if (!beforeNode) {
node = FollowingNode(node, self.root);
if (node == nil) {
return nil;
}
}
beforeNode = NO;
} else {
if (beforeNode) {
node = PrecedingNode(node, self.root);
if (node == nil) {
return nil;
}
}
beforeNode = YES;
}
} while (FilterNode(self.filter, self.whatToShow, node) != HTMLNodeFilterAccept);
_referenceNode = node;
_pointerBeforeReferenceNode = beforeNode;
return node;
}
- (HTMLNode *)nextNode
{
return [self traverseInDirection:TraverseDirectionNext];
}
- (HTMLNode *)previousNode
{
return [self traverseInDirection:TraverseDirectionPrevious];
}
#pragma mark - NSEnumerator
- (id)nextObject
{
return self.nextNode;
}
@end
+17
View File
@@ -0,0 +1,17 @@
//
// HTMLNodeTraversal.h
// HTMLKit
//
// Created by Iska on 05/06/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "HTMLNodeFilter.h"
@class HTMLNode;
extern HTMLNode * PrecedingNode(HTMLNode *node, HTMLNode *root);
extern HTMLNode * FollowingNode(HTMLNode *node, HTMLNode *root);
extern HTMLNode * FollowingNodeSkippingChildren(HTMLNode *node, HTMLNode *root);
extern HTMLNodeFilterValue FilterNode(id<HTMLNodeFilter> filter, HTMLNodeFilterShowOptions whatToShow, HTMLNode *node);
+76
View File
@@ -0,0 +1,76 @@
//
// HTMLNodeTraversal.m
// HTMLKit
//
// Created by Iska on 05/06/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import "HTMLNodeTraversal.h"
#import "HTMLNode.h"
#import "HTMLNodeFilter.h"
HTMLNode * PrecedingNode(HTMLNode *node, HTMLNode *root)
{
HTMLNode *previous = node.previousSibling;
if (previous != nil) {
while (previous.lastChild != nil) {
previous = previous.lastChild;
}
return previous;
}
if (node == root) {
return nil;
}
return node.parentNode;
}
HTMLNode * FollowingNode(HTMLNode *node, HTMLNode *root)
{
if (node.firstChild != nil) {
return node.firstChild;
}
do {
if (node == root) {
return nil;
}
if (node.nextSibling != nil) {
return node.nextSibling;
}
node = node.parentNode;
} while (node != nil);
return nil;
}
HTMLNode * FollowingNodeSkippingChildren(HTMLNode *node, HTMLNode *root)
{
do {
if (node == root) {
return nil;
}
if (node.nextSibling != nil) {
return node.nextSibling;
}
node = node.parentNode;
} while (node != nil);
return nil;
}
HTMLNodeFilterValue FilterNode(id<HTMLNodeFilter> filter, HTMLNodeFilterShowOptions whatToShow, HTMLNode *node)
{
unsigned long nthBit = (1 << (node.nodeType - 1)) & whatToShow;
if (!nthBit) {
return HTMLNodeFilterSkip;
}
if (filter == nil) {
return HTMLNodeFilterAccept;
}
return [filter acceptNode:node];
}
-17
View File
@@ -1,17 +0,0 @@
//
// HTMLNodeTreeEnumerator.h
// HTMLKit
//
// Created by Iska on 28/03/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
@class HTMLNode;
@interface HTMLNodeTreeEnumerator : NSEnumerator
- (instancetype)initWithNode:(HTMLNode *)node reverse:(BOOL)reverse;
@end
-54
View File
@@ -1,54 +0,0 @@
//
// HTMLNodeTreeEnumerator.m
// HTMLKit
//
// Created by Iska on 28/03/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import "HTMLNodeTreeEnumerator.h"
#import "HTMLNode.h"
@interface HTMLNodeTreeEnumerator ()
{
BOOL _reverse;
NSMutableArray *_stack;
}
@end
@implementation HTMLNodeTreeEnumerator
- (instancetype)initWithNode:(HTMLNode *)node reverse:(BOOL)reverse
{
self = [super init];
if (self) {
_reverse = reverse;
_stack = [[NSMutableArray alloc] initWithObjects:node, nil];
}
return self;
}
- (id)nextObject
{
if (_stack.count == 0) {
return nil;
}
HTMLNode *node = _stack.lastObject;
[_stack removeLastObject];
NSArray *childNodes = node.childNodes.array;
if (childNodes != nil && childNodes.count > 0) {
if (childNodes.count > 1) {
NSRange range = NSMakeRange(_reverse ? 0 : 1, childNodes.count - 1);
NSArray *rest = [childNodes subarrayWithRange:range];
[_stack addObjectsFromArray:_reverse ? rest : rest.reverseObjectEnumerator.allObjects];
}
[_stack addObject:_reverse ? childNodes.lastObject : childNodes.firstObject];
}
return node;
}
@end
+12 -8
View File
@@ -8,18 +8,22 @@
#import <Foundation/Foundation.h>
@interface HTMLOrderedDictionary : NSMutableDictionary
NS_ASSUME_NONNULL_BEGIN
- (id)objectAtIndex:(NSUInteger)index;
- (void)setObject:(id)anObject forKey:(id<NSCopying>)aKey atIndex:(NSUInteger)index;
@interface HTMLOrderedDictionary<KeyType, ObjectType> : NSMutableDictionary<KeyType, ObjectType>
- (ObjectType)objectAtIndex:(NSUInteger)index;
- (void)setObject:(ObjectType)anObject forKey:(KeyType<NSCopying>)aKey atIndex:(NSUInteger)index;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (void)replaceKeyValueAtIndex:(NSUInteger)index withObject:(id)anObject andKey:(id<NSCopying>)aKey;
- (void)replaceKey:(id<NSCopying>)aKey withKey:(id<NSCopying>)newKey;
- (NSUInteger)indexOfKey:(id<NSCopying>)aKey;
- (void)replaceKeyValueAtIndex:(NSUInteger)index withObject:(ObjectType)anObject andKey:(KeyType<NSCopying>)aKey;
- (void)replaceKey:(KeyType<NSCopying>)aKey withKey:(KeyType<NSCopying>)newKey;
- (NSUInteger)indexOfKey:(KeyType<NSCopying>)aKey;
- (id)objectAtIndexedSubscript:(NSUInteger)index;
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)index;
- (ObjectType)objectAtIndexedSubscript:(NSUInteger)index;
- (void)setObject:(ObjectType)obj atIndexedSubscript:(NSUInteger)index;
- (NSEnumerator *)reverseKeyEnumerator;
@end
NS_ASSUME_NONNULL_END
+8 -8
View File
@@ -12,7 +12,7 @@
#import "HTMLStackOfOpenElements.h"
#import "HTMLListOfActiveFormattingElements.h"
#import "HTMLParserInsertionModes.h"
#import "HTMLNodes.h"
#import "HTMLDOM.h"
#import "HTMLElementTypes.h"
#import "HTMLElementAdjustment.h"
#import "HTMLMarker.h"
@@ -127,7 +127,7 @@
return nil;
}
if ([_contextElement isEqualTo:contextElement]) {
if ([_contextElement isEqual:contextElement]) {
return [_document childNodeAtIndex:0].childNodes.array;
}
@@ -421,8 +421,8 @@
- (HTMLElement *)createElementForToken:(HTMLTagToken *)token inNamespace:(HTMLNamespace)htmlNamespace
{
HTMLElement *element = [[HTMLElement alloc] initWithTagName:token.tagName
attributes:token.attributes
namespace:htmlNamespace];
namespace:htmlNamespace
attributes:token.attributes];
return element;
}
@@ -452,11 +452,11 @@
HTMLElement *child = nil;
HTMLNode *adjustedInsertionLocation = [self appropriatePlaceForInsertingANodeWithOverrideTarget:nil
beforeChildNode:&child];
if (adjustedInsertionLocation.type != HTMLNodeDocument) {
if (child != nil && child.previousSibling.type == HTMLNodeText) {
if (adjustedInsertionLocation.nodeType != HTMLNodeDocument) {
if (child != nil && child.previousSibling.nodeType == HTMLNodeText) {
[(HTMLText *)child.previousSibling appendString:data];
} else if (adjustedInsertionLocation.lastChildNode.type == HTMLNodeText) {
[(HTMLText *)adjustedInsertionLocation.lastChildNode appendString:data];
} else if (adjustedInsertionLocation.lastChild.nodeType == HTMLNodeText) {
[(HTMLText *)adjustedInsertionLocation.lastChild appendString:data];
} else {
HTMLText *text = [[HTMLText alloc] initWithData:data];
[adjustedInsertionLocation insertNode:text beforeChildNode:child];
+36
View File
@@ -0,0 +1,36 @@
//
// HTMLTreeWalker.h
// HTMLKit
//
// Created by Iska on 05/06/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "HTMLNodeFilter.h"
@class HTMLNode;
@interface HTMLTreeWalker : NSObject
@property (nonatomic, strong, readonly) HTMLNode *root;
@property (nonatomic, assign, readonly) HTMLNodeFilterShowOptions whatToShow;
@property (nonatomic, strong, readonly) id<HTMLNodeFilter> filter;
@property (nonatomic, strong) HTMLNode *currentNode;
- (instancetype)initWithNode:(HTMLNode *)node;
- (instancetype)initWithNode:(HTMLNode *)node
filter:(id<HTMLNodeFilter>)filter;
- (instancetype)initWithNode:(HTMLNode *)node
showOptions:(HTMLNodeFilterShowOptions)showOptions
filter:(id<HTMLNodeFilter>)filter;
- (HTMLNode *)parentNode;
- (HTMLNode *)firstChild;
- (HTMLNode *)lastChild;
- (HTMLNode *)previousSibling;
- (HTMLNode *)nextSibling;
- (HTMLNode *)previousNode;
- (HTMLNode *)nextNode;
@end
+236
View File
@@ -0,0 +1,236 @@
//
// HTMLTreeWalker.m
// HTMLKit
//
// Created by Iska on 05/06/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import "HTMLTreeWalker.h"
#import "HTMLNode.h"
#import "HTMLNodeFilter.h"
#import "HTMLNodeTraversal.h"
typedef NS_ENUM(short, HTMLTreeWalkerChildrenType)
{
HTMLTreeWalkerChildrenTypeFirst,
HTMLTreeWalkerChildrenTypeLast
};
typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType)
{
HTMLTreeWalkerSiblingsTypeNext,
HTMLTreeWalkerSiblingsTypePrevious
};
@implementation HTMLTreeWalker
#pragma mark - Lifecycle
- (instancetype)initWithNode:(HTMLNode *)node
{
return [self initWithNode:node filter:nil];
}
- (instancetype)initWithNode:(HTMLNode *)node
filter:(id<HTMLNodeFilter>)filter
{
return [self initWithNode:node showOptions:HTMLNodeFilterShowAll filter:filter];
}
- (instancetype)initWithNode:(HTMLNode *)node
showOptions:(HTMLNodeFilterShowOptions)showOptions
filter:(id<HTMLNodeFilter>)filter
{
self = [super init];
if (self) {
_root = node;
_filter = filter;
_whatToShow = showOptions;
_currentNode = _root;
}
return self;
}
#pragma mark - Traversal
- (HTMLNode *)parentNode
{
HTMLNode *node = _currentNode;
while (node != nil && node != _root) {
node = node.parentNode;
if (node != nil && FilterNode(self.filter, self.whatToShow, node) == HTMLNodeFilterAccept) {
_currentNode = node;
return node;
}
}
return nil;
}
- (HTMLNode *)traverseChildrenOfType:(HTMLTreeWalkerChildrenType)type
{
HTMLNode *node = _currentNode;
node = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.firstChild : node.lastChild;
while (node != nil) {
HTMLNodeFilterValue result = FilterNode(self.filter, self.whatToShow, node);
if (result == HTMLNodeFilterAccept) {
_currentNode = node;
return node;
}
if (result == HTMLNodeFilterSkip) {
HTMLNode *child = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.firstChild : node.lastChild;
if (child != nil) {
node = child;
continue;
}
}
while (node != nil) {
HTMLNode *sibling = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.nextSibling : node.previousSibling;
if (sibling != nil) {
node = sibling;
break;
}
HTMLNode *parent = node.parentNode;
if (parent == nil || parent == _root || parent == _currentNode) {
return nil;
}
node = parent;
}
}
return nil;
}
- (HTMLNode *)firstChild
{
return [self traverseChildrenOfType:HTMLTreeWalkerChildrenTypeFirst];
}
- (HTMLNode *)lastChild
{
return [self traverseChildrenOfType:HTMLTreeWalkerChildrenTypeLast];
}
- (HTMLNode *)traverseSiblingsOfType:(HTMLTreeWalkerSiblingsType)type
{
HTMLNode *node = _currentNode;
if (node == _root) {
return nil;
}
while (YES) {
HTMLNode *sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.nextSibling : node.previousSibling;
while (sibling != nil) {
node = sibling;
HTMLNodeFilterValue result = FilterNode(self.filter, self.whatToShow, node);
if (result == HTMLNodeFilterAccept) {
_currentNode = node;
return node;
}
sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.firstChild : node.lastChild;
if (result == HTMLNodeFilterReject || sibling == nil) {
sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.nextSibling : node.previousSibling;
}
}
node = node.parentNode;
if (node == nil || node == _root) {
return nil;
}
if (FilterNode(self.filter, self.whatToShow, node) == HTMLNodeFilterAccept) {
return nil;
}
}
return nil;
}
- (HTMLNode *)previousSibling
{
return [self traverseSiblingsOfType:HTMLTreeWalkerSiblingsTypePrevious];
}
- (HTMLNode *)nextSibling
{
return [self traverseSiblingsOfType:HTMLTreeWalkerSiblingsTypeNext];
}
- (HTMLNode *)previousNode
{
HTMLNode *node = _currentNode;
while (node != _root) {
HTMLNode *sibling = node.previousSibling;
while (sibling != nil) {
node = sibling;
HTMLNodeFilterValue result = FilterNode(self.filter, self.whatToShow, node);
while (result != HTMLNodeFilterReject && node.hasChildNodes) {
node = node.lastChild;
result = FilterNode(self.filter, self.whatToShow, node);
}
if (result == HTMLNodeFilterAccept) {
_currentNode = node;
return node;
}
sibling = node.previousSibling;
}
if (node == _root || node.parentNode == nil) {
return nil;
}
node = node.parentNode;
if (FilterNode(self.filter, self.whatToShow, node) == HTMLNodeFilterAccept) {
_currentNode = node;
return node;
}
}
return nil;
}
- (HTMLNode *)nextNode
{
HTMLNode *node = _currentNode;
HTMLNodeFilterValue result = YES;
while (YES) {
while (result != HTMLNodeFilterReject && node.hasChildNodes) {
node = node.firstChild;
result = FilterNode(self.filter, self.whatToShow, node);
if (result == HTMLNodeFilterAccept) {
_currentNode = node;
return node;
}
}
HTMLNode *nextSibling;
while ((nextSibling = FollowingNodeSkippingChildren(node, _root)) != nil) {
node = nextSibling;
result = FilterNode(self.filter, self.whatToShow, node);
if (result == HTMLNodeFilterAccept) {
_currentNode = node;
return node;
}
break;
}
}
return nil;
}
@end
+17
View File
@@ -0,0 +1,17 @@
//
// NSCharacterSet+HTMLKit.h
// HTMLKit
//
// Created by Iska on 14/05/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSCharacterSet (HTMLKit)
+ (instancetype)HTMLWhitespaceCharacterSet;
+ (instancetype)HTMLHexNumberCharacterSet;
+ (instancetype)CSSNthExpressionCharacterSet;
@end
+43
View File
@@ -0,0 +1,43 @@
//
// NSCharacterSet+HTMLKit.m
// HTMLKit
//
// Created by Iska on 14/05/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import "NSCharacterSet+HTMLKit.h"
@implementation NSCharacterSet (HTMLKit)
+ (instancetype)HTMLWhitespaceCharacterSet
{
static NSCharacterSet *set = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
set = [NSCharacterSet characterSetWithCharactersInString:@" \t\n\r\f"];
});
return set;
}
+ (instancetype)HTMLHexNumberCharacterSet
{
static NSCharacterSet *set = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
set = [NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEFabcdef"];
});
return set;
}
+ (instancetype)CSSNthExpressionCharacterSet
{
static NSCharacterSet *set = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
set = [NSCharacterSet characterSetWithCharactersInString:@" 0123456789nN-+"];
});
return set;
}
@end
+238
View File
@@ -0,0 +1,238 @@
//
// CSSAttributeSelectorTests.m
// HTMLKit
//
// Created by Iska on 14/05/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "CSSSelectors.h"
#import "HTMLDOM.h"
@interface CSSAttributeSelectorTests : XCTestCase
{
HTMLElement *_element;
}
@end
@implementation CSSAttributeSelectorTests
- (void)setUp
{
[super setUp];
_element = [[HTMLElement alloc] initWithTagName:@"div"];
}
- (void)testAttributeExists
{
CSSSelector *selector = hasAttributeSelector(@"attr");
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"class"] = @"class";
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"attr"] = @"value";
XCTAssertEqual([selector acceptElement:_element], YES);
_element[@"attr"] = @"value 2";
XCTAssertEqual([selector acceptElement:_element], YES);
_element[@"attr"] = @"value 3";
XCTAssertEqual([selector acceptElement:_element], YES);
[_element.attributes removeObjectForKey:@"attr"];
XCTAssertEqual([selector acceptElement:_element], NO);
}
- (void)testAttributeExactMatch
{
CSSSelector *selector = attributeSelector(CSSAttributeSelectorExactMatch, @"attr", @"value");
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"class"] = @"class";
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"attr"] = @"value 1";
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"attr"] = @"value 2";
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"attr"] = @"value 3";
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"attr"] = @"value";
XCTAssertEqual([selector acceptElement:_element], YES);
[_element.attributes removeObjectForKey:@"attr"];
XCTAssertEqual([selector acceptElement:_element], NO);
}
- (void)testAttributeIncludes
{
CSSSelector *selector = attributeSelector(CSSAttributeSelectorIncludes, @"attr", @"value");
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"class"] = @"class";
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"attr"] = @"value";
XCTAssertEqual([selector acceptElement:_element], YES);
_element[@"attr"] = @"a b value";
XCTAssertEqual([selector acceptElement:_element], YES);
_element[@"attr"] = @"a value b";
XCTAssertEqual([selector acceptElement:_element], YES);
[_element.attributes removeObjectForKey:@"attr"];
XCTAssertEqual([selector acceptElement:_element], NO);
}
- (void)testAttributeBegins
{
CSSSelector *selector = attributeSelector(CSSAttributeSelectorBegins, @"attr", @"value");
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"class"] = @"class";
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"attr"] = @"val";
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"attr"] = @"a value";
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"attr"] = @"value";
XCTAssertEqual([selector acceptElement:_element], YES);
_element[@"attr"] = @"values";
XCTAssertEqual([selector acceptElement:_element], YES);
[_element.attributes removeObjectForKey:@"attr"];
XCTAssertEqual([selector acceptElement:_element], NO);
}
- (void)testAttributeEnds
{
CSSSelector *selector = attributeSelector(CSSAttributeSelectorEnds, @"attr", @"value");
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"class"] = @"class";
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"attr"] = @"val";
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"attr"] = @"value";
XCTAssertEqual([selector acceptElement:_element], YES);
_element[@"attr"] = @"a value";
XCTAssertEqual([selector acceptElement:_element], YES);
_element[@"attr"] = @"some-value";
XCTAssertEqual([selector acceptElement:_element], YES);
[_element.attributes removeObjectForKey:@"attr"];
XCTAssertEqual([selector acceptElement:_element], NO);
}
- (void)testAttributeContains
{
CSSSelector *selector = attributeSelector(CSSAttributeSelectorContains, @"attr", @"value");
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"class"] = @"class";
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"attr"] = @"val";
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"attr"] = @"value";
XCTAssertEqual([selector acceptElement:_element], YES);
_element[@"attr"] = @"a value b";
XCTAssertEqual([selector acceptElement:_element], YES);
_element[@"attr"] = @"some-values";
XCTAssertEqual([selector acceptElement:_element], YES);
[_element.attributes removeObjectForKey:@"attr"];
XCTAssertEqual([selector acceptElement:_element], NO);
}
- (void)testAttributeHyphen
{
CSSSelector *selector = attributeSelector(CSSAttributeSelectorHyphen, @"attr", @"top");
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"class"] = @"class";
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"attr"] = @"top_text";
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"attr"] = @"toptext";
XCTAssertEqual([selector acceptElement:_element], NO);
_element[@"attr"] = @"top";
XCTAssertEqual([selector acceptElement:_element], YES);
_element[@"attr"] = @"top-text";
XCTAssertEqual([selector acceptElement:_element], YES);
[_element.attributes removeObjectForKey:@"attr"];
XCTAssertEqual([selector acceptElement:_element], NO);
}
- (void)testAttributeNot
{
CSSSelector *selector = attributeSelector(CSSAttributeSelectorNot, @"attr", @"value");
XCTAssertEqual([selector acceptElement:_element], YES);
_element[@"class"] = @"class";
XCTAssertEqual([selector acceptElement:_element], YES);
_element[@"attr"] = @"top_text";
XCTAssertEqual([selector acceptElement:_element], YES);
_element[@"attr"] = @"toptext";
XCTAssertEqual([selector acceptElement:_element], YES);
_element[@"attr"] = @"top";
XCTAssertEqual([selector acceptElement:_element], YES);
_element[@"attr"] = @"top-text";
XCTAssertEqual([selector acceptElement:_element], YES);
[_element.attributes removeObjectForKey:@"attr"];
XCTAssertEqual([selector acceptElement:_element], YES);
_element[@"attr"] = @"value";
XCTAssertEqual([selector acceptElement:_element], NO);
}
- (void)testIdSelector
{
_element[@"id"] = @"id";
XCTAssertEqual([idSelector(@"id") acceptElement:_element], YES);
XCTAssertEqual([idSelector(@"other id") acceptElement:_element], NO);
}
- (void)testClassSelector
{
_element[@"class"] = @"class";
XCTAssertEqual([classSelector(@"class") acceptElement:_element], YES);
XCTAssertEqual([classSelector(@"other class") acceptElement:_element], NO);
}
@end
+116
View File
@@ -0,0 +1,116 @@
//
// CSSCombinatorSelectorTests.m
// HTMLKit
//
// Created by Iska on 23/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "CSSSelectors.h"
#import "HTMLParser.h"
#import "HTMLDOM.h"
@interface CSSCombinatorSelectorTests : XCTestCase
{
HTMLDocument *_document;
}
@end
@implementation CSSCombinatorSelectorTests
- (void)setUp
{
[super setUp];
/*
| <body>
| <p id='p1'>
| A paragraph<span id='span1'>A span</span>
| <table id='table'>
| <tbody id='tbody'>
| <tr id='tr1'>
| <td id='td'>
| <span id='span2'>Span in table</span>
| </td>
| </tr>
| <tr id='tr2'></tr>
| <tr id='tr3'></tr>
| </tbody>
| </table>
| </p>
| <div id='inner-div1'>
| <p id='p2'></p>
| </div>
| <div id='inner-div2'>
| <p id='p3'></p>
| </div>
| </body>
*/
_document = [[[HTMLParser alloc] initWithString:@"<body>"
"<p id='p1'>"
"A paragraph<span id='span1'>A span</span>"
"<table id='table'>"
"<tbody id='tbody'>"
"<tr id='tr1'><td id='td'>"
"<span id='span2'>Span in table</span>"
"</td></tr>"
"<tr id='tr2'></tr>"
"<tr id='tr3'></tr>"
"</tbody>"
"</table>"
"</p>"
"<div id='inner-div1'>"
"<p id='p2'></p>"
"</div>"
"<div id='inner-div2'>"
"<p id='p3'></p>"
"</div>"
"</body>"] document];
}
-(void)testChildOfElementCombinator
{
NSArray *elements = [_document elementsMatchingSelector:childOfElementSelector(typeSelector(@"body"))];
NSArray *expected = @[@"p1", @"inner-div1", @"inner-div2"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_document elementsMatchingSelector:childOfElementSelector(typeSelector(@"p"))];
expected = @[@"span1", @"table"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
-(void)testDescendantOfElementCombinator
{
NSArray *elements = [_document elementsMatchingSelector:descendantOfElementSelector(typeSelector(@"p"))];
NSArray *expected = @[@"span1", @"table", @"tbody", @"tr1", @"td", @"span2", @"tr2", @"tr3"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_document elementsMatchingSelector:descendantOfElementSelector(typeSelector(@"table"))];
expected = @[@"tbody", @"tr1", @"td", @"span2", @"tr2", @"tr3"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
-(void)testAdjacentSiblingCombinator
{
NSArray *elements = [_document elementsMatchingSelector:adjacentSiblingSelector(typeSelector(@"tr"))];
NSArray *expected = @[@"tr2", @"tr3"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_document elementsMatchingSelector:adjacentSiblingSelector(typeSelector(@"p"))];
expected = @[@"inner-div1"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
-(void)testGeneralSiblingCombinator
{
NSArray *elements = [_document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"tr"))];
NSArray *expected = @[@"tr2", @"tr3"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"p"))];
expected = @[@"inner-div1", @"inner-div2"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
@end
@@ -0,0 +1,352 @@
//
// CSSNThExpressionSelectorTests.m
// HTMLKit
//
// Created by Iska on 21/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "CSSSelectors.h"
#import "HTMLParser.h"
#import "HTMLDOM.h"
@interface CSSNThExpressionSelectorTests : XCTestCase
{
HTMLDocument *_childTree;
HTMLDocument *_typeTree;
}
@end
@implementation CSSNThExpressionSelectorTests
- (void)setUp
{
[super setUp];
/*
| <div>
| <table>
| <tr id='11'><td>11</td></tr>
| <tr id='12'><td>12</td></tr>
| <tr id='13'><td>13</td></tr>
| <tr id='14'><td>14</td></tr>
| </table>
| </div>
| <div>
| <table>
| <tr id='21'><td>21</td></tr>
| </table>
| </div>
| <div>
| <table>
| <tr id='31'><td>31</td></tr>
| <tr id='32'><td>32</td></tr>
| <tr id='33'><td>33</td></tr>
| <tr id='34'><td>34</td></tr>
| <tr id='35'><td>35</td></tr>
| <tr id='36'><td>36</td></tr>
| </table>
| </div>
*/
_childTree = [[[HTMLParser alloc] initWithString:@"<div><table>"
"<tr id='11'><td>11</td></tr>"
"<tr id='12'><td>12</td></tr>"
"<tr id='13'><td>13</td></tr>"
"<tr id='14'><td>14</td></tr>"
"</table></div>"
"<div><table>"
"<tr id='21'><td>21</td></tr>"
"</table></div>"
"<div><table>"
"<tr id='31'><td>31</td></tr>"
"<tr id='32'><td>32</td></tr>"
"<tr id='33'><td>33</td></tr>"
"<tr id='34'><td>34</td></tr>"
"<tr id='35'><td>35</td></tr>"
"<tr id='36'><td>36</td></tr>"
"</table></div>"] document];
/*
| <div>
| <span id='s11'>s11</span>
| <span id='s12'>s12</span>
| <span id='s13'>s13</span>
| <b id='b1'>b1</b>
| </div>
| <div>
| <b id='b2'>b2</b>
| <span id='s21'>s21</span>
| <span id='s22'>s22</span>
| <span id='s23'>s23</span>
| </div>
| <div>
| <span id='s31'>s31</span>
| <b id='b3'>b3</b>
| <span id='s32'>s32</span>
| <b id='b4'>b4</b>
| <span id='s33'>s33</span>
| <b id='b5'>b5</b>
| <span id='s34'>s34</span>
| </div>
*/
_typeTree = [[[HTMLParser alloc] initWithString:@"<div>"
"<span id='s11'>s11</span>"
"<span id='s12'>s12</span>"
"<span id='s13'>s13</span>"
"<b id='b1'>b1</b>"
"</div>"
"<div>"
"<b id='b2'>b2</b>"
"<span id='s21'>s21</span>"
"<span id='s22'>s22</span>"
"<span id='s23'>s23</span>"
"</div>"
"<div>"
"<span id='s31'>s31</span>"
"<b id='b3'>b3</b>"
"<span id='s32'>s32</span>"
"<b id='b4'>b4</b>"
"<span id='s33'>s33</span>"
"<b id='b5'>b5</b>"
"<span id='s34'>s34</span>"
"</div>"] document];
}
- (void)testOddSelector
{
NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), oddSelector()])];
NSArray *expected = @[@"11", @"13", @"21", @"31", @"33", @"35"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
- (void)testEvenSelector
{
NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), evenSlector()])];
NSArray *expected = @[@"12", @"14", @"32", @"34", @"36"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
- (void)testFirstChildSelector
{
NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), firstChildSelector()])];
NSArray *expected = @[@"11", @"21", @"31"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
- (void)testLastChildSelector
{
NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), lastChildSelector()])];
NSArray *expected = @[@"14", @"21", @"36"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
- (void)testFirstOfTypeSelector
{
NSArray *elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), firstOfTypeSelector()])];
NSArray *expected = @[@"s11", @"s21", @"s31"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
- (void)testLastOfTypeSelector
{
NSArray *elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), lastOfTypeSelector()])];
NSArray *expected = @[@"s13", @"s23", @"s34"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
- (void)testOnlyChildSelector
{
NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), onlyChildSelector()])];
NSArray *expected = @[@"21"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
- (void)testOnlyOfTypeSelector
{
NSArray *elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), onlyOfTypeSelector()])];
NSArray *expected = @[];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), onlyOfTypeSelector()])];
expected = @[];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"b"), onlyOfTypeSelector()])];
expected = @[@"b1", @"b2"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
#pragma mark - Nth-Child Selector
- (void)test_NthChild_B_Selector
{
NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthChildSelector(CSSNthExpressionMake(0, 1))])];
NSArray *expected = @[@"11", @"21", @"31"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthChildSelector(CSSNthExpressionMake(0, 2))])];
expected = @[@"12", @"32"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthChildSelector(CSSNthExpressionMake(0, 3))])];
expected = @[@"13", @"33"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
- (void)test_NthChild_An_Selector
{
NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthChildSelector(CSSNthExpressionMake(2, 0))])];
NSArray *expected = @[@"12", @"14", @"32", @"34", @"36"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthChildSelector(CSSNthExpressionMake(3, 0))])];
expected = @[@"13", @"33", @"36"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthChildSelector(CSSNthExpressionMake(4, 0))])];
expected = @[@"14", @"34"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
- (void)test_NthChild_An_B_Selector
{
NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthChildSelector(CSSNthExpressionMake(3, 1))])];
NSArray *expected = @[@"11", @"14", @"21", @"31", @"34"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthChildSelector(CSSNthExpressionMake(3, 2))])];
expected = @[@"12", @"32", @"35"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
#pragma mark - Nth-Last-Child Selector
- (void)test_NthLastChild_B_Selector
{
NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthLastChildSelector(CSSNthExpressionMake(0, 1))])];
NSArray *expected = @[@"14", @"21", @"36"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthLastChildSelector(CSSNthExpressionMake(0, 2))])];
expected = @[@"13", @"35"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthLastChildSelector(CSSNthExpressionMake(0, 3))])];
expected = @[@"12", @"34"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
- (void)test_NthLastChild_An_Selector
{
NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthLastChildSelector(CSSNthExpressionMake(2, 0))])];
NSArray *expected = @[@"11", @"13", @"31", @"33", @"35"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthLastChildSelector(CSSNthExpressionMake(3, 0))])];
expected = @[@"12", @"31", @"34"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthLastChildSelector(CSSNthExpressionMake(4, 0))])];
expected = @[@"11", @"33"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
- (void)test_NthLastChild_An_B_Selector
{
NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthLastChildSelector(CSSNthExpressionMake(3, 1))])];
NSArray *expected = @[@"11", @"14", @"21", @"33", @"36"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthLastChildSelector(CSSNthExpressionMake(3, 2))])];
expected = @[@"13", @"32", @"35"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
#pragma mark - Nth-Of-Type Selector
- (void)test_NthOfType_B_Selector
{
NSArray *elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthOfTypeSelector(CSSNthExpressionMake(0, 1))])];
NSArray *expected = @[@"s11", @"s21", @"s31"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthOfTypeSelector(CSSNthExpressionMake(0, 2))])];
expected = @[@"s12", @"s22", @"s32"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthOfTypeSelector(CSSNthExpressionMake(0, 3))])];
expected = @[@"s13", @"s23", @"s33"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
- (void)test_NthOfType_An_Selector
{
NSArray *elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthOfTypeSelector(CSSNthExpressionMake(2, 0))])];
NSArray *expected = @[@"s12", @"s22", @"s32", @"s34"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthOfTypeSelector(CSSNthExpressionMake(3, 0))])];
expected = @[@"s13", @"s23", @"s33"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthOfTypeSelector(CSSNthExpressionMake(4, 0))])];
expected = @[@"s34"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
- (void)test_NthOfType_An_B_Selector
{
NSArray *elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthOfTypeSelector(CSSNthExpressionMake(2, 1))])];
NSArray *expected = @[@"s11", @"s13", @"s21", @"s23", @"s31", @"s33"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthOfTypeSelector(CSSNthExpressionMake(2, 2))])];
expected = @[@"s12", @"s22", @"s32", @"s34"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
#pragma mark - Nth-Last-Of-Type Selector
- (void)test_NthLastOfType_B_Selector
{
NSArray *elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthLastOfTypeSelector(CSSNthExpressionMake(0, 1))])];
NSArray *expected = @[@"s13", @"s23", @"s34"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthLastOfTypeSelector(CSSNthExpressionMake(0, 2))])];
expected = @[@"s12", @"s22", @"s33"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthLastOfTypeSelector(CSSNthExpressionMake(0, 3))])];
expected = @[@"s11", @"s21", @"s32"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
- (void)test_NthLastOfType_An_Selector
{
NSArray *elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthLastOfTypeSelector(CSSNthExpressionMake(2, 0))])];
NSArray *expected = @[@"s12", @"s22", @"s31", @"s33"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthLastOfTypeSelector(CSSNthExpressionMake(3, 0))])];
expected = @[@"s11", @"s21", @"s32"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthLastOfTypeSelector(CSSNthExpressionMake(4, 0))])];
expected = @[@"s31"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
- (void)test_NthLastOfType_An_B_Selector
{
NSArray *elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthLastOfTypeSelector(CSSNthExpressionMake(2, 1))])];
NSArray *expected = @[@"s11", @"s13", @"s21", @"s23", @"s32", @"s34"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthLastOfTypeSelector(CSSNthExpressionMake(2, 2))])];
expected = @[@"s12", @"s22", @"s31", @"s33"];
XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
}
@end
+158
View File
@@ -0,0 +1,158 @@
//
// CSSNthExpressionsParserTests.m
// HTMLKit
//
// Created by Iska on 10/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "CSSNthExpressionParser.h"
@interface CSSNthExpressionsParserTests : XCTestCase
@end
@implementation CSSNthExpressionsParserTests
- (void)testOddEvenExpression
{
CSSNthExpression odd = [CSSNthExpressionParser parseExpression:@"odd"];
XCTAssertEqual(odd.an, 2);
XCTAssertEqual(odd.b, 1);
odd = [CSSNthExpressionParser parseExpression:@" odd"];
XCTAssertEqual(odd.an, 2);
XCTAssertEqual(odd.b, 1);
odd = [CSSNthExpressionParser parseExpression:@"odd "];
XCTAssertEqual(odd.an, 2);
XCTAssertEqual(odd.b, 1);
odd = [CSSNthExpressionParser parseExpression:@" odd "];
XCTAssertEqual(odd.an, 2);
XCTAssertEqual(odd.b, 1);
CSSNthExpression even = [CSSNthExpressionParser parseExpression:@"even"];
XCTAssertEqual(even.an, 2);
XCTAssertEqual(even.b, 0);
even = [CSSNthExpressionParser parseExpression:@" even"];
XCTAssertEqual(even.an, 2);
XCTAssertEqual(even.b, 0);
even = [CSSNthExpressionParser parseExpression:@"even "];
XCTAssertEqual(even.an, 2);
XCTAssertEqual(even.b, 0);
even = [CSSNthExpressionParser parseExpression:@" even "];
XCTAssertEqual(even.an, 2);
XCTAssertEqual(even.b, 0);
}
- (void)test_B_Expression
{
CSSNthExpression expression = [CSSNthExpressionParser parseExpression:@"1"];
XCTAssertEqual(expression.an, 0);
XCTAssertEqual(expression.b, 1);
expression = [CSSNthExpressionParser parseExpression:@"+1"];
XCTAssertEqual(expression.an, 0);
XCTAssertEqual(expression.b, 1);
expression = [CSSNthExpressionParser parseExpression:@"-1"];
XCTAssertEqual(expression.an, 0);
XCTAssertEqual(expression.b, -1);
expression = [CSSNthExpressionParser parseExpression:@" -1"];
XCTAssertEqual(expression.an, 0);
XCTAssertEqual(expression.b, -1);
expression = [CSSNthExpressionParser parseExpression:@"+1 "];
XCTAssertEqual(expression.an, 0);
XCTAssertEqual(expression.b, 1);
expression = [CSSNthExpressionParser parseExpression:@" +1 "];
XCTAssertEqual(expression.an, 0);
XCTAssertEqual(expression.b, 1);
}
- (void)test_AN_Expression
{
CSSNthExpression expression = [CSSNthExpressionParser parseExpression:@"n"];
XCTAssertEqual(expression.an, 1);
XCTAssertEqual(expression.b, 0);
expression = [CSSNthExpressionParser parseExpression:@"+n"];
XCTAssertEqual(expression.an, 1);
XCTAssertEqual(expression.b, 0);
expression = [CSSNthExpressionParser parseExpression:@"2n"];
XCTAssertEqual(expression.an, 2);
XCTAssertEqual(expression.b, 0);
expression = [CSSNthExpressionParser parseExpression:@"+2n"];
XCTAssertEqual(expression.an, 2);
XCTAssertEqual(expression.b, 0);
expression = [CSSNthExpressionParser parseExpression:@"-n"];
XCTAssertEqual(expression.an, -1);
XCTAssertEqual(expression.b, 0);
expression = [CSSNthExpressionParser parseExpression:@"-2n"];
XCTAssertEqual(expression.an, -2);
XCTAssertEqual(expression.b, 0);
}
- (void)test_AN_B_Expression
{
CSSNthExpression expression = [CSSNthExpressionParser parseExpression:@"2n+1"];
XCTAssertEqual(expression.an, 2);
XCTAssertEqual(expression.b, 1);
expression = [CSSNthExpressionParser parseExpression:@"+2n+1"];
XCTAssertEqual(expression.an, 2);
XCTAssertEqual(expression.b, 1);
expression = [CSSNthExpressionParser parseExpression:@"-2n+1"];
XCTAssertEqual(expression.an, -2);
XCTAssertEqual(expression.b, 1);
expression = [CSSNthExpressionParser parseExpression:@"2n-1"];
XCTAssertEqual(expression.an, 2);
XCTAssertEqual(expression.b, -1);
expression = [CSSNthExpressionParser parseExpression:@"-2n-1"];
XCTAssertEqual(expression.an, -2);
XCTAssertEqual(expression.b, -1);
expression = [CSSNthExpressionParser parseExpression:@"n-1"];
XCTAssertEqual(expression.an, 1);
XCTAssertEqual(expression.b, -1);
expression = [CSSNthExpressionParser parseExpression:@"-n-1"];
XCTAssertEqual(expression.an, -1);
XCTAssertEqual(expression.b, -1);
expression = [CSSNthExpressionParser parseExpression:@"+n-1"];
XCTAssertEqual(expression.an, 1);
XCTAssertEqual(expression.b, -1);
expression = [CSSNthExpressionParser parseExpression:@"+2n + 1"];
XCTAssertEqual(expression.an, 2);
XCTAssertEqual(expression.b, 1);
expression = [CSSNthExpressionParser parseExpression:@"-2n + 1"];
XCTAssertEqual(expression.an, -2);
XCTAssertEqual(expression.b, 1);
expression = [CSSNthExpressionParser parseExpression:@" 2n - 1"];
XCTAssertEqual(expression.an, 2);
XCTAssertEqual(expression.b, -1);
expression = [CSSNthExpressionParser parseExpression:@" -2n -1 "];
XCTAssertEqual(expression.an, -2);
XCTAssertEqual(expression.b, -1);
}
@end
+104
View File
@@ -0,0 +1,104 @@
//
// CSSSelectorParserTests.m
// HTMLKit
//
// Created by Iska on 23/10/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import <XCTest/XCTest.h>
#import <HTMLKit/HTMLKit.h>
#import "CSSSelectorTest.h"
#import "CSSSelectorParser.h"
@interface CSSSelectorParserTests : XCTestCase
@property (nonatomic, strong) CSSSelectorTest *testCase;
@end
@implementation CSSSelectorParserTests
+ (XCTestSuite *)defaultTestSuite
{
XCTestSuite *suite = [[XCTestSuite alloc] initWithName:NSStringFromClass(self)];
NSArray *tests = [CSSSelectorTest loadCSSSelectorTests];
for (CSSSelectorTest *test in tests) {
[self addSelectorTest:test toTestSuite:suite];
}
return suite;
}
+ (void)addSelectorTest:(CSSSelectorTest *)selectorTest toTestSuite:(XCTestSuite *)suite
{
NSArray *allInvocations = [self testInvocations];
for (NSInvocation *invocation in allInvocations) {
XCTestCase *testCase = [[self alloc] initWithInvocation:invocation
testCase:selectorTest];
[suite addTest:testCase];
}
}
#pragma mark - Instance
- (instancetype)initWithInvocation:(NSInvocation *)invocation
testCase:(CSSSelectorTest *)testCase
{
self = [super initWithInvocation:invocation];
if (self) {
_testCase = testCase;
}
return self;
}
- (NSString *)name
{
NSInvocation *invocation = [self invocation];
NSString *title = self.testCase.testName.stringByDeletingPathExtension;
return [NSString stringWithFormat:@"-[%@ %@_%@]", self.class, NSStringFromSelector(invocation.selector), title];
}
- (NSString *)description
{
return self.name;
}
#pragma mark - Tests
- (void)testParser
{
for (NSDictionary *testDescription in self.testCase.selectors) {
NSString *selectorString = testDescription[@"selector"];
NSArray *expectedMatches = testDescription[@"match"];
NSNumber *expectedError = testDescription[@"error"];
HTMLElement *testDOM = self.testCase.testDOM;
NSError *error = nil;
CSSSelector *parsedSelector = [CSSSelectorParser parseSelector:selectorString error:&error];
if (expectedError) {
XCTAssertNotNil(error);
XCTAssertNil(parsedSelector);
NSUInteger errorLocation = [error.userInfo[CSSSelectorErrorLocationKey] unsignedIntegerValue];
XCTAssertEqual(errorLocation, expectedError.unsignedIntegerValue);
} else {
XCTAssertNil(error);
XCTAssertNotNil(parsedSelector);
NSArray *matchedElements = [testDOM elementsMatchingSelector:parsedSelector];
NSArray *matchedIds = [matchedElements valueForKeyPath:@"attributes.id"];
NSString *message = [NSString stringWithFormat:@"CSS test: \'%@\'\nInput:\n%@\nDOM:\n%@\nExpected:\n%@\nActual:\n%@\n",
self.name,
selectorString,
testDOM.outerHTML,
expectedMatches,
matchedIds];
XCTAssertEqualObjects(matchedIds, expectedMatches, @"IDs mismatch:\n%@", message);
}
}
}
@end
+21
View File
@@ -0,0 +1,21 @@
//
// CSSSelectorTest.h
// HTMLKit
//
// Created by Iska on 22/11/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
@class HTMLElement;
@interface CSSSelectorTest : NSObject
@property (nonatomic, copy) NSString *testName;
@property (nonatomic, strong) NSArray *selectors;
@property (nonatomic, strong) HTMLElement *testDOM;
+ (NSArray *)loadCSSSelectorTests;
@end
+60
View File
@@ -0,0 +1,60 @@
//
// CSSSelectorTest.m
// HTMLKit
//
// Created by Iska on 22/11/15.
// Copyright © 2015 BrainCookie. All rights reserved.
//
#import "CSSSelectorTest.h"
#import "HTMLParser.h"
#import "HTMLDocument.h"
#import "HTMLElement.h"
#import "CSSSelectors.h"
static NSString * const CSSTests = @"css-tests";
@implementation CSSSelectorTest
+ (NSArray *)loadCSSSelectorTests
{
NSString *path = [[NSBundle bundleForClass:self.class] resourcePath];
path = [path stringByAppendingPathComponent:CSSTests];
NSMutableArray *tests = [NSMutableArray array];
NSArray *testFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
for (NSString *testFile in testFiles) {
if (![testFile.pathExtension isEqualToString:@"html"]) {
continue;
}
NSString *testFilePath = [path stringByAppendingPathComponent:testFile];
CSSSelectorTest *test = [CSSSelectorTest testWithFileAtPath:testFilePath];
[tests addObject:test];
}
return tests;
}
+ (instancetype)testWithFileAtPath:(NSString *)filePath
{
NSString *testName = filePath.lastPathComponent.stringByDeletingPathExtension;
NSString *html = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
HTMLDocument *document = [HTMLDocument documentWithString:html];
HTMLElement *domElement = [document firstElementMatchingSelector:idSelector(@"testDOM")];
HTMLElement *scriptElement = [document firstElementMatchingSelector:idSelector(@"selectors")];
NSData *data = [scriptElement.textContent dataUsingEncoding:NSUTF8StringEncoding];
NSArray *selectors = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
CSSSelectorTest *instance = [CSSSelectorTest new];
instance.testName = testName;
instance.selectors = selectors;
instance.testDOM = domElement;
return instance;
}
@end
+43
View File
@@ -0,0 +1,43 @@
//
// CSSTypeSelectorTests.m
// HTMLKit
//
// Created by Iska on 14/05/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "CSSSelectors.h"
#import "HTMLDOM.h"
#import "CSSSelectorParser.h"
@interface CSSTypeSelectorTests : XCTestCase
@end
@implementation CSSTypeSelectorTests
- (void)testUniversalSelector
{
CSSSelector *selector = universalSelector();
XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:nil]], YES);
XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:@""]], YES);
XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:@"div"]], YES);
XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:@"p"]], YES);
XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:@"any other name"]], YES);
}
- (void)testTypeSelector
{
CSSSelector *selector = typeSelector(@"div");
XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:nil]], NO);
XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:@""]], NO);
XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:@"p"]], NO);
XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:@"any other name"]], NO);
XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:@"div"]], YES);
}
@end
+2 -3
View File
@@ -13,7 +13,6 @@
#import "HTMLElement.h"
#import "HTMLText.h"
#import "HTMLComment.h"
#import "HTMLNodeTreeEnumerator.h"
static NSString * const HTML5LibTests = @"html5lib-tests";
static NSString * const TreeConstruction = @"tree-construction";
@@ -111,7 +110,7 @@ static NSString * const TreeConstruction = @"tree-construction";
fragment = [fragment substringFromIndex:@"svg ".length];
namespace = HTMLNamespaceSVG;
}
test.documentFragment = [[HTMLElement alloc] initWithTagName:fragment attributes:nil namespace:namespace];
test.documentFragment = [[HTMLElement alloc] initWithTagName:fragment namespace:namespace attributes:nil];
} else if ([match hasPrefix:@"#document\n"]) {
NSArray *parts = [[match substringFromIndex:@"#document\n".length] componentsSeparatedByString:@"| "];
NSArray *nodes = [HTML5LibTreeConstructionTest parseDocument:parts];
@@ -245,7 +244,7 @@ NS_INLINE HTMLElement * parseTag(NSString *str)
NSString *tagName = parts.count == 2 ? parts[1] : parts[0];
HTMLNamespace namespace = parts.count == 1 ? HTMLNamespaceHTML : ([parts[0] isEqualToString:@"math"] ? HTMLNamespaceMathML : HTMLNamespaceSVG);
HTMLElement *element = [[HTMLElement alloc] initWithTagName:tagName attributes:nil namespace:namespace];
HTMLElement *element = [[HTMLElement alloc] initWithTagName:tagName namespace:namespace attributes:nil];
return element;
}
@@ -0,0 +1,376 @@
//
// HTMLKitMutationAlgorithmsTests.m
// HTMLKit
//
// Created by Iska on 27/04/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "HTMLDOM.h"
#import "NSString+HTMLKit.h"
extern uint64_t dispatch_benchmark(size_t count, void (^block)(void));
@interface HTMLKitMutationAlgorithmsTests : XCTestCase
@end
@implementation HTMLKitMutationAlgorithmsTests
- (void)testValidParentNodeWhenAppending
{
HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"];
id parent = [HTMLDocument new];
XCTAssertNoThrow([parent appendNode:element]);
parent = [HTMLDocumentFragment new];
XCTAssertNoThrow([parent appendNode:element]);
parent = [HTMLElement new];
XCTAssertNoThrow([parent appendNode:element]);
parent = [HTMLTemplate new];
XCTAssertNoThrow([parent appendNode:element]);
parent = [HTMLDocumentType new];
XCTAssertThrows([parent appendNode:element]);
parent = [HTMLComment new];
XCTAssertThrows([parent appendNode:element]);
parent = [HTMLText new];
XCTAssertThrows([parent appendNode:element]);
}
- (void)testValidParentNodeWhenInserting
{
HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"];
id parent = [HTMLDocument new];
XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]);
parent = [HTMLDocumentFragment new];
XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]);
parent = [HTMLElement new];
XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]);
parent = [HTMLTemplate new];
XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]);
parent = [HTMLDocumentType new];
XCTAssertThrows([parent insertNode:element beforeChildNode:nil]);
parent = [HTMLComment new];
XCTAssertThrows([parent insertNode:element beforeChildNode:nil]);
parent = [HTMLText new];
XCTAssertThrows([parent insertNode:element beforeChildNode:nil]);
}
- (void)testValidChildNodeWhenInserting
{
HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div"];
HTMLElement *p = [[HTMLElement alloc] initWithTagName:@"p"];
[div appendNode:p];
HTMLElement *table = [[HTMLElement alloc] initWithTagName:@"table"];
XCTAssertNoThrow([div insertNode:table beforeChildNode:p]);
[div removeChildNode:p];
HTMLElement *section = [[HTMLElement alloc] initWithTagName:@"section"];
XCTAssertThrows([div insertNode:section beforeChildNode:p]);
}
- (void)testValidChildNodeWhenReplacing
{
HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div"];
HTMLElement *p = [[HTMLElement alloc] initWithTagName:@"p"];
[div appendNode:p];
HTMLElement *table = [[HTMLElement alloc] initWithTagName:@"table"];
XCTAssertNoThrow([div replaceChildNode:p withNode:table]);
XCTAssertThrows([div replaceChildNode:p withNode:[HTMLComment new]]);
}
- (void)testValidChildNodeWhenRemoving
{
HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div"];
HTMLElement *p = [[HTMLElement alloc] initWithTagName:@"p"];
[div appendNode:p];
XCTAssertNoThrow([div removeChildNode:p]);
XCTAssertThrows([div removeChildNode:p]);
}
- (void)testValidInsertedNode
{
HTMLDocument *document = [HTMLDocument new];
XCTAssertNoThrow([document appendNode:[HTMLDocumentFragment new]]);
XCTAssertNoThrow([document appendNode:[HTMLDocumentType new]]);
XCTAssertNoThrow([document appendNode:[HTMLElement new]]);
HTMLElement *element = [HTMLElement new];
XCTAssertNoThrow([element appendNode:[HTMLTemplate new]]);
XCTAssertNoThrow([element appendNode:[HTMLComment new]]);
XCTAssertNoThrow([element appendNode:[HTMLText new]]);
}
- (void)testValidParentForDoctype
{
HTMLDocumentType *doctype = [HTMLDocumentType new];
XCTAssertNoThrow([[HTMLDocument new] appendNode:doctype]);
XCTAssertThrows([[HTMLDocumentFragment new] appendNode:doctype]);
XCTAssertThrows([[HTMLDocumentType new] appendNode:doctype]);
XCTAssertThrows([[HTMLElement new] appendNode:doctype]);
XCTAssertThrows([[HTMLTemplate new] appendNode:doctype]);
XCTAssertThrows([[HTMLComment new] appendNode:doctype]);
XCTAssertThrows([[HTMLText new] appendNode:doctype]);
}
- (void)testValidParentForText
{
HTMLText *text = [HTMLText new];
XCTAssertThrows([[HTMLDocument new] appendNode:text]);
XCTAssertThrows([[HTMLDocumentType new] appendNode:text]);
XCTAssertThrows([[HTMLComment new] appendNode:text]);
XCTAssertThrows([[HTMLText new] appendNode:text]);
XCTAssertNoThrow([[HTMLDocumentFragment new] appendNode:text]);
XCTAssertNoThrow([[HTMLElement new] appendNode:text]);
XCTAssertNoThrow([[HTMLTemplate new] appendNode:text]);
}
- (void)testValidDocumentFragmentInsertionIntoDocument
{
HTMLDocument *document = [HTMLDocument new];
HTMLDocumentFragment *fragment = [[HTMLDocumentFragment alloc] initWithDocument:document];
void (^ reset)() = ^ {
[fragment removeAllChildNodes];
[document removeAllChildNodes];
};
/**
* Fragment has a Text node child
*/
[fragment appendNode:[HTMLText new]];
XCTAssertThrows([document appendNode:fragment]);
/**
* Fragment has more than one Element child
*/
reset();
[fragment appendNode:[HTMLElement new]];
[fragment appendNode:[HTMLElement new]];
XCTAssertThrows([document appendNode:fragment]);
/**
* Fragment has one node child
* Document has an Element child
*/
reset();
[fragment appendNode:[HTMLElement new]];
[document appendNode:[HTMLElement new]];
XCTAssertThrows([document appendNode:fragment]);
/**
* Fragment has one node child
* "before child" is a Doctype
*/
reset();
HTMLDocumentType *doctype = [HTMLDocumentType new];
[fragment appendNode:[HTMLElement new]];
[document appendNode:doctype];
XCTAssertThrows([document insertNode:fragment beforeChildNode:doctype]);
/**
* Fragment has one node child
* "before child" is following a Doctype
*/
reset();
HTMLComment *doctypePreviousSibling = [HTMLComment new];
[fragment appendNode:[HTMLElement new]];
[document appendNode:doctypePreviousSibling];
[document appendNode:doctype];
XCTAssertThrows([document insertNode:fragment beforeChildNode:doctypePreviousSibling]);
}
- (void)testValidElementInsertionIntoDocument
{
HTMLDocument *document = [HTMLDocument new];
HTMLElement *element = [HTMLElement new];
void (^ reset)() = ^ {
[element removeAllChildNodes];
[document removeAllChildNodes];
};
/**
* Document has an Element child
*/
[document appendNode:[HTMLElement new]];
XCTAssertThrows([document appendNode:element]);
/**
* "before child" is a Doctype
*/
reset();
HTMLDocumentType *doctype = [HTMLDocumentType new];
[document appendNode:doctype];
XCTAssertThrows([document insertNode:element beforeChildNode:doctype]);
/**
* Doctype is following the "before child"
*/
reset();
HTMLComment *doctypePreviousSibling = [HTMLComment new];
[document appendNode:doctypePreviousSibling];
[document appendNode:doctype];
XCTAssertThrows([document insertNode:element beforeChildNode:doctypePreviousSibling]);
}
- (void)testValidDoctypeInsertionIntoDocument
{
HTMLDocument *document = [HTMLDocument new];
HTMLDocumentType *doctype = [HTMLDocumentType new];
void (^ reset)() = ^ {
[document removeAllChildNodes];
};
/**
* Document has a Doctype child
*/
[document appendNode:[HTMLDocumentType new]];
XCTAssertThrows([document appendNode:doctype]);
/**
* An Element is preceding the "before child"
*/
reset();
HTMLComment *secondChild = [HTMLComment new];
[document appendNode:[HTMLElement new]];
[document appendNode:secondChild];
XCTAssertThrows([document insertNode:doctype beforeChildNode:secondChild]);
/**
* Document has an Element child
*/
reset();
[document appendNode:[HTMLElement new]];
XCTAssertThrows([document appendNode:doctype]);
}
- (void)testValidDocumentFragmentReplacementIntoDocument
{
HTMLDocument *document = [HTMLDocument new];
HTMLComment *child = [HTMLComment new];
HTMLDocumentFragment *replacement = [[HTMLDocumentFragment alloc] initWithDocument:document];
void (^ reset)() = ^ {
[replacement removeAllChildNodes];
[document removeAllChildNodes];
[document appendNode:child];
};
/**
* Replacement Fragment has a Text node child
*/
[replacement appendNode:[HTMLText new]];
XCTAssertThrows([document replaceChildNode:child withNode:replacement]);
/**
* Replacement Fragment has more than one Element child
*/
reset();
[replacement appendNode:[HTMLElement new]];
[replacement appendNode:[HTMLElement new]];
XCTAssertThrows([document replaceChildNode:child withNode:replacement]);
/**
* Replacement Fragment has one Element child
* Document has an Element child that is not the Replacement
*/
reset();
[replacement appendNode:[HTMLElement new]];
[document appendNode:[HTMLElement new]];
XCTAssertThrows([document replaceChildNode:child withNode:replacement]);
/**
* Replacement Fragment has one Element child
* Doctype is following the child node
*/
reset();
HTMLDocumentType *doctype = [HTMLDocumentType new];
[replacement appendNode:[HTMLElement new]];
[document appendNode:doctype];
XCTAssertThrows([document replaceChildNode:child withNode:replacement]);
}
- (void)testValidElementReplacementIntoDocument
{
HTMLDocument *document = [HTMLDocument new];
HTMLComment *child = [HTMLComment new];
HTMLElement *replacement = [HTMLElement new];
void (^ reset)() = ^ {
[replacement removeAllChildNodes];
[document removeAllChildNodes];
[document appendNode:child];
};
/**
* Docment has an Element child that is not replacement
*/
[document appendNode:[HTMLElement new]];
XCTAssertThrows([document replaceChildNode:child withNode:replacement]);
/**
* Doctype is following the child node
*/
reset();
HTMLDocumentType *doctype = [HTMLDocumentType new];
[document appendNode:doctype];
XCTAssertThrows([document replaceChildNode:child withNode:replacement]);
}
- (void)testValidDoctypeReplacementIntoDocument
{
HTMLDocument *document = [HTMLDocument new];
HTMLComment *child = [HTMLComment new];
HTMLDocumentType *replacement = [HTMLDocumentType new];
void (^ reset)() = ^ {
[replacement removeAllChildNodes];
[document removeAllChildNodes];
[document appendNode:child];
};
/**
* Docment has an Doctype child that is not replacement
*/
[document appendNode:[HTMLDocumentType new]];
XCTAssertThrows([document replaceChildNode:child withNode:replacement]);
/**
* An Element is preceding the child node
*/
reset();
[document insertNode:[HTMLElement new] beforeChildNode:child];
XCTAssertThrows([document replaceChildNode:child withNode:replacement]);
}
@end
+568
View File
@@ -0,0 +1,568 @@
//
// HTMLKitNodeTreeEnumratorTests.m
// HTMLKit
//
// Created by Iska on 28/03/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "HTMLDOM.h"
@interface HTMLKitNodeIteratorTests : XCTestCase
@end
@implementation HTMLKitNodeIteratorTests
#pragma mark - Test DOM
- (HTMLElement *)div
{
return [[HTMLElement alloc] initWithTagName:@"div"];
}
- (HTMLElement *)simpleTree
{
/*
| div
| a
| b
| c
*/
HTMLElement *div = self.div;
[div appendNode:[[HTMLElement alloc] initWithTagName:@"a"]];
[div appendNode:[[HTMLElement alloc] initWithTagName:@"b"]];
[div appendNode:[[HTMLElement alloc] initWithTagName:@"c"]];
return div;
}
- (HTMLElement *)nestedSimpleTree
{
/*
| div
| div
| a
| b
| c
| div
| a
| b
| c
*/
HTMLElement *div = self.div;
[div appendNode:self.simpleTree];
[div appendNode:self.simpleTree];
return div;
}
- (HTMLElement *)complexTree
{
/*
| div
| div
| div
| a
| b
| c
| e
| f
| div
| a
| b
| c
*/
HTMLElement *root = self.div;
HTMLElement *div = self.div;
[div appendNode:self.simpleTree];
[root appendNode:div];
HTMLElement *e = [[HTMLElement alloc] initWithTagName:@"e"];
[e appendNode:[[HTMLElement alloc] initWithTagName:@"f"]];
[root appendNode:e];
[root appendNode:self.simpleTree];
return root;
}
- (HTMLDocument *)mixedTree
{
/*
| doctype
| #comment <!-- This is a Comment -->
| #comment <!-- This is a second Comment -->
| html
| #text "This is a Text"
| div
*/
HTMLDocument *document = [HTMLDocument new];
document.documentType = [HTMLDocumentType new];;
HTMLComment *comment = [[HTMLComment alloc] initWithData:@"This is a Comment"];
[document appendNode:comment];
HTMLComment *secondCommnet = [[HTMLComment alloc] initWithData:@"This is a second Comment"];
[document appendNode:secondCommnet];
HTMLElement *root = [[HTMLElement alloc] initWithTagName:@"html"];
[document appendNode:root];
[root appendNode:[[HTMLText alloc] initWithData:@"This is a Text"]];
[root appendNode:[[HTMLElement alloc] initWithTagName:@"div"]];
return document;
}
- (HTMLDocument *)document
{
NSString *htmlString =
@"<!DOCTYPE html>"
@"<html>"
@"<head>"
@"<title>Title</title>"
@"</head>"
@"<body>"
@"<span>Hello<strong>World!</strong><strong>HTML <!-- This is a Comment! --> Kit</strong></span>"
@"<p>This is an <em>Important</em> paragraph</p>"
@"</body>"
@"</html>";
HTMLDocument *document = [HTMLDocument documentWithString:htmlString];
return document;
}
#pragma mark - Test Iterator
- (void)testNodeIteratorInit
{
HTMLElement *tree = self.simpleTree;
HTMLNodeIterator *iterator = tree.nodeIterator;
XCTAssertEqualObjects(iterator.root, tree);
XCTAssertEqualObjects(iterator.referenceNode, tree);
XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES);
XCTAssertEqualObjects(iterator.filter, nil);
XCTAssertEqual(iterator.whatToShow, HTMLNodeFilterShowAll);
}
- (void)testNewIteratorNextNodeShouldBeRoot
{
HTMLElement *tree = self.simpleTree;
HTMLNodeIterator *iterator = tree.nodeIterator;
XCTAssertEqualObjects(iterator.nextNode, tree);
XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO);
}
- (void)testNewIteratorPreviousNodeShouldBeNil
{
HTMLElement *tree = self.simpleTree;
HTMLNodeIterator *iterator = tree.nodeIterator;
XCTAssertEqualObjects(iterator.previousNode, nil);
XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES);
}
- (void)testNewIteratorPreviousNodeShouldBeNextNode
{
HTMLElement *tree = self.simpleTree;
HTMLNodeIterator *iterator = tree.nodeIterator;
HTMLNode *node = iterator.nextNode;
XCTAssertEqualObjects(iterator.previousNode, node);
XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES);
}
- (void)testNextPreviousIteration
{
HTMLElement *tree = self.simpleTree;
HTMLNodeIterator *iterator = tree.nodeIterator;
XCTAssertEqualObjects(iterator.previousNode, nil);
XCTAssertEqualObjects(iterator.nextNode.name, @"div");
XCTAssertEqualObjects(iterator.nextNode.name, @"a");
XCTAssertEqualObjects(iterator.previousNode.name, @"a");
XCTAssertEqualObjects(iterator.previousNode.name, @"div");
XCTAssertEqualObjects(iterator.nextNode.name, @"div");
XCTAssertEqualObjects(iterator.nextNode.name, @"a");
XCTAssertEqualObjects(iterator.nextNode.name, @"b");
XCTAssertEqualObjects(iterator.nextNode.name, @"c");
XCTAssertEqualObjects(iterator.previousNode.name, @"c");
XCTAssertEqualObjects(iterator.previousNode.name, @"b");
XCTAssertEqualObjects(iterator.previousNode.name, @"a");
XCTAssertEqualObjects(iterator.previousNode.name, @"div");
}
- (void)testSingleNodeIteration
{
HTMLElement *div = self.div;
NSArray *result = div.nodeIterator.allObjects;
NSArray *expected = @[@"div"];
XCTAssertEqualObjects([result valueForKey:@"name"], expected);
}
- (void)testSimpleTreeIteration
{
HTMLElement *tree = self.simpleTree;
NSArray *result = tree.nodeIterator.allObjects;
NSArray *expected = @[@"div", @"a", @"b", @"c"];
XCTAssertEqualObjects([result valueForKey:@"name"], expected);
}
- (void)testNestedSimpleTreeIteration
{
HTMLElement *tree = self.nestedSimpleTree;
NSArray *result = tree.nodeIterator.allObjects;
NSArray *expected = @[@"div", @"div", @"a", @"b", @"c", @"div", @"a", @"b", @"c"];
XCTAssertEqualObjects([result valueForKey:@"name"], expected);
}
- (void)testComplexTreeIteration
{
HTMLElement *tree = self.complexTree;
NSArray *result = tree.nodeIterator.allObjects;
NSArray *expected = @[@"div", @"div",@"div", @"a", @"b", @"c", @"e", @"f", @"div", @"a", @"b", @"c"];
XCTAssertEqualObjects([result valueForKey:@"name"], expected);
}
#pragma mark - Test Iterator ShowOptions (WhatToShow)
- (void)testShowDocument
{
HTMLDocument *document = self.mixedTree;
HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowDocument filter:nil];
NSArray *result = iterator.allObjects;
NSArray *expected = @[@"#document"];
XCTAssertEqual(result.count, 1);
XCTAssertEqual([result.firstObject class], [HTMLDocument class]);
XCTAssertEqualObjects([result valueForKey:@"name"], expected);
}
- (void)testShowDocumentType
{
HTMLDocument *document = self.mixedTree;
HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowDocumentType filter:nil];
NSArray *result = iterator.allObjects;
NSArray *expected = @[@"html"];
XCTAssertEqual(result.count, 1);
XCTAssertEqual([result.firstObject class], [HTMLDocumentType class]);
XCTAssertEqualObjects([result valueForKey:@"name"], expected);
}
- (void)testShowComment
{
HTMLDocument *document = self.mixedTree;
HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowComment filter:nil];
NSArray *result = iterator.allObjects;
NSArray *expected = @[@"#comment", @"#comment"];
XCTAssertEqual(result.count, 2);
XCTAssertEqual([result[0] class], [HTMLComment class]);
XCTAssertEqual([result[1] class], [HTMLComment class]);
XCTAssertEqualObjects([result valueForKey:@"name"], expected);
}
- (void)testShowText
{
HTMLDocument *document = self.mixedTree;
HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowText filter:nil];
NSArray *result = iterator.allObjects;
NSArray *expected = @[@"#text"];
XCTAssertEqual(result.count, 1);
XCTAssertEqual([result.firstObject class], [HTMLText class]);
XCTAssertEqualObjects([result valueForKey:@"name"], expected);
}
- (void)testShowElement
{
HTMLDocument *document = self.mixedTree;
HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil];
NSArray *result = iterator.allObjects;
NSArray *expected = @[@"html", @"div"];
XCTAssertEqual(result.count, 2);
XCTAssertEqual([result.firstObject class], [HTMLElement class]);
XCTAssertEqualObjects([result valueForKey:@"name"], expected);
}
- (void)testShowBitmask
{
HTMLDocument *document = self.mixedTree;
HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowElement | HTMLNodeFilterShowText
filter:nil];
NSArray *result = iterator.allObjects;
NSArray *expected = @[@"html", @"#text", @"div"];
XCTAssertEqual(result.count, 3);
XCTAssertEqualObjects([result valueForKey:@"name"], expected);
}
#pragma mark - Test Iterator Filter
- (void)testNodeFilter
{
HTMLDocument *document = self.mixedTree;
HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowAll
filterBlock:^HTMLNodeFilterValue(HTMLNode *node) {
if (node.nodeType == HTMLNodeComment) {
if ([[(HTMLComment *)node data] rangeOfString:@"second"].location != NSNotFound) {
return HTMLNodeFilterAccept;
}
}
return HTMLNodeFilterSkip;
}];
NSArray *result = iterator.allObjects;
NSArray *expected = @[@"#comment"];
XCTAssertEqual(result.count, 1);
XCTAssertEqualObjects([result[0] data], @"This is a second Comment");
XCTAssertEqualObjects([result valueForKey:@"name"], expected);
}
#pragma mark - Test Removing Steps
/*
Test cases for the Removing Steps
https://dom.spec.whatwg.org/#interface-nodeiterator
Following DOM is used:
| <html>
| <head>
| <title>
| "Title"
| <body>
| <span>
| "Hello"
| <strong>
| "World!"
| <strong>
| "HTML "
| <!-- This is a Comment! -->
| " Kit"
| <p>
| "This is an "
| <em>
| "Important"
| " paragraph"
*/
static void (^ RemoveThenInsertNode)(HTMLNode *) = ^ (HTMLNode *node) {
HTMLNode *parent = node.parentNode;
HTMLNode *nextSibling = node.nextSibling;
[parent removeChildNode:node];
[parent insertNode:node beforeChildNode:nextSibling];
};
static void (^ IterateUpToNode)(HTMLNodeIterator *, HTMLNode *) = ^ (HTMLNodeIterator *iterator, HTMLNode *target) {
for(HTMLNode *node = iterator.referenceNode; node && (node != target); node = iterator.nextNode);
};
static HTMLNode * (^ LastDescendant)(HTMLNode *) = ^ HTMLNode * (HTMLNode *node) {
while (node.lastChild) {
node = node.lastChild;
}
return node;
};
- (void)testThatRemovingRootNodeShouldNotAffectIterator
{
HTMLDocument *document = self.document;
HTMLNode *node = document.body.firstChild; // <span>
HTMLNodeIterator *iterator = node.nodeIterator;
[document.body removeChildNode:node];
XCTAssertEqualObjects(iterator.root, node);
XCTAssertEqualObjects(iterator.referenceNode, node);
XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES);
XCTAssertEqualObjects(iterator.referenceNode.parentNode, nil);
}
- (void)testThatRemovingANonInclusiveAnscestorOfReferenceShouldNotAffectIterator
{
HTMLDocument *document = self.document;
HTMLNode *body = document.body;
HTMLNodeIterator *iterator = body.nodeIterator;
[iterator nextNode]; // Reference node: <body>
[iterator nextNode]; // Reference node: <span>
RemoveThenInsertNode(iterator.root.childNodes[1]); // Remove <p>
XCTAssertEqualObjects(iterator.root, body);
XCTAssertEqualObjects(iterator.referenceNode, body.firstChild);
XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO);
}
- (void)testThatRemovingReferenceNodeShouldUpdateIterator_NilOldPreviousSibling
{
HTMLDocument *document = self.document;
HTMLNodeIterator *iterator = document.body.nodeIterator;
[iterator nextNode]; // Reference node: <body>
HTMLNode *node = iterator.nextNode; // Reference node: <span>
RemoveThenInsertNode(node); // Remove <span> with old previos sibling being nil
XCTAssertEqualObjects(iterator.referenceNode, iterator.root);
XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO);
HTMLNode *next = iterator.nextNode; // "Hello"
XCTAssertEqualObjects(next, iterator.root.firstChild);
}
- (void)testThatRemovingReferenceNodeShouldUpdateIterator_NonNilOldPreviousSibling_NotBeforeReference
{
HTMLDocument *document = self.document;
HTMLNode *body = document.body;
HTMLNodeIterator *iterator = body.nodeIterator;
HTMLNode *node = iterator.root.childNodes[1]; // <p>
IterateUpToNode(iterator, node); // Reference node: <p>, pointer-before-reference: NO
RemoveThenInsertNode(node); // Remove <p> with old previos sibling being <span>
XCTAssertEqualObjects(iterator.referenceNode, LastDescendant(body.firstChild));
XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO);
}
- (void)testThatRemovingReferenceNodeShouldUpdateIterator_NonNilOldPreviousSibling_BeforeReference
{
HTMLDocument *document = self.document;
HTMLNode *body = document.body;
HTMLNodeIterator *iterator = body.nodeIterator;
HTMLNode *node = iterator.root.childNodes[1]; // <p>
IterateUpToNode(iterator, node); // Reference node: <p>, pointer-before-reference: NO
[iterator previousNode]; // pointer-before-reference: YES
RemoveThenInsertNode(node); // Remove <p> with old previos sibling being <span>
XCTAssertEqualObjects(iterator.referenceNode, body.firstChild);
XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES);
}
- (void)testThatRemovingThenReinsertingReferenceNodeAfterNextShouldReturnItAgain
{
HTMLDocument *document = self.document;
HTMLNodeIterator *iterator = document.body.nodeIterator;
[iterator nextNode]; // Reference node: <body>
HTMLNode *node = iterator.nextNode; // <span>
RemoveThenInsertNode(node);
XCTAssertEqualObjects(iterator.referenceNode, iterator.root);
XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO);
HTMLNode *next = iterator.nextNode;
XCTAssertEqualObjects(next, node);
}
- (void)testThatRemovingThenReinsertingReferenceNodeAfterPreviousShouldReturnItAgain
{
HTMLDocument *document = self.document;
HTMLNodeIterator *iterator = document.body.nodeIterator;
[iterator nextNode]; // Reference node: <body>
[iterator nextNode]; // Reference node: <span>
HTMLNode *node = iterator.previousNode; // Reference node: <span>, pointer-before-reference: YES
HTMLNode *next = node.nextSibling; // <p>
RemoveThenInsertNode(node);
XCTAssertEqualObjects(iterator.referenceNode, next);
XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES);
HTMLNode *previous = iterator.previousNode;
XCTAssertEqualObjects(previous, LastDescendant(node));
}
- (void)testThatRemovingParentOfReferenceNodeShouldUpdateIterator_NotBeforeReference
{
HTMLDocument *document = self.document;
HTMLNode *body = document.body;
HTMLNodeIterator *iterator = body.nodeIterator;
HTMLNode *parent = body.childNodes[1];
IterateUpToNode(iterator, parent); // Reference node: <p>, pointer-before-reference: NO
[iterator nextNode]; // Reference node: "This is an "
RemoveThenInsertNode(parent);
XCTAssertEqualObjects(iterator.referenceNode, LastDescendant(body.firstChild));
XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO);
}
- (void)testThatRemovingParentOfReferenceNodeShouldUpdateIterator_BeforeReference
{
HTMLDocument *document = self.document;
HTMLNode *body = document.body;
HTMLNodeIterator *iterator = body.nodeIterator;
HTMLNode *parent = body.childNodes[1];
IterateUpToNode(iterator, parent); // Reference node: <p>, pointer-before-reference: NO
[iterator nextNode]; // Reference node: "This is an "
[iterator previousNode]; // pointer-before-reference: YES
RemoveThenInsertNode(parent);
XCTAssertEqualObjects(iterator.referenceNode, body.firstChild);
XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES);
}
- (void)testRemoveReferenceNode_NilPreviousSibling_NonNilParentFirstChild
{
HTMLDocument *document = self.document;
HTMLNode *body = document.body;
HTMLNodeIterator *iterator = body.nodeIterator;
[iterator nextNode]; // Reference node: <body>
[iterator nextNode]; // Reference node: <span>
HTMLNode *node = iterator.previousNode; // Reference node: <span>, pointer-before-reference: YES
XCTAssertNil(node.previousSibling);
XCTAssertNotNil(node.nextSibling);
HTMLNode *nextSibling = node.nextSibling; // <p>
RemoveThenInsertNode(node);
XCTAssertEqualObjects(iterator.referenceNode, nextSibling);
XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES);
HTMLNode *next = iterator.nextNode; // <p>
XCTAssertNotEqualObjects(next, node);
XCTAssertEqualObjects(next, nextSibling);
}
- (void)testRemoveReferenceNode_NodeAfterOldParentIsOutsideRoot_BeforeReference
{
HTMLDocument *document = self.document;
HTMLNode *body = document.body;
body.innerHTML = @"<div><p><a></a></p></div><div></div>";
HTMLNodeIterator *iterator = body.firstChild.nodeIterator;
IterateUpToNode(iterator, LastDescendant(body.firstChild)); // Referecne node: <a>
HTMLNode *node = [iterator previousNode]; // pointer-before-reference: YES
RemoveThenInsertNode(node);
XCTAssertEqualObjects(iterator.referenceNode, iterator.root.firstChild);
XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO);
}
@end
@@ -1,148 +0,0 @@
//
// HTMLKitNodeTreeEnumratorTests.m
// HTMLKit
//
// Created by Iska on 28/03/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "HTMLNodes.h"
@interface HTMLKitNodeTreeEnumratorTests : XCTestCase
@end
@implementation HTMLKitNodeTreeEnumratorTests
#pragma mark - Elements
- (HTMLElement *)div
{
return [[HTMLElement alloc] initWithTagName:@"div"];
}
- (HTMLElement *)simpleTree
{
/*
| div
| a
| b
| c
*/
HTMLElement *div = self.div;
[div appendNode:[[HTMLElement alloc] initWithTagName:@"a"]];
[div appendNode:[[HTMLElement alloc] initWithTagName:@"b"]];
[div appendNode:[[HTMLElement alloc] initWithTagName:@"c"]];
return div;
}
- (HTMLElement *)nestedSimpleTree
{
/*
| div
| div
| a
| b
| c
| div
| a
| b
| c
*/
HTMLElement *div = self.div;
[div appendNode:self.simpleTree];
[div appendNode:self.simpleTree];
return div;
}
- (HTMLElement *)complexTree
{
/*
| div
| div
| div
| a
| b
| c
| e
| f
| div
| a
| b
| c
*/
HTMLElement *root = self.div;
HTMLElement *div = self.div;
[div appendNode:self.simpleTree];
[root appendNode:div];
HTMLElement *e = [[HTMLElement alloc] initWithTagName:@"e"];
[e appendNode:[[HTMLElement alloc] initWithTagName:@"f"]];
[root appendNode:e];
[root appendNode:self.simpleTree];
return root;
}
#pragma mark - Tests
- (void)testSingle
{
HTMLElement *div = self.div;
NSArray *result = div.treeEnumerator.allObjects;
NSArray *expected = @[@"div"];
XCTAssertEqualObjects([result valueForKey:@"name"], expected);
}
- (void)testSimpleTree
{
HTMLElement *tree = self.simpleTree;
NSArray *result = tree.treeEnumerator.allObjects;
NSArray *expected = @[@"div", @"a", @"b", @"c"];
XCTAssertEqualObjects([result valueForKey:@"name"], expected);
}
- (void)testSimpleTreeReversed
{
HTMLElement *tree = self.simpleTree;
NSArray *result = tree.reverseTreeEnumerator.allObjects;
NSArray *expected = @[@"div", @"c", @"b", @"a"];
XCTAssertEqualObjects([result valueForKey:@"name"], expected);
}
- (void)testNestedSimpleTree
{
HTMLElement *tree = self.nestedSimpleTree;
NSArray *result = tree.treeEnumerator.allObjects;
NSArray *expected = @[@"div", @"div", @"a", @"b", @"c", @"div", @"a", @"b", @"c"];
XCTAssertEqualObjects([result valueForKey:@"name"], expected);
}
- (void)testNestedSimpleTreeReversed
{
HTMLElement *tree = self.nestedSimpleTree;
NSArray *result = tree.reverseTreeEnumerator.allObjects;
NSArray *expected = @[@"div", @"div", @"c", @"b", @"a", @"div", @"c", @"b", @"a"];
XCTAssertEqualObjects([result valueForKey:@"name"], expected);
}
- (void)testComplexSimpleTree
{
HTMLElement *tree = self.complexTree;
NSArray *result = tree.treeEnumerator.allObjects;
NSArray *expected = @[@"div", @"div",@"div", @"a", @"b", @"c", @"e", @"f", @"div", @"a", @"b", @"c"];
XCTAssertEqualObjects([result valueForKey:@"name"], expected);
}
- (void)testComplexSimpleTreeReversed
{
HTMLElement *tree = self.complexTree;
NSArray *result = tree.reverseTreeEnumerator.allObjects;
NSArray *expected = @[@"div", @"div", @"c", @"b", @"a", @"e", @"f", @"div", @"div", @"c", @"b", @"a"];
XCTAssertEqualObjects([result valueForKey:@"name"], expected);
}
@end
+485
View File
@@ -0,0 +1,485 @@
//
// HTMLKitNodesTests.m
// HTMLKit
//
// Created by Iska on 20/04/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "HTMLDOM.h"
@interface HTMLKitNodesTests : XCTestCase
@end
@implementation HTMLKitNodesTests
- (void)setUp
{
[super setUp];
}
- (void)tearDown
{
[super tearDown];
}
- (void)testInitNode
{
HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
XCTAssertNotNil(node);
XCTAssertEqualObjects(node.name, @"name");
XCTAssertEqual(node.nodeType, HTMLNodeElement);
XCTAssertNotNil(node.childNodes);
XCTAssertEqual(node.childNodes.count, 0);
XCTAssertNil(node.ownerDocument);
XCTAssertNil(node.parentNode);
XCTAssertNil(node.parentElement);
XCTAssertNil(node.firstChild);
XCTAssertNil(node.lastChild);
XCTAssertNil(node.previousSibling);
XCTAssertNil(node.lastChild);
}
- (void)testAppendNode
{
HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"];
HTMLComment *comment = [HTMLComment new];
[element appendNode:comment];
XCTAssertEqual(element.childNodesCount, 1);
XCTAssertEqual(element.firstChild, comment);
HTMLElement *firstElement = [HTMLElement new];
HTMLElement *secondElement = [HTMLElement new];
NSArray *nodes = @[firstElement, secondElement];
[element appendNodes:nodes];
XCTAssertEqual(element.childNodesCount, 3);
XCTAssertEqual(element.firstChild, comment);
XCTAssertEqual(element.lastChild, secondElement);
}
- (void)testPrependNode
{
HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"];
HTMLText *text = [HTMLText new];
[element appendNode:text];
HTMLComment *comment = [HTMLComment new];
[element prependNode:comment];
XCTAssertEqual(element.childNodesCount, 2);
XCTAssertEqual(element.firstChild, comment);
HTMLElement *firstElement = [HTMLElement new];
HTMLElement *secondElement = [HTMLElement new];
NSArray *nodes = @[firstElement, secondElement];
[element prependNodes:nodes];
XCTAssertEqual(element.childNodesCount, 4);
XCTAssertEqual(element.firstChild, firstElement);
XCTAssertEqual(element.lastChild, text);
}
- (void)testAppendDocumentFragment
{
HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"];
HTMLComment *comment = [HTMLComment new];
[element appendNode:comment];
HTMLDocumentFragment *fragment = [HTMLDocumentFragment new];
HTMLElement *firstChild = [HTMLElement new];
HTMLElement *secondChild = [HTMLElement new];
[fragment appendNode:firstChild];
[fragment appendNode:secondChild];
[element appendNode:fragment];
XCTAssertEqual(element.childNodesCount, 3);
XCTAssertEqual(fragment.childNodesCount, 0);
XCTAssertEqualObjects(firstChild.parentNode, element);
XCTAssertEqualObjects(secondChild.parentNode, element);
XCTAssertEqualObjects(element.firstChild, comment);
XCTAssertEqualObjects(element.firstChild.nextSibling, firstChild);
XCTAssertEqualObjects(element.lastChild.previousSibling, firstChild);
XCTAssertEqualObjects(element.lastChild, secondChild);
}
- (void)testParentNode
{
HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
XCTAssertNil(firstChild.parentNode);
[node appendNode:firstChild];
XCTAssertTrue(node.hasChildNodes);
XCTAssertEqual(node.childNodesCount, 1);
XCTAssertNotNil(firstChild.parentNode);
XCTAssertEqualObjects(firstChild.parentNode, node);
HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
[node appendNodes:@[secondChild, thirdChild]];
XCTAssertTrue(node.hasChildNodes);
XCTAssertEqual(node.childNodesCount, 3);
XCTAssertNotNil(secondChild.parentNode);
XCTAssertEqualObjects(secondChild.parentNode, node);
XCTAssertNotNil(thirdChild.parentNode);
XCTAssertEqualObjects(thirdChild.parentNode, node);
}
- (void)testChildNodesCount
{
HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
XCTAssertFalse(node.hasChildNodes);
XCTAssertEqual(node.childNodesCount, 0);
HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
[node appendNode:firstChild];
XCTAssertTrue(node.hasChildNodes);
XCTAssertEqual(node.childNodesCount, 1);
HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
[node appendNode:secondChild];
XCTAssertTrue(node.hasChildNodes);
XCTAssertEqual(node.childNodesCount, 2);
HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
[node appendNode:thirdChild];
XCTAssertTrue(node.hasChildNodes);
XCTAssertEqual(node.childNodesCount, 3);
}
- (void)testFirstAndLastChildNodes
{
HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
[node appendNode:firstChild];
XCTAssertEqualObjects(node.firstChild, firstChild);
XCTAssertEqualObjects(node.lastChild, firstChild);
HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
[node appendNode:secondChild];
XCTAssertEqualObjects(node.firstChild, firstChild);
XCTAssertEqualObjects(node.lastChild, secondChild);
HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
[node appendNode:thirdChild];
XCTAssertEqualObjects(node.firstChild, firstChild);
XCTAssertEqualObjects(node.lastChild, thirdChild);
}
- (void)testNextAndPreviousSiblingNodes
{
HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
[node appendNode:firstChild];
HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
[node appendNode:secondChild];
HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
[node appendNode:thirdChild];
XCTAssertNil(firstChild.previousSibling);
XCTAssertEqualObjects(firstChild.nextSibling, secondChild);
XCTAssertEqualObjects(secondChild.previousSibling, firstChild);
XCTAssertEqualObjects(secondChild.nextSibling, thirdChild);
XCTAssertEqualObjects(thirdChild.previousSibling, secondChild);
XCTAssertNil(thirdChild.nextSibling);
}
- (void)testHasChildNodeOfType
{
HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
XCTAssertFalse([node hasChildNodeOfType:HTMLNodeElement]);
XCTAssertFalse([node hasChildNodeOfType:HTMLNodeText]);
XCTAssertFalse([node hasChildNodeOfType:HTMLNodeComment]);
HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
[node appendNode:firstChild];
XCTAssertTrue([node hasChildNodeOfType:HTMLNodeElement]);
HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeText];
[node appendNode:secondChild];
XCTAssertTrue([node hasChildNodeOfType:HTMLNodeText]);
HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeComment];
[node appendNode:thirdChild];
XCTAssertTrue([node hasChildNodeOfType:HTMLNodeComment]);
}
- (void)testChildNodeAtIndex
{
HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
[node appendNode:firstChild];
HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
[node appendNode:secondChild];
HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
[node appendNode:thirdChild];
XCTAssertEqualObjects([node childNodeAtIndex:0], firstChild);
XCTAssertEqualObjects([node childNodeAtIndex:1], secondChild);
XCTAssertEqualObjects([node childNodeAtIndex:2], thirdChild);
}
- (void)testIndexOfChildNode
{
HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
[node appendNode:firstChild];
HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
[node appendNode:secondChild];
HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
[node appendNode:thirdChild];
XCTAssertEqual([node indexOfChildNode:firstChild], 0);
XCTAssertEqual([node indexOfChildNode:secondChild], 1);
XCTAssertEqual([node indexOfChildNode:thirdChild], 2);
}
- (void)testIndertNodeBeforeChild
{
HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
[node appendNode:firstChild];
HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
[node insertNode:secondChild beforeChildNode:firstChild];
XCTAssertEqual([node indexOfChildNode:firstChild], 1);
XCTAssertEqual([node indexOfChildNode:secondChild], 0);
HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
[node insertNode:thirdChild beforeChildNode:firstChild];
XCTAssertEqual([node indexOfChildNode:firstChild], 2);
XCTAssertEqual([node indexOfChildNode:secondChild], 0);
XCTAssertEqual([node indexOfChildNode:thirdChild], 1);
}
- (void)testReplaceChildNode
{
HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
[node appendNode:firstChild];
HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
[node appendNode:secondChild];
HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
[node replaceChildNode:firstChild withNode:thirdChild];
XCTAssertNil(firstChild.parentNode);
XCTAssertEqual([node indexOfChildNode:firstChild], NSNotFound);
XCTAssertEqual([node indexOfChildNode:secondChild], 1);
XCTAssertEqual([node indexOfChildNode:thirdChild], 0);
}
- (void)testReplaceAllChildNodes
{
HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
[node appendNode:firstChild];
HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
[node appendNode:secondChild];
HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
[node replaceAllChildNodesWithNode:thirdChild];
XCTAssertNil(firstChild.parentNode);
XCTAssertNil(secondChild.parentNode);
XCTAssertEqualObjects(thirdChild.parentNode, node);
XCTAssertEqual(node.childNodesCount, 1);
XCTAssertEqual([node indexOfChildNode:thirdChild], 0);
}
- (void)testRemoveFromParentNode
{
HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
[node appendNode:firstChild];
[firstChild removeFromParentNode];
XCTAssertNil(firstChild.parentNode);
XCTAssertEqual(node.childNodesCount, 0);
XCTAssertEqual([node indexOfChildNode:firstChild], NSNotFound);
}
- (void)testRemoveChildNode
{
HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
[node appendNode:firstChild];
HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
[node appendNode:secondChild];
HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
[node appendNode:thirdChild];
[node removeChildNode:secondChild];
XCTAssertEqual(node.childNodesCount, 2);
XCTAssertEqualObjects(firstChild.parentNode, node);
XCTAssertNil(secondChild.parentNode);
XCTAssertEqualObjects(thirdChild.parentNode, node);
XCTAssertEqual([node indexOfChildNode:firstChild], 0);
XCTAssertEqual([node indexOfChildNode:secondChild], NSNotFound);
XCTAssertEqual([node indexOfChildNode:thirdChild], 1);
[node removeChildNode:firstChild];
XCTAssertEqual(node.childNodesCount, 1);
XCTAssertNil(firstChild.parentNode);
XCTAssertNil(secondChild.parentNode);
XCTAssertEqualObjects(thirdChild.parentNode, node);
XCTAssertEqual([node indexOfChildNode:firstChild], NSNotFound);
XCTAssertEqual([node indexOfChildNode:secondChild], NSNotFound);
XCTAssertEqual([node indexOfChildNode:thirdChild], 0);
}
- (void)testRemoveChildNodeAtIndex
{
HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
[node appendNode:firstChild];
HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
[node appendNode:secondChild];
HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
[node appendNode:thirdChild];
[node removeChildNodeAtIndex:1];
XCTAssertEqual(node.childNodesCount, 2);
XCTAssertEqualObjects(firstChild.parentNode, node);
XCTAssertNil(secondChild.parentNode);
XCTAssertEqualObjects(thirdChild.parentNode, node);
XCTAssertEqual([node indexOfChildNode:firstChild], 0);
XCTAssertEqual([node indexOfChildNode:secondChild], NSNotFound);
XCTAssertEqual([node indexOfChildNode:thirdChild], 1);
[node removeChildNodeAtIndex:0];
XCTAssertEqual(node.childNodesCount, 1);
XCTAssertNil(firstChild.parentNode);
XCTAssertNil(secondChild.parentNode);
XCTAssertEqualObjects(thirdChild.parentNode, node);
XCTAssertEqual([node indexOfChildNode:firstChild], NSNotFound);
XCTAssertEqual([node indexOfChildNode:secondChild], NSNotFound);
XCTAssertEqual([node indexOfChildNode:thirdChild], 0);
}
- (void)testRemoveAllChildNodes
{
HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
[node appendNode:firstChild];
HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
[node appendNode:secondChild];
HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
[node appendNode:thirdChild];
[node removeAllChildNodes];
XCTAssertEqual(node.childNodesCount, 0);
XCTAssertNil(firstChild.parentNode);
XCTAssertNil(secondChild.parentNode);
XCTAssertNil(thirdChild.parentNode);
XCTAssertEqual([node indexOfChildNode:firstChild], NSNotFound);
XCTAssertEqual([node indexOfChildNode:secondChild], NSNotFound);
XCTAssertEqual([node indexOfChildNode:thirdChild], NSNotFound);
}
- (void)testReparentChildNodes
{
HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement];
HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement];
[node appendNode:firstChild];
HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement];
[node appendNode:secondChild];
HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement];
[node appendNode:thirdChild];
HTMLNode *newParent = [[HTMLNode alloc] initWithName:@"new-parent" type:HTMLNodeElement];
[node reparentChildNodesIntoNode:newParent];
XCTAssertEqual(node.childNodesCount, 0);
XCTAssertEqual([node indexOfChildNode:firstChild], NSNotFound);
XCTAssertEqual([node indexOfChildNode:secondChild], NSNotFound);
XCTAssertEqual([node indexOfChildNode:thirdChild], NSNotFound);
XCTAssertEqualObjects(firstChild.parentNode, newParent);
XCTAssertEqualObjects(secondChild.parentNode, newParent);
XCTAssertEqualObjects(thirdChild.parentNode, newParent);
}
- (void)testElementSetInnerHTML
{
HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"];
[element appendNode:[[HTMLElement alloc] initWithTagName:@"img"]];
[element setInnerHTML:@"<p></p><p></p>"];
XCTAssertEqual(element.childNodesCount, 2);
XCTAssertEqualObjects(element.firstChild.asElement.tagName, @"p");
XCTAssertEqualObjects([element childNodeAtIndex:1].asElement.tagName, @"p");
}
@end
@@ -38,9 +38,6 @@
[_dictionary setObject:@"3" forKey:@"C"];
expected = @[@"A", @"B", @"C"];
XCTAssertEqualObjects(_dictionary.keyEnumerator.allObjects, expected);
XCTAssertThrows([_dictionary setObject:nil forKey:@"Key"]);
XCTAssertThrows([_dictionary setObject:@"Object" forKey:nil]);
}
- (void)testIndexOfKey
@@ -51,7 +48,6 @@
XCTAssertEqual([_dictionary indexOfKey:@"A"], 0);
XCTAssertEqual([_dictionary indexOfKey:@"B"], 1);
XCTAssertEqual([_dictionary indexOfKey:@"C"], 2);
XCTAssertEqual([_dictionary indexOfKey:nil], NSNotFound);
}
- (void)testObjectAtIndex
@@ -64,8 +60,6 @@
[_dictionary setObject:@"3" forKey:@"C" atIndex:1];
XCTAssertEqualObjects([_dictionary objectAtIndex:1], @"3");
XCTAssertThrows([_dictionary setObject:nil forKey:@"Key" atIndex:0]);
XCTAssertThrows([_dictionary setObject:@"Object" forKey:nil atIndex:0]);
XCTAssertThrows([_dictionary setObject:@"Object" forKey:@"Key" atIndex:100]);
}
@@ -120,8 +114,6 @@
XCTAssertEqualObjects(_dictionary[2], @"5");
XCTAssertEqualObjects(_dictionary[@"A"], @"5");
XCTAssertThrows([_dictionary setObject:nil forKey:@"Key" atIndex:0]);
XCTAssertThrows([_dictionary setObject:@"Object" forKey:nil atIndex:0]);
XCTAssertThrows([_dictionary setObject:@"Object" forKey:@"Key" atIndex:100]);
}
@@ -158,8 +150,6 @@
XCTAssertEqualObjects(_dictionary[0], @"5");
XCTAssertEqualObjects(_dictionary[@"E"], @"5");
XCTAssertThrows([_dictionary replaceKeyValueAtIndex:1 withObject:nil andKey:@"Key"]);
XCTAssertThrows([_dictionary replaceKeyValueAtIndex:1 withObject:@"Object" andKey:nil]);
XCTAssertThrows([_dictionary replaceKeyValueAtIndex:100 withObject:@"Object" andKey:@"Key"]);
}
+4
View File
@@ -15,6 +15,8 @@
@implementation HTMLKitParserPerformance
#define HTMLKIT_NO_DOM_CHECKS
- (void)testParserPerformance
{
NSString *path = [[NSBundle bundleForClass:self.class] resourcePath];
@@ -28,4 +30,6 @@
}];
}
#undef HTMLKIT_NO_DOM_CHECKS
@end
+7 -7
View File
@@ -9,7 +9,7 @@
#import <XCTest/XCTest.h>
#import "HTML5LibTreeConstructionTest.h"
#import "HTMLNodes.h"
#import "HTMLDOM.h"
#import "HTMLParser.h"
@@ -96,8 +96,8 @@
actual = [parser parseFragmentWithContextElement:contextElement];
}
NSString *expectedNodes = [[test.nodes valueForKey:@"debugDescription"] componentsJoinedByString:@"\n"];
NSString *actualNodes = [[parser.document.childNodes.array valueForKey:@"debugDescription"] componentsJoinedByString:@"\n"];
NSString *expectedNodes = [[test.nodes valueForKey:@"treeDescription"] componentsJoinedByString:@"\n"];
NSString *actualNodes = [[actual valueForKey:@"treeDescription"] componentsJoinedByString:@"\n"];
NSString *message = [NSString stringWithFormat:@"HTML5Lib test in file: \'%@\'\nInput:\n%@\nExpected:\n%@\nActual:\n%@\n",
test.testFile,
@@ -121,14 +121,14 @@
{
XCTAssertEqualObjects(actual.name, expected.name, @"Node name mismatch [%@ should be %@]:\n%@",
actual.name, expected.name, message);
XCTAssert(actual.type == expected.type, @"Node type mismatch [%hd should be %hd]:\n%@",
actual.type, expected.type, message);
XCTAssert(actual.nodeType == expected.nodeType, @"Node type mismatch [%hd should be %hd]:\n%@",
actual.nodeType, expected.nodeType, message);
if (actual.type != expected.type) {
if (actual.nodeType != expected.nodeType) {
return;
}
switch (actual.type) {
switch (actual.nodeType) {
case HTMLNodeDocumentType:
XCTAssertEqualObjects([(HTMLDocumentType *)actual publicIdentifier], [(HTMLDocumentType *)expected publicIdentifier], @"%@", message);
XCTAssertEqualObjects([(HTMLDocumentType *)actual systemIdentifier], [(HTMLDocumentType *)expected systemIdentifier], @"%@", message);
+503
View File
@@ -0,0 +1,503 @@
//
// HTMLTreeWalkerTests.m
// HTMLKit
//
// Created by Iska on 05/06/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "HTMLTreeWalker.h"
#import "HTMLDOM.h"
@interface HTMLTreeWalkerTests : XCTestCase
@end
@implementation HTMLTreeWalkerTests
#pragma mark - Asserts
#define AssertElementWithId(input, id) \
do { \
HTMLNode *node = input;\
XCTAssertEqual(node.nodeType, HTMLNodeElement);\
XCTAssertEqualObjects(node.asElement[@"id"], id);\
} while(0)
#define AssertTextWithValue(input, value) \
do { \
HTMLNode *node = input;\
XCTAssertEqual(node.nodeType, HTMLNodeText);\
XCTAssertEqualObjects(node.textContent, value);\
} while(0)
#define AssertCommentWithValue(input, value) \
do { \
HTMLNode *node = input;\
XCTAssertEqual(node.nodeType, HTMLNodeComment);\
XCTAssertEqualObjects(node.textContent, value);\
} while(0)
#pragma mark - Tests
- (void)testTreeWalkerInit
{
HTMLNode *root = self.basicWalkingDOM;
HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root];
XCTAssertNotNil(walker);
XCTAssertNotNil(walker.root);
XCTAssertNotNil(walker.currentNode);
XCTAssertNil(walker.filter);
XCTAssertEqual(walker.whatToShow, HTMLNodeFilterShowAll);
XCTAssertEqualObjects(walker.root, root);
XCTAssertEqualObjects(walker.root, walker.currentNode);
}
#pragma mark - Basic Walking
- (HTMLNode *)basicWalkingDOM
{
// Tree structure:
// #a
// |
// +----+----+
// | |
// "b" #c
// |
// +----+----+
// | |
// #d <!--j-->
// |
// +----+----+
// | | |
// "e" #f "i"
// |
// +--+--+
// | |
// "g" <!--h-->
HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"a"}];
[div appendNode:[[HTMLText alloc] initWithData:@"b"]];
HTMLElement *c = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"c"}];
[div appendNode:c];
HTMLElement *d = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"d"}];
[c appendNode:d];
[c appendNode:[[HTMLComment alloc] initWithData:@"j"]];
[d appendNode:[[HTMLText alloc] initWithData:@"e"]];
HTMLElement *f = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"f"}];
[d appendNode:f];
[d appendNode:[[HTMLText alloc] initWithData:@"i"]];
[f appendNode:[[HTMLText alloc] initWithData:@"g"]];
[f appendNode:[[HTMLComment alloc] initWithData:@"h"]];
return div;
}
- (void)testBasicWalking
{
HTMLNode *root = self.basicWalkingDOM;
HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root];
HTMLNode *f = root.lastChild.firstChild.childNodes[1];
AssertElementWithId(walker.currentNode, @"a");
XCTAssertNil(walker.parentNode);
AssertElementWithId(walker.currentNode, @"a");
AssertTextWithValue(walker.firstChild, @"b");
AssertTextWithValue(walker.currentNode, @"b");
AssertElementWithId(walker.nextSibling, @"c");
AssertElementWithId(walker.currentNode, @"c");
AssertCommentWithValue(walker.lastChild, @"j");
AssertCommentWithValue(walker.currentNode, @"j");
AssertElementWithId(walker.previousSibling, @"d");
AssertElementWithId(walker.currentNode, @"d");
AssertTextWithValue(walker.nextNode, @"e");
AssertTextWithValue(walker.currentNode, @"e");
AssertElementWithId(walker.parentNode, @"d");
AssertElementWithId(walker.currentNode, @"d");
AssertElementWithId(walker.previousNode, @"c");
AssertElementWithId(walker.currentNode, @"c");
XCTAssertNil(walker.nextSibling);
AssertElementWithId(walker.currentNode, @"c");
walker.currentNode = f;
XCTAssertEqualObjects(walker.currentNode, f);
}
#pragma mark - Current Node
- (HTMLDocument *)currentNodeDOM
{
HTMLDocument *document = [HTMLDocument documentWithString:
@"<div id='first'><p><a></a></p></div>"
@"<div id='second'><p><b></b></p</div>"];
return document;
}
- (void)testThatTreeWalkerParentHasNoEffectCurrentNodeWhenParentIsNotUnderRoot
{
HTMLDocument *document = self.currentNodeDOM;
HTMLNode *first = document.body.firstChild;
HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:first
showOptions:HTMLNodeFilterShowElement
filter:nil];
AssertElementWithId(walker.currentNode, @"first");
XCTAssertNil(walker.parentNode);
AssertElementWithId(walker.currentNode, @"first");
}
- (void)testThatSettingCurrentNodeToNodesNotUnderRootIsHandledCorrectly
{
HTMLDocument *document = self.currentNodeDOM;
HTMLNode *first = document.body.firstChild;
HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:first
showOptions:HTMLNodeFilterShowElement|HTMLNodeFilterShowComment
filter:nil];
walker.currentNode = document.documentElement;
XCTAssertNil(walker.parentNode);
XCTAssertEqualObjects(walker.currentNode, document.documentElement);
walker.currentNode = document.documentElement;
XCTAssertEqualObjects(walker.nextNode, document.documentElement.firstChild);
XCTAssertEqualObjects(walker.currentNode, document.documentElement.firstChild);
walker.currentNode = document.documentElement;
XCTAssertNil(walker.previousNode);
XCTAssertEqualObjects(walker.currentNode, document.documentElement);
walker.currentNode = document.documentElement;
XCTAssertEqualObjects(walker.firstChild, document.documentElement.firstChild);
XCTAssertEqualObjects(walker.currentNode, document.documentElement.firstChild);
walker.currentNode = document.documentElement;
XCTAssertEqualObjects(walker.lastChild, document.documentElement.lastChild);
XCTAssertEqualObjects(walker.currentNode, document.documentElement.lastChild);
walker.currentNode = document.documentElement;
XCTAssertNil(walker.nextSibling);
XCTAssertEqualObjects(walker.currentNode, document.documentElement);
walker.currentNode = document.documentElement;
XCTAssertNil(walker.previousSibling);
XCTAssertEqualObjects(walker.currentNode, document.documentElement);
}
#pragma mark - Filter
- (HTMLElement *)filterBasicDOM
{
HTMLElement *root = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"root"}];
HTMLElement *a1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"A1"}];
HTMLElement *b1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B1"}];
HTMLElement *b2 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B2"}];
[root appendNode:a1];
[a1 appendNode:b1];
[a1 appendNode:b2];
return root;
}
- (void)testTreeWalkerNilFilter
{
HTMLElement *root = self.filterBasicDOM;
HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
showOptions:HTMLNodeFilterShowElement
filter:nil];
AssertElementWithId(walker.currentNode, @"root");
AssertElementWithId(walker.firstChild, @"A1");
AssertElementWithId(walker.currentNode, @"A1");
AssertElementWithId(walker.nextNode, @"B1");
AssertElementWithId(walker.currentNode, @"B1");
}
- (void)testTreeWalkerWithFilter
{
HTMLElement *root = self.filterBasicDOM;
id<HTMLNodeFilter> filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) {
if ([node.asElement[@"id"] isEqualToString:@"B1"]) {
return HTMLNodeFilterSkip;
}
return HTMLNodeFilterAccept;
}];
HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
showOptions:HTMLNodeFilterShowElement
filter:filter];
AssertElementWithId(walker.currentNode, @"root");
AssertElementWithId(walker.firstChild, @"A1");
AssertElementWithId(walker.currentNode, @"A1");
AssertElementWithId(walker.nextNode, @"B2");
AssertElementWithId(walker.currentNode, @"B2");
}
#pragma mark - Filter Skip
- (HTMLElement *)filterDOM
{
HTMLElement *root = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"root"}];
HTMLElement *a1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"A1"}];
HTMLElement *b1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B1"}];
HTMLElement *b2 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B2"}];
HTMLElement *b3 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B3"}];
HTMLElement *c1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"C1"}];
[root appendNode:a1];
[a1 appendNode:b1];
[a1 appendNode:b2];
[a1 appendNode:b3];
[b1 appendNode:c1];
return root;
}
- (id<HTMLNodeFilter>)skipB1Filter
{
id<HTMLNodeFilter> filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) {
if ([node.asElement[@"id"] isEqualToString:@"B1"]) {
return HTMLNodeFilterSkip;
}
return HTMLNodeFilterAccept;
}];
return filter;
}
- (id<HTMLNodeFilter>)skipB2Filter
{
id<HTMLNodeFilter> filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) {
if ([node.asElement[@"id"] isEqualToString:@"B2"]) {
return HTMLNodeFilterSkip;
}
return HTMLNodeFilterAccept;
}];
return filter;
}
- (id<HTMLNodeFilter>)rejectB1Filter
{
id<HTMLNodeFilter> filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) {
if ([node.asElement[@"id"] isEqualToString:@"B1"]) {
return HTMLNodeFilterReject;
}
return HTMLNodeFilterAccept;
}];
return filter;
}
- (id<HTMLNodeFilter>)rejectB2Filter
{
id<HTMLNodeFilter> filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) {
if ([node.asElement[@"id"] isEqualToString:@"B2"]) {
return HTMLNodeFilterReject;
}
return HTMLNodeFilterAccept;
}];
return filter;
}
static HTMLElement * (^ FindElementById)(HTMLNode *, NSString *) = ^ HTMLElement * (HTMLNode *root, NSString *id) {
for (HTMLNode *node in [root nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]) {
if ([node.asElement[@"id"] isEqualToString:id]) {
return node.asElement;
}
}
return nil;
};
- (void)testThatFilterSkipsNextNode
{
HTMLElement *root = self.filterDOM;
id<HTMLNodeFilter> filter = self.skipB1Filter;
HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
showOptions:HTMLNodeFilterShowElement
filter:filter];
AssertElementWithId(walker.nextNode, @"A1");
AssertElementWithId(walker.nextNode, @"C1");
AssertElementWithId(walker.nextNode, @"B2");
AssertElementWithId(walker.nextNode, @"B3");
}
- (void)testThatFilterSkipsFirstChild
{
HTMLElement *root = self.filterDOM;
id<HTMLNodeFilter> filter = self.skipB1Filter;
HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
showOptions:HTMLNodeFilterShowElement
filter:filter];
AssertElementWithId(walker.firstChild, @"A1");
AssertElementWithId(walker.firstChild, @"C1");
}
- (void)testThatFilterSkipsNextSibling
{
HTMLElement *root = self.filterDOM;
id<HTMLNodeFilter> filter = self.skipB2Filter;
HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
showOptions:HTMLNodeFilterShowElement
filter:filter];
AssertElementWithId(walker.firstChild, @"A1");
AssertElementWithId(walker.firstChild, @"B1");
AssertElementWithId(walker.nextSibling, @"B3");
}
- (void)testThatFilterSkipsParentNode
{
HTMLElement *root = self.filterDOM;
id<HTMLNodeFilter> filter = self.skipB1Filter;
HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
showOptions:HTMLNodeFilterShowElement
filter:filter];
walker.currentNode = FindElementById(root, @"C1");
AssertElementWithId(walker.parentNode, @"A1");
}
- (void)testThatFilterSkipsPreviousSibling
{
HTMLElement *root = self.filterDOM;
id<HTMLNodeFilter> filter = self.skipB2Filter;
HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
showOptions:HTMLNodeFilterShowElement
filter:filter];
walker.currentNode = FindElementById(root, @"B3");
AssertElementWithId(walker.previousSibling, @"B1");
}
- (void)testThatFilterSkipsPreviousNode
{
HTMLElement *root = self.filterDOM;
id<HTMLNodeFilter> filter = self.skipB1Filter;
HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
showOptions:HTMLNodeFilterShowElement
filter:filter];
walker.currentNode = FindElementById(root, @"B3");
AssertElementWithId(walker.previousNode, @"B2");
AssertElementWithId(walker.previousNode, @"C1");
AssertElementWithId(walker.previousNode, @"A1");
}
#pragma mark - Filter Reject
- (void)testThatFilterRejectsNextNode
{
HTMLElement *root = self.filterDOM;
id<HTMLNodeFilter> filter = self.rejectB1Filter;
HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
showOptions:HTMLNodeFilterShowElement
filter:filter];
AssertElementWithId(walker.nextNode, @"A1");
AssertElementWithId(walker.nextNode, @"B2");
AssertElementWithId(walker.nextNode, @"B3");
}
- (void)testThatFilterRejectsFirstChild
{
HTMLElement *root = self.filterDOM;
id<HTMLNodeFilter> filter = self.rejectB1Filter;
HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
showOptions:HTMLNodeFilterShowElement
filter:filter];
AssertElementWithId(walker.firstChild, @"A1");
AssertElementWithId(walker.firstChild, @"B2");
}
- (void)testThatFilterRejectsNextSibling
{
HTMLElement *root = self.filterDOM;
id<HTMLNodeFilter> filter = self.rejectB2Filter;
HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
showOptions:HTMLNodeFilterShowElement
filter:filter];
AssertElementWithId(walker.firstChild, @"A1");
AssertElementWithId(walker.firstChild, @"B1");
AssertElementWithId(walker.nextSibling, @"B3");
}
- (void)testThatFilterRejectsParentNode
{
HTMLElement *root = self.filterDOM;
id<HTMLNodeFilter> filter = self.rejectB1Filter;
HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
showOptions:HTMLNodeFilterShowElement
filter:filter];
walker.currentNode = FindElementById(root, @"C1");
AssertElementWithId(walker.parentNode, @"A1");
}
- (void)testThatFilterRejectsPreviousSibling
{
HTMLElement *root = self.filterDOM;
id<HTMLNodeFilter> filter = self.rejectB2Filter;
HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
showOptions:HTMLNodeFilterShowElement
filter:filter];
walker.currentNode = FindElementById(root, @"B3");
AssertElementWithId(walker.previousSibling, @"B1");
}
- (void)testThatFilterRejectsPreviousNode
{
HTMLElement *root = self.filterDOM;
id<HTMLNodeFilter> filter = self.rejectB1Filter;
HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root
showOptions:HTMLNodeFilterShowElement
filter:filter];
walker.currentNode = FindElementById(root, @"B3");
AssertElementWithId(walker.previousNode, @"B2");
AssertElementWithId(walker.previousNode, @"A1");
}
@end
+13
View File
@@ -0,0 +1,13 @@
<script type="application/json" id="selectors">
[{
"selector": "li,p",
"match": ["1", "2", "3"]
}]
</script>
<div id="testDOM">
<ul>
<li id="1">The background of this list item should be green</li>
<li id="2">The background of this second list item should be also green</li>
</ul>
<p id="3">The background of this paragraph should be green.</p>
</div>
+9
View File
@@ -0,0 +1,9 @@
<script type="application/json" id="selectors">
[{
"selector": "p[title$=\"bar\"]",
"match": ["1"]
}]
</script>
<div id="testDOM">
<p id="1" title="foobar">This paragraph should have a green background because its title attribute ends with &quot;bar&quot;</p>
</div>
+9
View File
@@ -0,0 +1,9 @@
<script type="application/json" id="selectors">
[{
"selector": "p[title*=\"bar\"]",
"match": ["1"]
}]
</script>
<div id="testDOM">
<p id="1" title="foobarufoo">This paragraph should have a green background because its title attribute contains &quot;bar&quot;</p>
</div>
+23
View File
@@ -0,0 +1,23 @@
<script type="application/json" id="selectors">
[{
"selector": ".t1",
"match": ["1"]
},
{
"selector": "li.t2",
"match": ["2", "3"]
},
{
"selector": ".t3",
"match": []
}]
</script>
<div id="testDOM">
<ul>
<li id="1" class="t1">This list item should have green background because its class is &quot;t1&quot;</li>
<li id="2" class="t2">This list item should have green background because its class is &quot;t2&quot;</li>
<li id="3" class="t2">
<span id="4" class="t33">This list item should have green background because the inner SPAN does not match SPAN.t3</span>
</li>
</ul>
</div>
+30
View File
@@ -0,0 +1,30 @@
<script type="application/json" id="selectors">
[{
"selector": "p.t1",
"match": ["1"]
},
{
"selector": "p.t2",
"match": ["1"]
},
{
"selector": "div.teST",
"match": []
},
{
"selector": "div.te",
"match": []
},
{
"selector": "div.st",
"match": []
},
{
"selector": "div.te.st",
"match": []
}]
</script>
<div id="testDOM">
<p id="1" class="t1 t2">This paragraph should have a green background and a green thick solid border because it carries both classes t1 and t2.</p>
<div id="2" class="test">This line should be green.</div>
</div>
+11
View File
@@ -0,0 +1,11 @@
<script type="application/json" id="selectors">
[{
"selector": "div :not(:enabled):not(:disabled)",
"match": ["1", "2"]
}]
</script>
<div id="testDOM">
<div id="1">
<p id="2">This paragraph should have a green background.</p>
</div>
</div>
+9
View File
@@ -0,0 +1,9 @@
<script type="application/json" id="selectors">
[{
"selector": "p:empty",
"match": []
}]
</script>
<div id="testDOM">
<p id="1">This line should have a green background.</p>
</div>
+10
View File
@@ -0,0 +1,10 @@
<script type="application/json" id="selectors">
[{
"selector": "address:empty",
"match": ["1"]
}]
</script>
<div id="testDOM">
<address id="1"></address>
<div id="2" class="text">This line should have a green background.</div>
</div>
+22
View File
@@ -0,0 +1,22 @@
<script type="application/json" id="selectors">
[{
"selector": ".t1.fail",
"match": []
},
{
"selector": ".fail.t2",
"match": []
},
{
"selector": ".t2.fail",
"match": []
},
{
"selector": ".fail.t2",
"match": []
}]
</script>
<div id="testDOM">
<p id="1" class="t1">This line should be green.</p>
<p id="2" class="t1 t2">This line should be green.</p>
</div>
+19
View File
@@ -0,0 +1,19 @@
<script type="application/json" id="selectors">
[{
"selector": "p.t1.t2",
"match": ["1"]
},
{
"selector": "div.t1",
"match": []
},
{
"selector": "address.t5.t5",
"match": ["3"]
}]
</script>
<div id="testDOM">
<p id="1" class="t1 t2">This line should be green.</p>
<div id="2" class="t3">This line should be green.</div>
<address id="3" class="t4 t5 t6">This line should be green.</address>
</div>
+21
View File
@@ -0,0 +1,21 @@
<script type="application/json" id="selectors">
[{
"selector": ".t1:not(.t2)",
"match": []
},
{
"selector": ":not(.t2).t1",
"match": []
},
{
"selector": ".t2:not(.t1)",
"match": []
},
{
"selector": ":not(.t1).t2",
"match": []
}]
</script>
<div id="testDOM">
<p id="1" class="t1 t2">This line should be green.</p>
</div>
+19
View File
@@ -0,0 +1,19 @@
<script type="application/json" id="selectors">
[{
"selector": "p:not(.t1):not(.t2)",
"match": []
},
{
"selector": "div:not(.t1)",
"match": ["testDOM", "2"]
},
{
"selector": "address:not(.t5):not(.t5)",
"match": []
}]
</script>
<div id="testDOM">
<p id="1" class="t1 t2">This line should be green.</p>
<div id="2" class="t3">This line should be green.</div>
<address id="3" class="t4 t5 t6">This line should be green.</address>
</div>
+27
View File
@@ -0,0 +1,27 @@
<script type="application/json" id="selectors">
[{
"selector": "#t1",
"match": ["t1"]
},
{
"selector": "li#t2",
"match": ["t2"]
},
{
"selector": "li#t3",
"match": ["t3"]
},
{
"selector": "#t4",
"match": []
}]
</script>
<div id="testDOM">
<ul id="1">
<li id="t1">This list item should have a green background. because its ID is &quot;t1&quot;</li>
<li id="t2">This list item should have a green background. because its ID is &quot;t2&quot;</li>
<li id="t3">
<span id="t44">This list item should have a green background. because the inner SPAN does not match &quot;#t4&quot;</span>
</li>
</ul>
</div>
+11
View File
@@ -0,0 +1,11 @@
<script type="application/json" id="selectors">
[{
"selector": "address:empty",
"match": ["1"]
}]
</script>
<div id="testDOM">
<address id="1"><!-- --><?test random PI></address>
<div id="2" class="text">This line should have a green background.</div>
<p id="3">(Note: This test is based on unpublished errata.)</p>
</div>
+10
View File
@@ -0,0 +1,10 @@
<script type="application/json" id="selectors">
[{
"selector": "address:empty",
"match": []
}]
</script>
<div id="testDOM">
<address id="1"> </address>
<div id="2" class="text">This line should have a green background.</div>
</div>
+10
View File
@@ -0,0 +1,10 @@
<script type="application/json" id="selectors">
[{
"selector": "address:empty",
"match": []
}]
</script>
<div id="testDOM">
<address id="1"><span id="2"></span></address>
<div id="3" class="text">This line should have a green background.</div>
</div>
+10
View File
@@ -0,0 +1,10 @@
<script type="application/json" id="selectors">
[{
"selector": "p,",
"match": [],
"error": 1
}]
</script>
<div id="testDOM">
<p id="1">This line should have a green background.</p>
</div>
+10
View File
@@ -0,0 +1,10 @@
<script type="application/json" id="selectors">
[{
"selector": ".5cm",
"match": [],
"error": 1
}]
</script>
<div id="testDOM">
<p id="1" class="5cm">This line should have a green background.</p>
</div>
+9
View File
@@ -0,0 +1,9 @@
<script type="application/json" id="selectors">
[{
"selector": ".\\5cm",
"match": []
}]
</script>
<div id="testDOM">
<p class="5cm">This line should have a green background.</p>
</div>

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