├── . codecov.yml ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── .jazzy.yaml ├── CHANGELOG.md ├── Example └── HTMLKitExample │ ├── HTMLKitExample.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── HTMLKitExample.xcscheme │ └── HTMLKitExample │ └── main.swift ├── HTMLKit.playground ├── Pages │ ├── CSS Selectors.xcplaygroundpage │ │ └── Contents.swift │ ├── Intro.xcplaygroundpage │ │ ├── Contents.swift │ │ ├── Resources │ │ │ └── HTMLKit.png │ │ └── timeline.xctimeline │ ├── Parsing Documents.xcplaygroundpage │ │ ├── Contents.swift │ │ └── timeline.xctimeline │ ├── Parsing Fragments.xcplaygroundpage │ │ ├── Contents.swift │ │ └── timeline.xctimeline │ └── The DOM.xcplaygroundpage │ │ ├── Contents.swift │ │ └── timeline.xctimeline ├── contents.xcplayground └── playground.xcworkspace │ └── contents.xcworkspacedata ├── HTMLKit.png ├── HTMLKit.podspec ├── HTMLKit.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ ├── xcbaselines │ └── 625A14C219C7829400AD0C32.xcbaseline │ │ ├── 5AB53A08-CDD0-43FB-B47F-2EE38B3FE707.plist │ │ └── Info.plist │ └── xcschemes │ ├── HTMLKit-iOS.xcscheme │ ├── HTMLKit-macOS.xcscheme │ ├── HTMLKit-tvOS.xcscheme │ └── HTMLKit-watchOS.xcscheme ├── HTMLKit.xcworkspace └── contents.xcworkspacedata ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── CSSAttributeSelector.m ├── CSSCombinatorSelector.m ├── CSSCompoundSelector.m ├── CSSInputStream.m ├── CSSNthExpressionParser.m ├── CSSNthExpressionSelector.m ├── CSSPseudoClassSelector.m ├── CSSPseudoFunctionSelector.m ├── CSSSelector.m ├── CSSSelectorBlock.m ├── CSSSelectorParser.m ├── CSSSelectors.m ├── CSSStructuralPseudoSelectors.m ├── CSSTypeSelector.m ├── HTMLCharacterData.m ├── HTMLCharacterToken.m ├── HTMLComment.m ├── HTMLCommentToken.m ├── HTMLDOCTYPEToken.m ├── HTMLDOMTokenList.m ├── HTMLDOMUtils.m ├── HTMLDocument.m ├── HTMLDocumentFragment.m ├── HTMLDocumentType.m ├── HTMLEOFToken.m ├── HTMLElement.m ├── HTMLInputStreamReader.m ├── HTMLKit-Info.plist ├── HTMLKitDOMExceptions.m ├── HTMLListOfActiveFormattingElements.m ├── HTMLMarker.m ├── HTMLNode.m ├── HTMLNodeFilter.m ├── HTMLNodeIterator.m ├── HTMLNodeTraversal.m ├── HTMLNodeVisitor.m ├── HTMLOrderedDictionary.m ├── HTMLParseErrorToken.m ├── HTMLParser.m ├── HTMLQuircksMode.m ├── HTMLRange.m ├── HTMLSerializer.m ├── HTMLStackOfOpenElements.m ├── HTMLTagToken.m ├── HTMLTemplate.m ├── HTMLText.m ├── HTMLToken.m ├── HTMLTokenizer.m ├── HTMLTokenizerEntities.m ├── HTMLTreeVisitor.m ├── HTMLTreeWalker.m ├── NSCharacterSet+HTMLKit.m ├── NSString+HTMLKit.m ├── NSString+Private.m └── include │ ├── CSSAttributeSelector.h │ ├── CSSCodePoints.h │ ├── CSSCombinatorSelector.h │ ├── CSSCompoundSelector.h │ ├── CSSInputStream.h │ ├── CSSNthExpressionParser.h │ ├── CSSNthExpressionSelector.h │ ├── CSSPseudoClassSelector.h │ ├── CSSPseudoFunctionSelector.h │ ├── CSSSelector.h │ ├── CSSSelectorBlock.h │ ├── CSSSelectorParser.h │ ├── CSSSelectors.h │ ├── CSSStructuralPseudoSelectors.h │ ├── CSSTypeSelector.h │ ├── HTMLCharacterData+Private.h │ ├── HTMLCharacterData.h │ ├── HTMLCharacterToken.h │ ├── HTMLComment.h │ ├── HTMLCommentToken.h │ ├── HTMLDOCTYPEToken.h │ ├── HTMLDOM.h │ ├── HTMLDOMTokenList.h │ ├── HTMLDOMUtils.h │ ├── HTMLDocument+Private.h │ ├── HTMLDocument.h │ ├── HTMLDocumentFragment.h │ ├── HTMLDocumentType.h │ ├── HTMLEOFToken.h │ ├── HTMLElement.h │ ├── HTMLElementAdjustment.h │ ├── HTMLElementTypes.h │ ├── HTMLInputStreamReader.h │ ├── HTMLKit.h │ ├── HTMLKitDOMExceptions.h │ ├── HTMLKitErrorDomain.h │ ├── HTMLListOfActiveFormattingElements.h │ ├── HTMLMarker.h │ ├── HTMLNamespaces.h │ ├── HTMLNode+Private.h │ ├── HTMLNode.h │ ├── HTMLNodeFilter.h │ ├── HTMLNodeIterator+Private.h │ ├── HTMLNodeIterator.h │ ├── HTMLNodeTraversal.h │ ├── HTMLNodeVisitor.h │ ├── HTMLOrderedDictionary.h │ ├── HTMLParseErrorToken.h │ ├── HTMLParser+Private.h │ ├── HTMLParser.h │ ├── HTMLParserInsertionModes.h │ ├── HTMLQuirksMode.h │ ├── HTMLRange+Private.h │ ├── HTMLRange.h │ ├── HTMLSerializer.h │ ├── HTMLStackOfOpenElements.h │ ├── HTMLTagToken.h │ ├── HTMLTemplate.h │ ├── HTMLText.h │ ├── HTMLToken.h │ ├── HTMLTokenizer.h │ ├── HTMLTokenizerCharacters.h │ ├── HTMLTokenizerEntities.h │ ├── HTMLTokenizerStates.h │ ├── HTMLTokens.h │ ├── HTMLTreeVisitor.h │ ├── HTMLTreeWalker.h │ ├── NSCharacterSet+HTMLKit.h │ ├── NSString+HTMLKit.h │ ├── NSString+Private.h │ └── module.modulemap └── Tests ├── Fixtures ├── HTML Standard.html └── bug33.html ├── HTMLKitTests ├── CSSAttributeSelectorTests.m ├── CSSCombinatorSelectorTests.m ├── CSSExtensionSelectorsParsingTests.m ├── CSSNThExpressionSelectorTests.m ├── CSSNthExpressionsParserTests.m ├── CSSSelectorParserTests.m ├── CSSSelectorTest.h ├── CSSSelectorTest.m ├── CSSStructuralPseudoSelectors.m ├── CSSTypeSelectorTests.m ├── HTML5LibTokenizerTest.h ├── HTML5LibTokenizerTest.m ├── HTML5LibTreeConstructionTest.h ├── HTML5LibTreeConstructionTest.m ├── HTMLCharacterDataTests.m ├── HTMLDOMTokenListTests.m ├── HTMLKitParserIssuesTests.m ├── HTMLKitParserPerformance.m ├── HTMLKitTestObserver.h ├── HTMLKitTestObserver.m ├── HTMLKitTestUtil.h ├── HTMLKitTestUtil.m ├── HTMLKitTests-Info.plist ├── HTMLKitTokenizerPerformance.m ├── HTMLKitTokenizerTests.m ├── HTMLKitTreeConstructionTests.m ├── HTMLMutationAlgorithmsTests.m ├── HTMLNodeIteratorTests.m ├── HTMLNodesTests.m ├── HTMLOrderedDictionaryTests.m ├── HTMLRangeTests.m ├── HTMLSerializationTests.m ├── HTMLStringCategoryTests.m ├── HTMLTreeVisitorTests.m └── HTMLTreeWalkerTests.m └── css-tests ├── 1.html ├── 10.html ├── 11.html ├── 13.html ├── 14.html ├── 144.html ├── 148.html ├── 149.html ├── 14b.html ├── 14c.html ├── 14d.html ├── 14e.html ├── 15.html ├── 150.html ├── 151.html ├── 152.html ├── 154.html ├── 155.html ├── 155a.html ├── 155b.html ├── 155c.html ├── 155d.html ├── 156.html ├── 157.html ├── 158.html ├── 15b.html ├── 16.html ├── 160.html ├── 170.html ├── 170a.html ├── 170b.html ├── 170c.html ├── 170d.html ├── 175a.html ├── 175b.html ├── 175c.html ├── 176.html ├── 177b.html ├── 181.html ├── 183.html ├── 184a.html ├── 184b.html ├── 184c.html ├── 184d.html ├── 184e.html ├── 184f.html ├── 2.html ├── 23.html ├── 24.html ├── 25.html ├── 27a.html ├── 27b.html ├── 28.html ├── 29.html ├── 3.html ├── 30.html ├── 31.html ├── 32.html ├── 33.html ├── 34.html ├── 35.html ├── 36.html ├── 37.html ├── 4.html ├── 43.html ├── 44.html ├── 44c.html ├── 44d.html ├── 45.html ├── 45c.html ├── 46.html ├── 5.html ├── 54.html ├── 55.html ├── 56.html ├── 59.html ├── 6.html ├── 60.html ├── 68.html ├── 69.html ├── 7.html ├── 70.html ├── 72.html ├── 72b.html ├── 73.html ├── 74.html ├── 75.html ├── 76.html ├── 77.html ├── 78.html ├── 79.html ├── 7b.html ├── 8.html ├── 80.html ├── 81.html ├── 82.html ├── 83.html ├── 86.html ├── 87.html ├── 88.html ├── 89.html ├── 9.html └── 90.html /. codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | branch: develop 3 | 4 | ignore: 5 | - "Tests/" 6 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: HTMLKit CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | macOS: 7 | name: Test macOS 8 | runs-on: macOS-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | with: 12 | submodules: recursive 13 | - name: macOS 14 | run: xcodebuild -workspace "HTMLKit.xcworkspace" -scheme "HTMLKit-macOS" -destination "platform=macOS" -enableCodeCoverage YES clean test | xcpretty 15 | - name: code coverage 16 | uses: codecov/codecov-action@v1 17 | iOS: 18 | name: Test iOS 19 | runs-on: macOS-latest 20 | env: 21 | DEVELOPER_DIR: /Applications/Xcode_11.5.app/Contents/Developer 22 | strategy: 23 | matrix: 24 | destination: ["OS=13.5,name=iPhone 11 Pro", "OS=12.4,name=iPhone XS", "OS=11.4,name=iPhone X"] 25 | steps: 26 | - uses: actions/checkout@v2 27 | with: 28 | submodules: recursive 29 | - name: iOS - ${{ matrix.destination }} 30 | run: xcodebuild -workspace "HTMLKit.xcworkspace" -scheme "HTMLKit-iOS" -destination "${{ matrix.destination }}" clean test | xcpretty 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | .build/ 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | Pods/ 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | Carthage/Checkouts 43 | Carthage/Build 44 | 45 | # Jazzy 46 | docs/ 47 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "html5lib-tests"] 2 | path = Tests/html5lib-tests 3 | url = https://github.com/html5lib/html5lib-tests.git 4 | -------------------------------------------------------------------------------- /Example/HTMLKitExample/HTMLKitExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /HTMLKit.playground/Pages/Intro.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | # HTMLKit 3 | 4 | ![Logo](HTMLKit.png) 5 | 6 | **** 7 | 8 | An Objective-C kit for your everyday HTML needs. 9 | 10 | **** 11 | 12 | HTMLKit is a [WHATWG](https://html.spec.whatwg.org/multipage/) specification-compliant framework for parsing and serializing HTML documents and document fragments for iOS and OSX. HTMLKit parses real-world HTML the same way modern web browsers would. 13 | 14 | **** 15 | 16 | HTMLKit is available under the MIT License 17 | 18 | **** 19 | 20 | # Table of Contents 21 | 22 | - [Parsing Document](Parsing%20Documents) 23 | - [Parsing Fragments](Parsing%20Fragments) 24 | - [The DOM](The%20DOM) 25 | - [CSS Selectors](CSS%20Selectors) 26 | 27 | **** 28 | 29 | [Next](@next) 30 | */ 31 | -------------------------------------------------------------------------------- /HTMLKit.playground/Pages/Intro.xcplaygroundpage/Resources/HTMLKit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iabudiab/HTMLKit/118cb4197164cc6987f41fae7e1eb21eb96b5f20/HTMLKit.playground/Pages/Intro.xcplaygroundpage/Resources/HTMLKit.png -------------------------------------------------------------------------------- /HTMLKit.playground/Pages/Intro.xcplaygroundpage/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /HTMLKit.playground/Pages/Parsing Documents.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | /*: 3 | # Parsing HTML Documents 4 | */ 5 | 6 | import HTMLKit 7 | 8 | /*: 9 | Given some HTML content 10 | */ 11 | 12 | let htmlString = "

HTMLKit

Hello there!

This is a demo of HTMLKit

" 13 | htmlString 14 | 15 | /*: 16 | You can parse it using the HTMLParser: 17 | */ 18 | 19 | let parser = HTMLParser(string: htmlString) 20 | let documentViaParser = parser.parseDocument() 21 | documentViaParser.innerHTML 22 | 23 | /*: 24 | You can also create a document from a given HTML string directly: 25 | */ 26 | 27 | let document = HTMLDocument(string: htmlString) 28 | document.innerHTML 29 | 30 | //: [Next](@next) 31 | -------------------------------------------------------------------------------- /HTMLKit.playground/Pages/Parsing Documents.xcplaygroundpage/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 15 | 16 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /HTMLKit.playground/Pages/Parsing Fragments.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | /*: 3 | # Parsing Document Fragments 4 | */ 5 | 6 | import HTMLKit 7 | 8 | /*: 9 | Given some HTML content 10 | */ 11 | 12 | let htmlString = "

Hello HTMLKit

some table data" 13 | 14 | /*: 15 | You can prase it as a document fragment in a specified context element: 16 | */ 17 | 18 | let parser = HTMLParser(string: htmlString) 19 | 20 | let tableContext = HTMLElement(tagName: "table") 21 | var elements = parser.parseFragment(withContextElement: tableContext) 22 | 23 | for element in elements { 24 | print(element.outerHTML) 25 | } 26 | 27 | /*: 28 | The same parser instance can be reusued: 29 | */ 30 | 31 | let bodyContext = HTMLElement(tagName: "body") 32 | elements = parser.parseFragment(withContextElement: bodyContext) 33 | 34 | for element in elements { 35 | print(element.outerHTML) 36 | } 37 | 38 | //: [Next](@next) 39 | -------------------------------------------------------------------------------- /HTMLKit.playground/Pages/The DOM.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | /*: 3 | # The DOM 4 | 5 | HTMLKit provides a rich DOM implementation for manipulating and navigating the document tree. Here are some of the features: 6 | */ 7 | 8 | import HTMLKit 9 | 10 | let htmlString = "

HTMLKit

Hello there!

" 11 | let document = HTMLDocument(string: htmlString) 12 | 13 | /*: 14 | Create new elements and assign attributes 15 | */ 16 | 17 | let description = HTMLElement(tagName:"meta", attributes: ["name": "description"]) 18 | description["content"] = "HTMLKit for iOS & OSX" 19 | 20 | /*: 21 | Append nodes to the document 22 | */ 23 | let head = document.head! 24 | head.append(description) 25 | document.innerHTML 26 | 27 | let body = document.body! 28 | let nodes = [ 29 | HTMLElement(tagName: "div", attributes: ["class": "red"]), 30 | HTMLElement(tagName: "div", attributes: ["class": "green"]), 31 | HTMLElement(tagName: "div", attributes: ["class": "blue"]) 32 | ] 33 | body.append(nodes) 34 | body.innerHTML 35 | 36 | /*: 37 | Enumerate child elements and perform DOM manipulation 38 | */ 39 | body.enumerateChildElements { (element, index, stop) -> Void in 40 | if element.tagName == "div" { 41 | let lorem = HTMLElement(tagName: "p") 42 | lorem.textContent = "Lorem ipsum: \(index)" 43 | element.append(lorem) 44 | } 45 | } 46 | body.innerHTML 47 | 48 | /*: 49 | Remove nodes from the document 50 | */ 51 | body.removeChildNode(at: 1) 52 | body.innerHTML 53 | 54 | /*: 55 | Navigate to child and sibling nodes 56 | */ 57 | body.lastChild!.removeFromParentNode() 58 | let greenDiv = body.firstChild!.nextSibling! 59 | greenDiv.outerHTML 60 | 61 | /*: 62 | Manipulate the HTML directly 63 | */ 64 | greenDiv.innerHTML = "