Browse Source

removed soup

tags/v1.5.0
boB Rudis 5 years ago
parent
commit
3d70aea123
No known key found for this signature in database GPG Key ID: 1D7529BE14E2BBA9
  1. 8
      Podfile
  2. 14
      Podfile.lock
  3. 9
      Pods/Just/LICENSE.md
  4. 156
      Pods/Just/README.md
  5. 1130
      Pods/Just/Sources/Just/Just.swift
  6. 14
      Pods/Manifest.lock
  7. 601
      Pods/Pods.xcodeproj/project.pbxproj
  8. 6
      Pods/Pods.xcodeproj/xcuserdata/hrbrmstr.xcuserdatad/xcschemes/Just.xcscheme
  9. 4
      Pods/Pods.xcodeproj/xcuserdata/hrbrmstr.xcuserdatad/xcschemes/xcschememanagement.plist
  10. 21
      Pods/SwiftSoup/LICENSE
  11. 595
      Pods/SwiftSoup/README.md
  12. 21
      Pods/SwiftSoup/Sources/ArrayExt.swift
  13. 148
      Pods/SwiftSoup/Sources/Attribute.swift
  14. 264
      Pods/SwiftSoup/Sources/Attributes.swift
  15. 26
      Pods/SwiftSoup/Sources/BooleanAttribute.swift
  16. 81
      Pods/SwiftSoup/Sources/CharacterExt.swift
  17. 463
      Pods/SwiftSoup/Sources/CharacterReader.swift
  18. 150
      Pods/SwiftSoup/Sources/Cleaner.swift
  19. 59
      Pods/SwiftSoup/Sources/Collector.swift
  20. 127
      Pods/SwiftSoup/Sources/CombiningEvaluator.swift
  21. 66
      Pods/SwiftSoup/Sources/Comment.swift
  22. 10
      Pods/SwiftSoup/Sources/Connection.swift
  23. 85
      Pods/SwiftSoup/Sources/DataNode.swift
  24. 24
      Pods/SwiftSoup/Sources/DataUtil.swift
  25. 562
      Pods/SwiftSoup/Sources/Document.swift
  26. 129
      Pods/SwiftSoup/Sources/DocumentType.swift
  27. 1295
      Pods/SwiftSoup/Sources/Element.swift
  28. 602
      Pods/SwiftSoup/Sources/Elements.swift
  29. 382
      Pods/SwiftSoup/Sources/Entities.swift
  30. 720
      Pods/SwiftSoup/Sources/Evaluator.swift
  31. 22
      Pods/SwiftSoup/Sources/Exception.swift
  32. 125
      Pods/SwiftSoup/Sources/FormElement.swift
  33. 771
      Pods/SwiftSoup/Sources/HtmlTreeBuilder.swift
  34. 1549
      Pods/SwiftSoup/Sources/HtmlTreeBuilderState.swift
  35. 10
      Pods/SwiftSoup/Sources/HttpStatusException.swift
  36. 808
      Pods/SwiftSoup/Sources/Node.swift
  37. 50
      Pods/SwiftSoup/Sources/NodeTraversor.swift
  38. 37
      Pods/SwiftSoup/Sources/NodeVisitor.swift
  39. 429
      Pods/SwiftSoup/Sources/OrderedDictionary.swift
  40. 453
      Pods/SwiftSoup/Sources/OrderedSet.swift
  41. 42
      Pods/SwiftSoup/Sources/ParseError.swift
  42. 52
      Pods/SwiftSoup/Sources/ParseErrorList.swift
  43. 59
      Pods/SwiftSoup/Sources/ParseSettings.swift
  44. 201
      Pods/SwiftSoup/Sources/Parser.swift
  45. 84
      Pods/SwiftSoup/Sources/Pattern.swift
  46. 322
      Pods/SwiftSoup/Sources/QueryParser.swift
  47. 163
      Pods/SwiftSoup/Sources/Selector.swift
  48. 10
      Pods/SwiftSoup/Sources/SerializationException.swift
  49. 39
      Pods/SwiftSoup/Sources/SimpleDictionary.swift
  50. 89
      Pods/SwiftSoup/Sources/StreamReader.swift
  51. 206
      Pods/SwiftSoup/Sources/String.swift
  52. 165
      Pods/SwiftSoup/Sources/StringBuilder.swift
  53. 262
      Pods/SwiftSoup/Sources/StringUtil.swift
  54. 176
      Pods/SwiftSoup/Sources/StructuralEvaluator.swift
  55. 242
      Pods/SwiftSoup/Sources/SwiftSoup.swift
  56. 347
      Pods/SwiftSoup/Sources/Tag.swift
  57. 199
      Pods/SwiftSoup/Sources/TextNode.swift
  58. 396
      Pods/SwiftSoup/Sources/Token.swift
  59. 428
      Pods/SwiftSoup/Sources/TokenQueue.swift
  60. 311
      Pods/SwiftSoup/Sources/Tokeniser.swift
  61. 1644
      Pods/SwiftSoup/Sources/TokeniserState.swift
  62. 98
      Pods/SwiftSoup/Sources/TreeBuilder.swift
  63. 67
      Pods/SwiftSoup/Sources/UnicodeScalar.swift
  64. 133
      Pods/SwiftSoup/Sources/Validate.swift
  65. 650
      Pods/SwiftSoup/Sources/Whitelist.swift
  66. 77
      Pods/SwiftSoup/Sources/XmlDeclaration.swift
  67. 146
      Pods/SwiftSoup/Sources/XmlTreeBuilder.swift
  68. 2
      Pods/Target Support Files/Just/Just-Info.plist
  69. 5
      Pods/Target Support Files/Just/Just-dummy.m
  70. 0
      Pods/Target Support Files/Just/Just-prefix.pch
  71. 4
      Pods/Target Support Files/Just/Just-umbrella.h
  72. 6
      Pods/Target Support Files/Just/Just.modulemap
  73. 5
      Pods/Target Support Files/Just/Just.xcconfig
  74. 24
      Pods/Target Support Files/Pods-RSwitch/Pods-RSwitch-acknowledgements.markdown
  75. 24
      Pods/Target Support Files/Pods-RSwitch/Pods-RSwitch-acknowledgements.plist
  76. 2
      Pods/Target Support Files/Pods-RSwitch/Pods-RSwitch-frameworks-Debug-input-files.xcfilelist
  77. 2
      Pods/Target Support Files/Pods-RSwitch/Pods-RSwitch-frameworks-Debug-output-files.xcfilelist
  78. 2
      Pods/Target Support Files/Pods-RSwitch/Pods-RSwitch-frameworks-Release-input-files.xcfilelist
  79. 2
      Pods/Target Support Files/Pods-RSwitch/Pods-RSwitch-frameworks-Release-output-files.xcfilelist
  80. 4
      Pods/Target Support Files/Pods-RSwitch/Pods-RSwitch-frameworks.sh
  81. 7
      Pods/Target Support Files/Pods-RSwitch/Pods-RSwitch.debug.xcconfig
  82. 7
      Pods/Target Support Files/Pods-RSwitch/Pods-RSwitch.release.xcconfig
  83. 5
      Pods/Target Support Files/SwiftSoup/SwiftSoup-dummy.m
  84. 6
      Pods/Target Support Files/SwiftSoup/SwiftSoup.modulemap
  85. 4
      RSwitch.xcodeproj/project.pbxproj
  86. 9
      RSwitch/Swift/AppDelegate.swift
  87. 95
      RSwitch/Swift/Downloaders/DownloadRStudio.swift
  88. 9
      RSwitch/Swift/Utils/Preferences.swift
  89. 29
      RSwitch/Swift/Utils/RStudioUtils.swift

8
Podfile

@ -1,11 +1,11 @@
source 'https://cdn.cocoapods.org/'
platform :osx, '10.14'
source 'https://cdn.cocoapods.org/'
use_frameworks!
target 'RSwitch' do
use_frameworks!
pod 'SwiftSoup'
pod 'Just'
end

14
Podfile.lock

@ -1,16 +1,16 @@
PODS:
- SwiftSoup (2.2.0)
- Just (0.8.0)
DEPENDENCIES:
- SwiftSoup
- Just
SPEC REPOS:
https://github.com/cocoapods/specs.git:
- SwiftSoup
trunk:
- Just
SPEC CHECKSUMS:
SwiftSoup: 230991361c5ba249d140161860a3864a28b63860
Just: aef5105fb2c7069b65aa6f21aec8f429057f9c19
PODFILE CHECKSUM: d5db68a23a6fb36f3d94717563c9d5199d9f3bdf
PODFILE CHECKSUM: 0ed9277c30181dde6c0bb4a7b18f37dca46c8ec9
COCOAPODS: 1.7.5
COCOAPODS: 1.8.0.beta.2

9
Pods/Just/LICENSE.md

@ -0,0 +1,9 @@
The MIT License (MIT)
Copyright (c) 2015 Just contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

156
Pods/Just/README.md

@ -0,0 +1,156 @@
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![CocoaPods](https://img.shields.io/cocoapods/v/Just.svg)](https://cocoapods.org/pods/Just)
[![Build Status](https://travis-ci.org/JustHTTP/Just.svg?branch=master)](https://travis-ci.org/JustHTTP/Just)
![Supported Flatform](https://img.shields.io/cocoapods/p/Just.svg)
<img src="https://raw.githubusercontent.com/JustHTTP/Just/master/Docs/IconMasked.png" width="280" height="280">
Just is a client-side HTTP library inspired by [python-requests][] - HTTP for Humans.
Just requires Swift 5. For Swift 4.2 support, use version [0.7.1][].
[0.7.1]: https://github.com/JustHTTP/Just/releases/tag/0.7.1
[python-requests]: http://python-requests.org "python-requests"
[twitter]: https://twitter.com/JustHTTP
# Features
Just lets you to the following effortlessly:
- URL queries
- custom headers
- form (`x-www-form-encoded`) / JSON HTTP body
- redirect control
- multipart file upload along with form values.
- basic/digest authentication
- cookies
- timeouts
- synchronous / asynchronous requests
- upload / download progress tracking for asynchronous requests
- link headers
- friendly accessible results
# Use
The simplest request with Just looks like this:
```swift
// A simple get request
Just.get("http://httpbin.org/get")
```
The next example shows how to upload a file along with some data:
```swift
// talk to registration end point
let r = Just.post(
"http://justiceleauge.org/member/register",
data: ["username": "barryallen", "password":"ReverseF1ashSucks"],
files: ["profile_photo": .url(fileURLWithPath:"flash.jpeg", nil)]
)
if r.ok { /* success! */ }
```
Here's the same example done asynchronously:
```swift
// talk to registration end point
Just.post(
"http://justiceleauge.org/member/register",
data: ["username": "barryallen", "password":"ReverseF1ashSucks"],
files: ["profile_photo": .url(fileURLWithPath:"flash.jpeg", nil)]
) { r in
if r.ok { /* success! */ }
}
```
Read *Getting Started* [on the web][starting link] or
[in this playground][starting playground] to learn more!
[starting playground]: https://github.com/JustHTTP/Just/blob/master/Docs/QuickStart.zip?raw=true
[starting link]: https://JustHTTP.github.io
# Install
Here are some ways to leverage Just.
## Xcode
Add `https://github.com/JustHTTP/Just.git` the usual way.
## Swift Package Manager
Add the following to your `dependencies`:
```swift
.package(url: "https://github.com/JustHTTP/Just.git", from: "0.8.0")
```
… and `"Just"` to your target dependencies.
## Carthage
Include the following in your Cartfile:
github "JustHTTP/Just"
Just includes dynamic framework targets for both iOS and OS X.
## CocoaPods
The usual way:
platform :ios, '8.0'
use_frameworks!
target 'MyApp' do
pod 'Just'
end
## Manual
Drop `Just.xcodeproj` into your project navigator. Under the *General* tab of
your project settings, use the plus sign to add `Just.framework` to
*Linked Framework and Libraries*. Make sure to include the correct version
for your target's platform.
It's also common to add Just as a git submodule to your projects repository:
cd path/to/your/project
git submodule add https://github.org/JustHTTP/Just.git
## Source File
Put `Just.swift` directly into your project. Alternately, put it in the
*Sources* folder of a playground. (The latter makes a fun way to explore the
web.)
[Carthage]: https://github.com/Carthage/Carthage "Carthage"
# Contribute
Pull requests are welcome. Here are some tips for code contributors:
Work in `Just.xcworkspace`.
The tests for link headers relies on Github APIs, which has a low per-hour
limit. To overcome this, you can edit the Xcode build schemes and add
[environment variables][XcodeEnvVar] `GITHUB_TOKEN`. Learn more about
personal tokens [here][GithubToken].
For Xcode rebels, checkout `Makefile`.
HTML documentation pages are generated by literate programmin tool [docco][]
[docco]: http://jashkenas.github.io/docco/ "docco"
[GithubToken]: https://developer.github.com/v3/#increasing-the-unauthenticated-rate-limit-for-oauth-applications
[XcodeEnvVar]: http://nshipster.com/launch-arguments-and-environment-variables/
# License
MIT, see [LICENSE.md](https://github.com/JustHTTP/Just/blob/master/LICENSE.md).

1130
Pods/Just/Sources/Just/Just.swift

File diff suppressed because it is too large

14
Pods/Manifest.lock

@ -1,16 +1,16 @@
PODS:
- SwiftSoup (2.2.0)
- Just (0.8.0)
DEPENDENCIES:
- SwiftSoup
- Just
SPEC REPOS:
https://github.com/cocoapods/specs.git:
- SwiftSoup
trunk:
- Just
SPEC CHECKSUMS:
SwiftSoup: 230991361c5ba249d140161860a3864a28b63860
Just: aef5105fb2c7069b65aa6f21aec8f429057f9c19
PODFILE CHECKSUM: d5db68a23a6fb36f3d94717563c9d5199d9f3bdf
PODFILE CHECKSUM: 0ed9277c30181dde6c0bb4a7b18f37dca46c8ec9
COCOAPODS: 1.7.5
COCOAPODS: 1.8.0.beta.2

601
Pods/Pods.xcodeproj/project.pbxproj

@ -7,183 +7,95 @@
objects = {
/* Begin PBXBuildFile section */
0323040E0BAAB427F131B8B7593C1823 /* QueryParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FBEE42EAA661E6389FF98A8F7258F4A /* QueryParser.swift */; };
07DC1E76B4AC8D6F706C134BE25BFCB6 /* HttpStatusException.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99D90FA8A6E0EE945BE59B43E11F1991 /* HttpStatusException.swift */; };
08F669849B088847C5391DCA0D21EBCA /* XmlDeclaration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 268ADCD2947926FB1316F8C94FEFA5A8 /* XmlDeclaration.swift */; };
0ACC881DFA0D90EFAFA203F57BCABF31 /* OrderedDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49ACA67A55E4015D418EE108533FFE7F /* OrderedDictionary.swift */; };
0BD1BBE195BAC934A71711B53A7F020C /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F940C819049CFF8741C0F5E3E075E607 /* Cocoa.framework */; };
13646ECCC236443CE369AC3AB04C85A2 /* CombiningEvaluator.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC000E9275D38311D281B82C5AC7DF99 /* CombiningEvaluator.swift */; };
138730EAB95463CBD4F489B2F0C237E0 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F940C819049CFF8741C0F5E3E075E607 /* Cocoa.framework */; };
182563AFC01AAB5F8010B109A379109D /* Pods-RSwitch-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F000B9DE332C7D2340B760411896249 /* Pods-RSwitch-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
184FEFB86BB797EC34B8245B2448C9B1 /* StreamReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6E94E3D85C69197A8639760C6AD6133 /* StreamReader.swift */; };
1B1E44407BA5AB6B543BFD609D48FB4D /* Whitelist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22288D13FA0694E7A5E908C1CC9DE5BF /* Whitelist.swift */; };
1F6A5C0F3BEF9084C8E9066D3F8AD4F1 /* TokeniserState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3E019DBDCCCD70EFC6949EAE2EFD465 /* TokeniserState.swift */; };
212095A3D1CE5571B351D4999B2D72F7 /* ParseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1722C7DA6C7AADB6E9D3FF2DAC741D /* ParseError.swift */; };
23605B9E6B3F4C6849BEB2995B8D5CC9 /* Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7144D15083381AAD10E032BFC539087D /* Node.swift */; };
268BA1DBE8391AF20EE9D562480A2040 /* StructuralEvaluator.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBCB49D29F5DBB2E4E7C01BF9B286B42 /* StructuralEvaluator.swift */; };
2B29E96B4BEEE65DA4760B9E2070433C /* Pods-RSwitch-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = F0D5168CB259AAB643C8297DEDFBA2A3 /* Pods-RSwitch-dummy.m */; };
3A85CBC090CD9016F473398DEBBBC4D9 /* Element.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E39FD18511DFB98B97795AF070BAE8 /* Element.swift */; };
3DD15E341B2A822AE4170B66EF84785B /* FormElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AE07EC09DF0A0222B828F0110686DE /* FormElement.swift */; };
45AE0479CEA0DCD1CC289FAC7A0D74CD /* Tokeniser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AC2EF2D2470B5A55A2C5462E28A9246 /* Tokeniser.swift */; };
48061A6A40378AA49349A4E8E6E815D5 /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B345D614BE0463ECFFCDAF8AE1D959 /* Comment.swift */; };
4860E1263F573B4EEA7BDF236A1199D0 /* SimpleDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F9B9738C02AF58D1D5DE8834759899 /* SimpleDictionary.swift */; };
4B2DC0B5E57666B3222AD7F24E579353 /* DocumentType.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD297727E87AF4BEBB6DE15799DB2903 /* DocumentType.swift */; };
51C521B58C9C7F3E55CBBF64231CAD92 /* Entities.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DC1F45FA2EFC6FD203B16A40D787A7 /* Entities.swift */; };
59CCEF0DB3257C3791FB7FEF729656A1 /* SwiftSoup-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 29FE73A05C9903FA27E5023421D500A9 /* SwiftSoup-dummy.m */; };
5EA9213700B4A83923FE1F76199D7478 /* TextNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884E4622C66FF5ECF8A9CE1702E69CEB /* TextNode.swift */; };
5F4C6190E5A08291473F5C0E88FC3739 /* SwiftSoup-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 783742B9F3C0D7789BF1936E5320633D /* SwiftSoup-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
605E6909CF08E0DAD68A675C92906264 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D7E478CFFA3AD51798EFA5D2B48C5E5 /* String.swift */; };
6307C9763A4BF4AD4A57596F5756E3D6 /* Selector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 049DA62C981D7D8B1F9AA7F2E8E13327 /* Selector.swift */; };
6400A9389A0A3E67315FA4FD500FB283 /* SwiftSoup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE0E2E1E5641AB6B4DAB08F044186C3 /* SwiftSoup.swift */; };
64641F162787638B7319C71A2986F2D1 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484565425B7A00B867BFA9497B1BEC2C /* Document.swift */; };
68912B01B6D7D4021137BF72C32294F0 /* Collector.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DF6771C707E52E16626CEC16E3C3B8 /* Collector.swift */; };
6D755DA0247FE8D37B6D95D48B8D1697 /* NodeVisitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53EA4EB8183ACC0A1C56FAB0D1003783 /* NodeVisitor.swift */; };
6E8C2F72232BD8D69C06FD9BD135B0E0 /* HtmlTreeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F474D4B15BF04A79EDF6D540A62342C7 /* HtmlTreeBuilder.swift */; };
717A618E1185ED3073BF6557DAD82429 /* UnicodeScalar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D96EEA325686C991085438AA5B69502C /* UnicodeScalar.swift */; };
7383E209342B658049AAC2F6A47CD54E /* Token.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDDABBCE4809A2F836AA605B7E1B11C /* Token.swift */; };
74ED1DAD719312ED2E1EAB6BB0BBFF71 /* BooleanAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF631AD5246CE61C5DEFD7C3E3CD6B7 /* BooleanAttribute.swift */; };
787BA99F9006B790A7E2775CF4371A15 /* Exception.swift in Sources */ = {isa = PBXBuildFile; fileRef = B35D8D6D3BC418C508B79C61D6BC9615 /* Exception.swift */; };
7B5C603DF6AE98590BD5043DEE6533D2 /* StringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8100EBE612516CD2A52C839A5B99AFD1 /* StringBuilder.swift */; };
8A156E7D9F56597ABF006024CE5A38B2 /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F559860F6D0AAE454911A1EF3C0B668B /* Parser.swift */; };
8CCF800901928887AA517E01C12E7AA4 /* CharacterExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8948246B3834F8A3FEB030C74E3A2AB5 /* CharacterExt.swift */; };
96D271E1F3B86DABCAFBF96FA0F2EC03 /* Attributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E376FAE2C865C86BA8BCA7E9FC980FD4 /* Attributes.swift */; };
984F3128934C6FF64C4681754628FF0C /* CharacterReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1773A4068A9764EE8D86DF4EC7366AAC /* CharacterReader.swift */; };
9C390892DA03BA76F8078B5E9F1EE971 /* ArrayExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42D54DB54E3520B11C07EFA4EE4F3031 /* ArrayExt.swift */; };
9D9E42B84863721085CB2C0EE1467CA6 /* TokenQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22E36ED5E17B02508428258D46A39FAE /* TokenQueue.swift */; };
9E2E92CA61302CFAA8D2F97AC088C5C3 /* Attribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6ECF0F75FB8E7DB14833337F209943C6 /* Attribute.swift */; };
9F0612E3E2BD1F85D7620321BEFBE2DC /* StringUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = E58D58E2454CF9D510F70E0FF2A412E7 /* StringUtil.swift */; };
A072BEECFC9A659AC25573E82E8E0D65 /* Validate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1748F4905B4FC87B2F3D346E5B84B616 /* Validate.swift */; };
A815B081D1C20E722584BEDC86E1BA5C /* OrderedSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48FD3F1F6B0CD6E7482B19ECFAABE244 /* OrderedSet.swift */; };
AE698A9FA4B6EDA81A7F68DB435E3309 /* DataNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94DC1B26DF1845449C7B42D5A166B937 /* DataNode.swift */; };
AE993559119E69F0C50BCC2EBBF77E61 /* SerializationException.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A7F2AF32CB0CBEDD6D5F4B84B632F5A /* SerializationException.swift */; };
AF50D130B49C380ED075104BB7671FAB /* Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C98DEADFC55406419E8F91EAEA86E8F /* Tag.swift */; };
B22E0A04FCA9C46583CDE32FD83CFE1E /* ParseSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94EC1C8CEA4EE1B55C08F9562A8E8307 /* ParseSettings.swift */; };
BF10823605FA7A0062852CA043794186 /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0353C7EAAB2DCCB4166955D1F016296B /* Connection.swift */; };
C88398AADEBDF75E34AEF8564807EBBA /* TreeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9301E32BA67B6E5429D48C0AB38851AE /* TreeBuilder.swift */; };
CCCB6C8A300C9117E119983C5E693781 /* Cleaner.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2DEA4C6C21E89636AA601430CFF5504 /* Cleaner.swift */; };
D44EE0B232CCEDE658102DE87A02DAD2 /* NodeTraversor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 024F0251E1CBA4811918382B6D98DC5A /* NodeTraversor.swift */; };
DB16F4FC1AC7BEFE72972A8B7F7629B1 /* XmlTreeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A29168DA5AF77F67441271215C9DC6E /* XmlTreeBuilder.swift */; };
E18636DA726A4EB4566C30DC572CEF30 /* ParseErrorList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DF72B46DECD390354841E0957556D49 /* ParseErrorList.swift */; };
E25940181A204C2DDDE31DC346035755 /* HtmlTreeBuilderState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 152E999AE7B211A8286E727F84405191 /* HtmlTreeBuilderState.swift */; };
EC2ADA20DC4D6D68A1CED448533F7987 /* Pattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E92ECF1E721371A67F4871B34E6820 /* Pattern.swift */; };
F17880FE84897581A0353EA6C49E8909 /* Evaluator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49796846D7023B6FE722096F05F3BC42 /* Evaluator.swift */; };
F8C0CA7AF2CC574511A5C8C02DEDCF43 /* Elements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40D6C27F273357660198C2275EDB9B3F /* Elements.swift */; };
FAF882C254A4D68E3B8DB2F392F5AE8D /* DataUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4171FA64A1D37689A75BB082317E6E65 /* DataUtil.swift */; };
1642FF2CBDC0D5DE293557BAC7483330 /* Just.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8F0FBF810ECE65109C95E0C8BBB893F /* Just.swift */; };
183F65FE3A9F053B826D0BE6F6FD4976 /* Just-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B076A7E6A9514F7BC8853C054606452 /* Just-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
3319EB536901C1505EA1E0A74CA48C36 /* Pods-RSwitch-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = F0D5168CB259AAB643C8297DEDFBA2A3 /* Pods-RSwitch-dummy.m */; };
6E52FCF9BA1CABB3FE89003AA31BF40D /* Just-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A65C21E7E55D2FB16F3F2767B0063C7 /* Just-dummy.m */; };
833AEF1B4D51758EBA3E9E05B3C07743 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F940C819049CFF8741C0F5E3E075E607 /* Cocoa.framework */; };
CE397D843966A9C8A318E053B323901E /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F940C819049CFF8741C0F5E3E075E607 /* Cocoa.framework */; };
CF264F96F9FC03520BF4AEE09521E7C3 /* Pods-RSwitch-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F000B9DE332C7D2340B760411896249 /* Pods-RSwitch-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
B77095034AB3D02A24C74888527C265D /* PBXContainerItemProxy */ = {
E0B9423F4CC99D0A13E90EE927936CDD /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
proxyType = 1;
remoteGlobalIDString = D89F48405DFAFEF441B4E758A15117A5;
remoteInfo = SwiftSoup;
remoteGlobalIDString = CE86A5B255D405184FBF7B919824D937;
remoteInfo = Just;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
01B345D614BE0463ECFFCDAF8AE1D959 /* Comment.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Comment.swift; path = Sources/Comment.swift; sourceTree = "<group>"; };
024F0251E1CBA4811918382B6D98DC5A /* NodeTraversor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NodeTraversor.swift; path = Sources/NodeTraversor.swift; sourceTree = "<group>"; };
0353C7EAAB2DCCB4166955D1F016296B /* Connection.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Connection.swift; path = Sources/Connection.swift; sourceTree = "<group>"; };
049DA62C981D7D8B1F9AA7F2E8E13327 /* Selector.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Selector.swift; path = Sources/Selector.swift; sourceTree = "<group>"; };
0A29168DA5AF77F67441271215C9DC6E /* XmlTreeBuilder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = XmlTreeBuilder.swift; path = Sources/XmlTreeBuilder.swift; sourceTree = "<group>"; };
152E999AE7B211A8286E727F84405191 /* HtmlTreeBuilderState.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HtmlTreeBuilderState.swift; path = Sources/HtmlTreeBuilderState.swift; sourceTree = "<group>"; };
1748F4905B4FC87B2F3D346E5B84B616 /* Validate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Validate.swift; path = Sources/Validate.swift; sourceTree = "<group>"; };
1773A4068A9764EE8D86DF4EC7366AAC /* CharacterReader.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CharacterReader.swift; path = Sources/CharacterReader.swift; sourceTree = "<group>"; };
1DF72B46DECD390354841E0957556D49 /* ParseErrorList.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ParseErrorList.swift; path = Sources/ParseErrorList.swift; sourceTree = "<group>"; };
22288D13FA0694E7A5E908C1CC9DE5BF /* Whitelist.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Whitelist.swift; path = Sources/Whitelist.swift; sourceTree = "<group>"; };
22E36ED5E17B02508428258D46A39FAE /* TokenQueue.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenQueue.swift; path = Sources/TokenQueue.swift; sourceTree = "<group>"; };
268ADCD2947926FB1316F8C94FEFA5A8 /* XmlDeclaration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = XmlDeclaration.swift; path = Sources/XmlDeclaration.swift; sourceTree = "<group>"; };
29FE73A05C9903FA27E5023421D500A9 /* SwiftSoup-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SwiftSoup-dummy.m"; sourceTree = "<group>"; };
3AC2EF2D2470B5A55A2C5462E28A9246 /* Tokeniser.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Tokeniser.swift; path = Sources/Tokeniser.swift; sourceTree = "<group>"; };
40D6C27F273357660198C2275EDB9B3F /* Elements.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Elements.swift; path = Sources/Elements.swift; sourceTree = "<group>"; };
4171FA64A1D37689A75BB082317E6E65 /* DataUtil.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DataUtil.swift; path = Sources/DataUtil.swift; sourceTree = "<group>"; };
42D54DB54E3520B11C07EFA4EE4F3031 /* ArrayExt.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ArrayExt.swift; path = Sources/ArrayExt.swift; sourceTree = "<group>"; };
484565425B7A00B867BFA9497B1BEC2C /* Document.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Document.swift; path = Sources/Document.swift; sourceTree = "<group>"; };
48FD3F1F6B0CD6E7482B19ECFAABE244 /* OrderedSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OrderedSet.swift; path = Sources/OrderedSet.swift; sourceTree = "<group>"; };
49796846D7023B6FE722096F05F3BC42 /* Evaluator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Evaluator.swift; path = Sources/Evaluator.swift; sourceTree = "<group>"; };
49ACA67A55E4015D418EE108533FFE7F /* OrderedDictionary.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OrderedDictionary.swift; path = Sources/OrderedDictionary.swift; sourceTree = "<group>"; };
4F000B9DE332C7D2340B760411896249 /* Pods-RSwitch-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-RSwitch-umbrella.h"; sourceTree = "<group>"; };
4FE0E2E1E5641AB6B4DAB08F044186C3 /* SwiftSoup.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftSoup.swift; path = Sources/SwiftSoup.swift; sourceTree = "<group>"; };
53EA4EB8183ACC0A1C56FAB0D1003783 /* NodeVisitor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NodeVisitor.swift; path = Sources/NodeVisitor.swift; sourceTree = "<group>"; };
58F9B9738C02AF58D1D5DE8834759899 /* SimpleDictionary.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SimpleDictionary.swift; path = Sources/SimpleDictionary.swift; sourceTree = "<group>"; };
5A7F2AF32CB0CBEDD6D5F4B84B632F5A /* SerializationException.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SerializationException.swift; path = Sources/SerializationException.swift; sourceTree = "<group>"; };
5D52FFF81A1E26634DAD54B2BE7CBA99 /* Pods_RSwitch.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RSwitch.framework; sourceTree = BUILT_PRODUCTS_DIR; };
5F38EF8DCE7D46A354D1D1BBAC952903 /* SwiftSoup.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftSoup.framework; sourceTree = BUILT_PRODUCTS_DIR; };
653737F57003A332C4E6F80EA2E55DA6 /* SwiftSoup.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftSoup.xcconfig; sourceTree = "<group>"; };
574603E2D7597AEEB79587D7C3889AA1 /* Just.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Just.xcconfig; sourceTree = "<group>"; };
5A65C21E7E55D2FB16F3F2767B0063C7 /* Just-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Just-dummy.m"; sourceTree = "<group>"; };
5D52FFF81A1E26634DAD54B2BE7CBA99 /* Pods_RSwitch.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_RSwitch.framework; path = "Pods-RSwitch.framework"; sourceTree = BUILT_PRODUCTS_DIR; };
614B6A9B3F94B8D815819AA921D78D95 /* Just-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Just-prefix.pch"; sourceTree = "<group>"; };
6848EDAB880F7E2D2B180B0D25F67504 /* Just.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Just.modulemap; sourceTree = "<group>"; };
6B076A7E6A9514F7BC8853C054606452 /* Just-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Just-umbrella.h"; sourceTree = "<group>"; };
6B5F1D02B43D15E9D5752784305E1A5D /* Pods-RSwitch.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-RSwitch.debug.xcconfig"; sourceTree = "<group>"; };
6C98DEADFC55406419E8F91EAEA86E8F /* Tag.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Tag.swift; path = Sources/Tag.swift; sourceTree = "<group>"; };
6ECF0F75FB8E7DB14833337F209943C6 /* Attribute.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Attribute.swift; path = Sources/Attribute.swift; sourceTree = "<group>"; };
7144D15083381AAD10E032BFC539087D /* Node.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Node.swift; path = Sources/Node.swift; sourceTree = "<group>"; };
783742B9F3C0D7789BF1936E5320633D /* SwiftSoup-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftSoup-umbrella.h"; sourceTree = "<group>"; };
78E39FD18511DFB98B97795AF070BAE8 /* Element.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Element.swift; path = Sources/Element.swift; sourceTree = "<group>"; };
7B194431FBE08FAA54D43A7D8C27124C /* Pods-RSwitch-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-RSwitch-Info.plist"; sourceTree = "<group>"; };
7FBEE42EAA661E6389FF98A8F7258F4A /* QueryParser.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = QueryParser.swift; path = Sources/QueryParser.swift; sourceTree = "<group>"; };
8100EBE612516CD2A52C839A5B99AFD1 /* StringBuilder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StringBuilder.swift; path = Sources/StringBuilder.swift; sourceTree = "<group>"; };
884E4622C66FF5ECF8A9CE1702E69CEB /* TextNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TextNode.swift; path = Sources/TextNode.swift; sourceTree = "<group>"; };
8948246B3834F8A3FEB030C74E3A2AB5 /* CharacterExt.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CharacterExt.swift; path = Sources/CharacterExt.swift; sourceTree = "<group>"; };
8EDDABBCE4809A2F836AA605B7E1B11C /* Token.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Token.swift; path = Sources/Token.swift; sourceTree = "<group>"; };
9301E32BA67B6E5429D48C0AB38851AE /* TreeBuilder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TreeBuilder.swift; path = Sources/TreeBuilder.swift; sourceTree = "<group>"; };
94DC1B26DF1845449C7B42D5A166B937 /* DataNode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DataNode.swift; path = Sources/DataNode.swift; sourceTree = "<group>"; };
94EC1C8CEA4EE1B55C08F9562A8E8307 /* ParseSettings.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ParseSettings.swift; path = Sources/ParseSettings.swift; sourceTree = "<group>"; };
99D90FA8A6E0EE945BE59B43E11F1991 /* HttpStatusException.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HttpStatusException.swift; path = Sources/HttpStatusException.swift; sourceTree = "<group>"; };
9D7E478CFFA3AD51798EFA5D2B48C5E5 /* String.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = String.swift; path = Sources/String.swift; sourceTree = "<group>"; };
9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
A1E1DF856B2F4607BD0F2A365D1D9FD3 /* SwiftSoup-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftSoup-prefix.pch"; sourceTree = "<group>"; };
A9DC1F45FA2EFC6FD203B16A40D787A7 /* Entities.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Entities.swift; path = Sources/Entities.swift; sourceTree = "<group>"; };
9A212383B73C76EDF23FB6126214CAB4 /* Just.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Just.framework; path = Just.framework; sourceTree = BUILT_PRODUCTS_DIR; };
9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
ACB677C58AE332368BBEB1AF0A6918B1 /* Pods-RSwitch-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-RSwitch-acknowledgements.plist"; sourceTree = "<group>"; };
AD297727E87AF4BEBB6DE15799DB2903 /* DocumentType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DocumentType.swift; path = Sources/DocumentType.swift; sourceTree = "<group>"; };
B35D8D6D3BC418C508B79C61D6BC9615 /* Exception.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Exception.swift; path = Sources/Exception.swift; sourceTree = "<group>"; };
B39AAB51B4D348DDE0A772879510BD57 /* Pods-RSwitch-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-RSwitch-frameworks.sh"; sourceTree = "<group>"; };
C06313BA3ECCC78CBFABC9763DF22594 /* SwiftSoup-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "SwiftSoup-Info.plist"; sourceTree = "<group>"; };
C4DF6771C707E52E16626CEC16E3C3B8 /* Collector.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Collector.swift; path = Sources/Collector.swift; sourceTree = "<group>"; };
C6E94E3D85C69197A8639760C6AD6133 /* StreamReader.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StreamReader.swift; path = Sources/StreamReader.swift; sourceTree = "<group>"; };
CC1722C7DA6C7AADB6E9D3FF2DAC741D /* ParseError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ParseError.swift; path = Sources/ParseError.swift; sourceTree = "<group>"; };
D6E92ECF1E721371A67F4871B34E6820 /* Pattern.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Pattern.swift; path = Sources/Pattern.swift; sourceTree = "<group>"; };
B8F0FBF810ECE65109C95E0C8BBB893F /* Just.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Just.swift; path = Sources/Just/Just.swift; sourceTree = "<group>"; };
D722B0A61F22FDFBDEF11F24016B4D1D /* Pods-RSwitch.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-RSwitch.release.xcconfig"; sourceTree = "<group>"; };
D96EEA325686C991085438AA5B69502C /* UnicodeScalar.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = UnicodeScalar.swift; path = Sources/UnicodeScalar.swift; sourceTree = "<group>"; };
DB428D92F1F877A57EBAE4039884D744 /* Pods-RSwitch-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-RSwitch-acknowledgements.markdown"; sourceTree = "<group>"; };
DCFA50DE569F3B468BACF6ECDE5DDBA8 /* Pods-RSwitch.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-RSwitch.modulemap"; sourceTree = "<group>"; };
E2DEA4C6C21E89636AA601430CFF5504 /* Cleaner.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Cleaner.swift; path = Sources/Cleaner.swift; sourceTree = "<group>"; };
E376FAE2C865C86BA8BCA7E9FC980FD4 /* Attributes.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Attributes.swift; path = Sources/Attributes.swift; sourceTree = "<group>"; };
E3E019DBDCCCD70EFC6949EAE2EFD465 /* TokeniserState.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokeniserState.swift; path = Sources/TokeniserState.swift; sourceTree = "<group>"; };
E58D58E2454CF9D510F70E0FF2A412E7 /* StringUtil.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StringUtil.swift; path = Sources/StringUtil.swift; sourceTree = "<group>"; };
E5AE07EC09DF0A0222B828F0110686DE /* FormElement.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FormElement.swift; path = Sources/FormElement.swift; sourceTree = "<group>"; };
EAF631AD5246CE61C5DEFD7C3E3CD6B7 /* BooleanAttribute.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BooleanAttribute.swift; path = Sources/BooleanAttribute.swift; sourceTree = "<group>"; };
EBCB49D29F5DBB2E4E7C01BF9B286B42 /* StructuralEvaluator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StructuralEvaluator.swift; path = Sources/StructuralEvaluator.swift; sourceTree = "<group>"; };
EC000E9275D38311D281B82C5AC7DF99 /* CombiningEvaluator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CombiningEvaluator.swift; path = Sources/CombiningEvaluator.swift; sourceTree = "<group>"; };
EFC11670D004E932C92C144DAC577866 /* SwiftSoup.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SwiftSoup.modulemap; sourceTree = "<group>"; };
F0D5168CB259AAB643C8297DEDFBA2A3 /* Pods-RSwitch-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-RSwitch-dummy.m"; sourceTree = "<group>"; };
F474D4B15BF04A79EDF6D540A62342C7 /* HtmlTreeBuilder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HtmlTreeBuilder.swift; path = Sources/HtmlTreeBuilder.swift; sourceTree = "<group>"; };
F559860F6D0AAE454911A1EF3C0B668B /* Parser.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Parser.swift; path = Sources/Parser.swift; sourceTree = "<group>"; };
F167994BDDD437D52AE2F7A9A702D838 /* Just-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Just-Info.plist"; sourceTree = "<group>"; };
F940C819049CFF8741C0F5E3E075E607 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/System/Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
1387CD09AAD2DE1E81F58DA4273382DF /* Frameworks */ = {
992CF3B53A220E5FFE23E37882E06283 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
0BD1BBE195BAC934A71711B53A7F020C /* Cocoa.framework in Frameworks */,
CE397D843966A9C8A318E053B323901E /* Cocoa.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DB40808C3408552D3CD8A71C3992A07C /* Frameworks */ = {
E711F5EAC29CAB34E00E66AC2DB29C84 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
138730EAB95463CBD4F489B2F0C237E0 /* Cocoa.framework in Frameworks */,
833AEF1B4D51758EBA3E9E05B3C07743 /* Cocoa.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
099C0EBCE1385C461569E1E3FC5B58C5 /* Products */ = {
345EEEBE9381D91220155A959CF832C0 /* Pods */ = {
isa = PBXGroup;
children = (
9E03303A3E700793D80ED305328EEB37 /* Just */,
);
name = Pods;
sourceTree = "<group>";
};
46872DBB8C34D63DE630A92868138E93 /* Support Files */ = {
isa = PBXGroup;
children = (
6848EDAB880F7E2D2B180B0D25F67504 /* Just.modulemap */,
574603E2D7597AEEB79587D7C3889AA1 /* Just.xcconfig */,
5A65C21E7E55D2FB16F3F2767B0063C7 /* Just-dummy.m */,
F167994BDDD437D52AE2F7A9A702D838 /* Just-Info.plist */,
614B6A9B3F94B8D815819AA921D78D95 /* Just-prefix.pch */,
6B076A7E6A9514F7BC8853C054606452 /* Just-umbrella.h */,
);
name = "Support Files";
path = "../Target Support Files/Just";
sourceTree = "<group>";
};
623DD1C81238D9DB831E4CF39661DBBE /* Products */ = {
isa = PBXGroup;
children = (
9A212383B73C76EDF23FB6126214CAB4 /* Just.framework */,
5D52FFF81A1E26634DAD54B2BE7CBA99 /* Pods_RSwitch.framework */,
5F38EF8DCE7D46A354D1D1BBAC952903 /* SwiftSoup.framework */,
);
name = Products;
sourceTree = "<group>";
@ -213,68 +125,14 @@
name = "Targets Support Files";
sourceTree = "<group>";
};
B67502FE5D481268EEE7772D16B9AFC4 /* SwiftSoup */ = {
9E03303A3E700793D80ED305328EEB37 /* Just */ = {
isa = PBXGroup;
children = (
42D54DB54E3520B11C07EFA4EE4F3031 /* ArrayExt.swift */,
6ECF0F75FB8E7DB14833337F209943C6 /* Attribute.swift */,
E376FAE2C865C86BA8BCA7E9FC980FD4 /* Attributes.swift */,
EAF631AD5246CE61C5DEFD7C3E3CD6B7 /* BooleanAttribute.swift */,
8948246B3834F8A3FEB030C74E3A2AB5 /* CharacterExt.swift */,
1773A4068A9764EE8D86DF4EC7366AAC /* CharacterReader.swift */,
E2DEA4C6C21E89636AA601430CFF5504 /* Cleaner.swift */,
C4DF6771C707E52E16626CEC16E3C3B8 /* Collector.swift */,
EC000E9275D38311D281B82C5AC7DF99 /* CombiningEvaluator.swift */,
01B345D614BE0463ECFFCDAF8AE1D959 /* Comment.swift */,
0353C7EAAB2DCCB4166955D1F016296B /* Connection.swift */,
94DC1B26DF1845449C7B42D5A166B937 /* DataNode.swift */,
4171FA64A1D37689A75BB082317E6E65 /* DataUtil.swift */,
484565425B7A00B867BFA9497B1BEC2C /* Document.swift */,
AD297727E87AF4BEBB6DE15799DB2903 /* DocumentType.swift */,
78E39FD18511DFB98B97795AF070BAE8 /* Element.swift */,
40D6C27F273357660198C2275EDB9B3F /* Elements.swift */,
A9DC1F45FA2EFC6FD203B16A40D787A7 /* Entities.swift */,
49796846D7023B6FE722096F05F3BC42 /* Evaluator.swift */,
B35D8D6D3BC418C508B79C61D6BC9615 /* Exception.swift */,
E5AE07EC09DF0A0222B828F0110686DE /* FormElement.swift */,
F474D4B15BF04A79EDF6D540A62342C7 /* HtmlTreeBuilder.swift */,
152E999AE7B211A8286E727F84405191 /* HtmlTreeBuilderState.swift */,
99D90FA8A6E0EE945BE59B43E11F1991 /* HttpStatusException.swift */,
7144D15083381AAD10E032BFC539087D /* Node.swift */,
024F0251E1CBA4811918382B6D98DC5A /* NodeTraversor.swift */,
53EA4EB8183ACC0A1C56FAB0D1003783 /* NodeVisitor.swift */,
49ACA67A55E4015D418EE108533FFE7F /* OrderedDictionary.swift */,
48FD3F1F6B0CD6E7482B19ECFAABE244 /* OrderedSet.swift */,
CC1722C7DA6C7AADB6E9D3FF2DAC741D /* ParseError.swift */,
1DF72B46DECD390354841E0957556D49 /* ParseErrorList.swift */,
F559860F6D0AAE454911A1EF3C0B668B /* Parser.swift */,
94EC1C8CEA4EE1B55C08F9562A8E8307 /* ParseSettings.swift */,
D6E92ECF1E721371A67F4871B34E6820 /* Pattern.swift */,
7FBEE42EAA661E6389FF98A8F7258F4A /* QueryParser.swift */,
049DA62C981D7D8B1F9AA7F2E8E13327 /* Selector.swift */,
5A7F2AF32CB0CBEDD6D5F4B84B632F5A /* SerializationException.swift */,
58F9B9738C02AF58D1D5DE8834759899 /* SimpleDictionary.swift */,
C6E94E3D85C69197A8639760C6AD6133 /* StreamReader.swift */,
9D7E478CFFA3AD51798EFA5D2B48C5E5 /* String.swift */,
8100EBE612516CD2A52C839A5B99AFD1 /* StringBuilder.swift */,
E58D58E2454CF9D510F70E0FF2A412E7 /* StringUtil.swift */,
EBCB49D29F5DBB2E4E7C01BF9B286B42 /* StructuralEvaluator.swift */,
4FE0E2E1E5641AB6B4DAB08F044186C3 /* SwiftSoup.swift */,
6C98DEADFC55406419E8F91EAEA86E8F /* Tag.swift */,
884E4622C66FF5ECF8A9CE1702E69CEB /* TextNode.swift */,
8EDDABBCE4809A2F836AA605B7E1B11C /* Token.swift */,
3AC2EF2D2470B5A55A2C5462E28A9246 /* Tokeniser.swift */,
E3E019DBDCCCD70EFC6949EAE2EFD465 /* TokeniserState.swift */,
22E36ED5E17B02508428258D46A39FAE /* TokenQueue.swift */,
9301E32BA67B6E5429D48C0AB38851AE /* TreeBuilder.swift */,
D96EEA325686C991085438AA5B69502C /* UnicodeScalar.swift */,
1748F4905B4FC87B2F3D346E5B84B616 /* Validate.swift */,
22288D13FA0694E7A5E908C1CC9DE5BF /* Whitelist.swift */,
268ADCD2947926FB1316F8C94FEFA5A8 /* XmlDeclaration.swift */,
0A29168DA5AF77F67441271215C9DC6E /* XmlTreeBuilder.swift */,
E1B5BB6304CFDA3D036CDE4281FF4070 /* Support Files */,
B8F0FBF810ECE65109C95E0C8BBB893F /* Just.swift */,
46872DBB8C34D63DE630A92868138E93 /* Support Files */,
);
path = SwiftSoup;
name = Just;
path = Just;
sourceTree = "<group>";
};
CBD43E0947D94E318A0ED1350CFF29ED /* OS X */ = {
@ -290,20 +148,12 @@
children = (
9D940727FF8FB9C785EB98E56350EF41 /* Podfile */,
E0A1E60606E0BF6E2E10F1F01350DFE8 /* Frameworks */,
CFB4ABEAEAFCDB4587FF6BE5A683746C /* Pods */,
099C0EBCE1385C461569E1E3FC5B58C5 /* Products */,
345EEEBE9381D91220155A959CF832C0 /* Pods */,
623DD1C81238D9DB831E4CF39661DBBE /* Products */,
9A32C13F6CC4CEB61BBDD444CD90E386 /* Targets Support Files */,
);
sourceTree = "<group>";
};
CFB4ABEAEAFCDB4587FF6BE5A683746C /* Pods */ = {
isa = PBXGroup;
children = (
B67502FE5D481268EEE7772D16B9AFC4 /* SwiftSoup */,
);
name = Pods;
sourceTree = "<group>";
};
E0A1E60606E0BF6E2E10F1F01350DFE8 /* Frameworks */ = {
isa = PBXGroup;
children = (
@ -312,36 +162,22 @@
name = Frameworks;
sourceTree = "<group>";
};
E1B5BB6304CFDA3D036CDE4281FF4070 /* Support Files */ = {
isa = PBXGroup;
children = (
EFC11670D004E932C92C144DAC577866 /* SwiftSoup.modulemap */,
653737F57003A332C4E6F80EA2E55DA6 /* SwiftSoup.xcconfig */,
29FE73A05C9903FA27E5023421D500A9 /* SwiftSoup-dummy.m */,
C06313BA3ECCC78CBFABC9763DF22594 /* SwiftSoup-Info.plist */,
A1E1DF856B2F4607BD0F2A365D1D9FD3 /* SwiftSoup-prefix.pch */,
783742B9F3C0D7789BF1936E5320633D /* SwiftSoup-umbrella.h */,
);
name = "Support Files";
path = "../Target Support Files/SwiftSoup";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
45226E43EF0BCD4D807D13B063AED550 /* Headers */ = {
21756C5F361BBD3F898F752B5FC5280B /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
182563AFC01AAB5F8010B109A379109D /* Pods-RSwitch-umbrella.h in Headers */,
CF264F96F9FC03520BF4AEE09521E7C3 /* Pods-RSwitch-umbrella.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
BEB4C428105AE2B5F04B887EA6FB77BA /* Headers */ = {
A012C3164E69047E3F0F8E2A30BFD809 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
5F4C6190E5A08291473F5C0E88FC3739 /* SwiftSoup-umbrella.h in Headers */,
183F65FE3A9F053B826D0BE6F6FD4976 /* Just-umbrella.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -350,39 +186,39 @@
/* Begin PBXNativeTarget section */
36D714ADB141D512E7387D09CD1BF199 /* Pods-RSwitch */ = {
isa = PBXNativeTarget;
buildConfigurationList = 96487669C0D0E8C89F8460652C8D1EA6 /* Build configuration list for PBXNativeTarget "Pods-RSwitch" */;
buildConfigurationList = 338659363A374BA154D8B72A03CAB0FD /* Build configuration list for PBXNativeTarget "Pods-RSwitch" */;
buildPhases = (
45226E43EF0BCD4D807D13B063AED550 /* Headers */,
CBAEEE521B01B8038F1ECD057D96D45D /* Sources */,
DB40808C3408552D3CD8A71C3992A07C /* Frameworks */,
EDB54ECC04F10737C0F8B0D48488AE87 /* Resources */,
21756C5F361BBD3F898F752B5FC5280B /* Headers */,
98D24086F94BD93279491A23481B9646 /* Sources */,
992CF3B53A220E5FFE23E37882E06283 /* Frameworks */,
56F8E51A5CD786E480D2E399AD98C4C6 /* Resources */,
);
buildRules = (
);
dependencies = (
7B277E72C2BDA768D527D230E005F952 /* PBXTargetDependency */,
77D947801AA4E2D5978E29F77C5511CD /* PBXTargetDependency */,
);
name = "Pods-RSwitch";
productName = "Pods-RSwitch";
productReference = 5D52FFF81A1E26634DAD54B2BE7CBA99 /* Pods_RSwitch.framework */;
productType = "com.apple.product-type.framework";
};
D89F48405DFAFEF441B4E758A15117A5 /* SwiftSoup */ = {
CE86A5B255D405184FBF7B919824D937 /* Just */ = {
isa = PBXNativeTarget;
buildConfigurationList = 58C662AE5354AFF1184BB5205EAFAD64 /* Build configuration list for PBXNativeTarget "SwiftSoup" */;
buildConfigurationList = 63262DFBECA49AB0DB89612107EBDE33 /* Build configuration list for PBXNativeTarget "Just" */;
buildPhases = (
BEB4C428105AE2B5F04B887EA6FB77BA /* Headers */,
2B35B20F754B9B73D4B17E77AE7B68D9 /* Sources */,
1387CD09AAD2DE1E81F58DA4273382DF /* Frameworks */,
EC434C17106FD2518CB3618747654313 /* Resources */,
A012C3164E69047E3F0F8E2A30BFD809 /* Headers */,
97F5F2B366C744A49770C2246E9ADFDB /* Sources */,
E711F5EAC29CAB34E00E66AC2DB29C84 /* Frameworks */,
DCA62D207B545086813B397BFAF10193 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = SwiftSoup;
productName = SwiftSoup;
productReference = 5F38EF8DCE7D46A354D1D1BBAC952903 /* SwiftSoup.framework */;
name = Just;
productName = Just;
productReference = 9A212383B73C76EDF23FB6126214CAB4 /* Just.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
@ -402,25 +238,25 @@
en,
);
mainGroup = CF1408CF629C7361332E53B88F7BD30C;
productRefGroup = 099C0EBCE1385C461569E1E3FC5B58C5 /* Products */;
productRefGroup = 623DD1C81238D9DB831E4CF39661DBBE /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
CE86A5B255D405184FBF7B919824D937 /* Just */,
36D714ADB141D512E7387D09CD1BF199 /* Pods-RSwitch */,
D89F48405DFAFEF441B4E758A15117A5 /* SwiftSoup */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
EC434C17106FD2518CB3618747654313 /* Resources */ = {
56F8E51A5CD786E480D2E399AD98C4C6 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
EDB54ECC04F10737C0F8B0D48488AE87 /* Resources */ = {
DCA62D207B545086813B397BFAF10193 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
@ -430,174 +266,35 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
2B35B20F754B9B73D4B17E77AE7B68D9 /* Sources */ = {
97F5F2B366C744A49770C2246E9ADFDB /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9C390892DA03BA76F8078B5E9F1EE971 /* ArrayExt.swift in Sources */,
9E2E92CA61302CFAA8D2F97AC088C5C3 /* Attribute.swift in Sources */,
96D271E1F3B86DABCAFBF96FA0F2EC03 /* Attributes.swift in Sources */,
74ED1DAD719312ED2E1EAB6BB0BBFF71 /* BooleanAttribute.swift in Sources */,
8CCF800901928887AA517E01C12E7AA4 /* CharacterExt.swift in Sources */,
984F3128934C6FF64C4681754628FF0C /* CharacterReader.swift in Sources */,
CCCB6C8A300C9117E119983C5E693781 /* Cleaner.swift in Sources */,
68912B01B6D7D4021137BF72C32294F0 /* Collector.swift in Sources */,
13646ECCC236443CE369AC3AB04C85A2 /* CombiningEvaluator.swift in Sources */,
48061A6A40378AA49349A4E8E6E815D5 /* Comment.swift in Sources */,
BF10823605FA7A0062852CA043794186 /* Connection.swift in Sources */,
AE698A9FA4B6EDA81A7F68DB435E3309 /* DataNode.swift in Sources */,
FAF882C254A4D68E3B8DB2F392F5AE8D /* DataUtil.swift in Sources */,
64641F162787638B7319C71A2986F2D1 /* Document.swift in Sources */,
4B2DC0B5E57666B3222AD7F24E579353 /* DocumentType.swift in Sources */,
3A85CBC090CD9016F473398DEBBBC4D9 /* Element.swift in Sources */,
F8C0CA7AF2CC574511A5C8C02DEDCF43 /* Elements.swift in Sources */,
51C521B58C9C7F3E55CBBF64231CAD92 /* Entities.swift in Sources */,
F17880FE84897581A0353EA6C49E8909 /* Evaluator.swift in Sources */,
787BA99F9006B790A7E2775CF4371A15 /* Exception.swift in Sources */,
3DD15E341B2A822AE4170B66EF84785B /* FormElement.swift in Sources */,
6E8C2F72232BD8D69C06FD9BD135B0E0 /* HtmlTreeBuilder.swift in Sources */,
E25940181A204C2DDDE31DC346035755 /* HtmlTreeBuilderState.swift in Sources */,
07DC1E76B4AC8D6F706C134BE25BFCB6 /* HttpStatusException.swift in Sources */,
23605B9E6B3F4C6849BEB2995B8D5CC9 /* Node.swift in Sources */,
D44EE0B232CCEDE658102DE87A02DAD2 /* NodeTraversor.swift in Sources */,
6D755DA0247FE8D37B6D95D48B8D1697 /* NodeVisitor.swift in Sources */,
0ACC881DFA0D90EFAFA203F57BCABF31 /* OrderedDictionary.swift in Sources */,
A815B081D1C20E722584BEDC86E1BA5C /* OrderedSet.swift in Sources */,
212095A3D1CE5571B351D4999B2D72F7 /* ParseError.swift in Sources */,
E18636DA726A4EB4566C30DC572CEF30 /* ParseErrorList.swift in Sources */,
8A156E7D9F56597ABF006024CE5A38B2 /* Parser.swift in Sources */,
B22E0A04FCA9C46583CDE32FD83CFE1E /* ParseSettings.swift in Sources */,
EC2ADA20DC4D6D68A1CED448533F7987 /* Pattern.swift in Sources */,
0323040E0BAAB427F131B8B7593C1823 /* QueryParser.swift in Sources */,
6307C9763A4BF4AD4A57596F5756E3D6 /* Selector.swift in Sources */,
AE993559119E69F0C50BCC2EBBF77E61 /* SerializationException.swift in Sources */,
4860E1263F573B4EEA7BDF236A1199D0 /* SimpleDictionary.swift in Sources */,
184FEFB86BB797EC34B8245B2448C9B1 /* StreamReader.swift in Sources */,
605E6909CF08E0DAD68A675C92906264 /* String.swift in Sources */,
7B5C603DF6AE98590BD5043DEE6533D2 /* StringBuilder.swift in Sources */,
9F0612E3E2BD1F85D7620321BEFBE2DC /* StringUtil.swift in Sources */,
268BA1DBE8391AF20EE9D562480A2040 /* StructuralEvaluator.swift in Sources */,
59CCEF0DB3257C3791FB7FEF729656A1 /* SwiftSoup-dummy.m in Sources */,
6400A9389A0A3E67315FA4FD500FB283 /* SwiftSoup.swift in Sources */,
AF50D130B49C380ED075104BB7671FAB /* Tag.swift in Sources */,
5EA9213700B4A83923FE1F76199D7478 /* TextNode.swift in Sources */,
7383E209342B658049AAC2F6A47CD54E /* Token.swift in Sources */,
45AE0479CEA0DCD1CC289FAC7A0D74CD /* Tokeniser.swift in Sources */,
1F6A5C0F3BEF9084C8E9066D3F8AD4F1 /* TokeniserState.swift in Sources */,
9D9E42B84863721085CB2C0EE1467CA6 /* TokenQueue.swift in Sources */,
C88398AADEBDF75E34AEF8564807EBBA /* TreeBuilder.swift in Sources */,
717A618E1185ED3073BF6557DAD82429 /* UnicodeScalar.swift in Sources */,
A072BEECFC9A659AC25573E82E8E0D65 /* Validate.swift in Sources */,
1B1E44407BA5AB6B543BFD609D48FB4D /* Whitelist.swift in Sources */,
08F669849B088847C5391DCA0D21EBCA /* XmlDeclaration.swift in Sources */,
DB16F4FC1AC7BEFE72972A8B7F7629B1 /* XmlTreeBuilder.swift in Sources */,
6E52FCF9BA1CABB3FE89003AA31BF40D /* Just-dummy.m in Sources */,
1642FF2CBDC0D5DE293557BAC7483330 /* Just.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
CBAEEE521B01B8038F1ECD057D96D45D /* Sources */ = {
98D24086F94BD93279491A23481B9646 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2B29E96B4BEEE65DA4760B9E2070433C /* Pods-RSwitch-dummy.m in Sources */,
3319EB536901C1505EA1E0A74CA48C36 /* Pods-RSwitch-dummy.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
7B277E72C2BDA768D527D230E005F952 /* PBXTargetDependency */ = {
77D947801AA4E2D5978E29F77C5511CD /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = SwiftSoup;
target = D89F48405DFAFEF441B4E758A15117A5 /* SwiftSoup */;
targetProxy = B77095034AB3D02A24C74888527C265D /* PBXContainerItemProxy */;
name = Just;
target = CE86A5B255D405184FBF7B919824D937 /* Just */;
targetProxy = E0B9423F4CC99D0A13E90EE927936CDD /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
28937119401B98869866251E32C12494 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 6B5F1D02B43D15E9D5752784305E1A5D /* Pods-RSwitch.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_WEAK = NO;
CODE_SIGN_IDENTITY = "Developer ID Application";
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = CBY22P58G8;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "Target Support Files/Pods-RSwitch/Pods-RSwitch-Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/Frameworks",
);
MACH_O_TYPE = staticlib;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MODULEMAP_FILE = "Target Support Files/Pods-RSwitch/Pods-RSwitch.modulemap";
OTHER_LDFLAGS = "";
OTHER_LIBTOOLFLAGS = "";
PODS_ROOT = "$(SRCROOT)";
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx;
SKIP_INSTALL = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
30E1CB02BB5209911271753062F04733 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 653737F57003A332C4E6F80EA2E55DA6 /* SwiftSoup.xcconfig */;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_WEAK = NO;
CODE_SIGN_IDENTITY = "Developer ID Application";
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = CBY22P58G8;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A;
GCC_PREFIX_HEADER = "Target Support Files/SwiftSoup/SwiftSoup-prefix.pch";
INFOPLIST_FILE = "Target Support Files/SwiftSoup/SwiftSoup-Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.10;
MODULEMAP_FILE = "Target Support Files/SwiftSoup/SwiftSoup.modulemap";
PRODUCT_MODULE_NAME = SwiftSoup;
PRODUCT_NAME = SwiftSoup;
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
430C0A1F2A9CB52955BEC7CE69B7B629 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -662,6 +359,44 @@
};
name = Debug;
};
434E5D3509C83A8B996D60E6BEDA6C77 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 574603E2D7597AEEB79587D7C3889AA1 /* Just.xcconfig */;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_WEAK = NO;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A;
GCC_PREFIX_HEADER = "Target Support Files/Just/Just-prefix.pch";
INFOPLIST_FILE = "Target Support Files/Just/Just-Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.10;
MODULEMAP_FILE = "Target Support Files/Just/Just.modulemap";
PRODUCT_MODULE_NAME = Just;
PRODUCT_NAME = Just;
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
597E092427DF423992C59C814880FE96 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -716,33 +451,31 @@
PRODUCT_NAME = "$(TARGET_NAME)";
STRIP_INSTALLED_PRODUCT = NO;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
SYMROOT = "${SRCROOT}/../build";
};
name = Release;
};
CE1E7DB9F3C040366A3496455A8B24BD /* Debug */ = {
ABCA764BDF2BC6B0A9DA9B8EAB2149ED /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 653737F57003A332C4E6F80EA2E55DA6 /* SwiftSoup.xcconfig */;
baseConfigurationReference = 574603E2D7597AEEB79587D7C3889AA1 /* Just.xcconfig */;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_WEAK = NO;
CODE_SIGN_IDENTITY = "Developer ID Application";
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = CBY22P58G8;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A;
GCC_PREFIX_HEADER = "Target Support Files/SwiftSoup/SwiftSoup-prefix.pch";
INFOPLIST_FILE = "Target Support Files/SwiftSoup/SwiftSoup-Info.plist";
GCC_PREFIX_HEADER = "Target Support Files/Just/Just-prefix.pch";
INFOPLIST_FILE = "Target Support Files/Just/Just-Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -750,10 +483,9 @@
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.10;
MODULEMAP_FILE = "Target Support Files/SwiftSoup/SwiftSoup.modulemap";
PRODUCT_MODULE_NAME = SwiftSoup;
PRODUCT_NAME = SwiftSoup;
PROVISIONING_PROFILE_SPECIFIER = "";
MODULEMAP_FILE = "Target Support Files/Just/Just.modulemap";
PRODUCT_MODULE_NAME = Just;
PRODUCT_NAME = Just;
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
@ -763,22 +495,20 @@
};
name = Debug;
};
D7F84BF4AD9212BB6A99F6519EC528A6 /* Release */ = {
D5BF4F3585D5CD8FE3C69B5C29C7A323 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D722B0A61F22FDFBDEF11F24016B4D1D /* Pods-RSwitch.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_WEAK = NO;
CODE_SIGN_IDENTITY = "Developer ID Application";
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = CBY22P58G8;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
@ -798,7 +528,6 @@
PODS_ROOT = "$(SRCROOT)";
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx;
SKIP_INSTALL = YES;
VERSIONING_SYSTEM = "apple-generic";
@ -806,32 +535,72 @@
};
name = Release;
};
F956E996BCA181F8AAFAB802C831FAB9 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 6B5F1D02B43D15E9D5752784305E1A5D /* Pods-RSwitch.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_WEAK = NO;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "Target Support Files/Pods-RSwitch/Pods-RSwitch-Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/Frameworks",
);
MACH_O_TYPE = staticlib;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MODULEMAP_FILE = "Target Support Files/Pods-RSwitch/Pods-RSwitch.modulemap";
OTHER_LDFLAGS = "";
OTHER_LIBTOOLFLAGS = "";
PODS_ROOT = "$(SRCROOT)";
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = macosx;
SKIP_INSTALL = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = {
338659363A374BA154D8B72A03CAB0FD /* Build configuration list for PBXNativeTarget "Pods-RSwitch" */ = {
isa = XCConfigurationList;
buildConfigurations = (
430C0A1F2A9CB52955BEC7CE69B7B629 /* Debug */,
597E092427DF423992C59C814880FE96 /* Release */,
F956E996BCA181F8AAFAB802C831FAB9 /* Debug */,
D5BF4F3585D5CD8FE3C69B5C29C7A323 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
58C662AE5354AFF1184BB5205EAFAD64 /* Build configuration list for PBXNativeTarget "SwiftSoup" */ = {
4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = {
isa = XCConfigurationList;
buildConfigurations = (
CE1E7DB9F3C040366A3496455A8B24BD /* Debug */,
30E1CB02BB5209911271753062F04733 /* Release */,
430C0A1F2A9CB52955BEC7CE69B7B629 /* Debug */,
597E092427DF423992C59C814880FE96 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
96487669C0D0E8C89F8460652C8D1EA6 /* Build configuration list for PBXNativeTarget "Pods-RSwitch" */ = {
63262DFBECA49AB0DB89612107EBDE33 /* Build configuration list for PBXNativeTarget "Just" */ = {
isa = XCConfigurationList;
buildConfigurations = (
28937119401B98869866251E32C12494 /* Debug */,
D7F84BF4AD9212BB6A99F6519EC528A6 /* Release */,
ABCA764BDF2BC6B0A9DA9B8EAB2149ED /* Debug */,
434E5D3509C83A8B996D60E6BEDA6C77 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;

6
Pods/Pods.xcodeproj/xcuserdata/hrbrmstr.xcuserdatad/xcschemes/SwiftSoup.xcscheme → Pods/Pods.xcodeproj/xcuserdata/hrbrmstr.xcuserdatad/xcschemes/Just.xcscheme

@ -14,9 +14,9 @@
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D89F48405DFAFEF441B4E758A15117A5"
BuildableName = "SwiftSoup.framework"
BlueprintName = "SwiftSoup"
BlueprintIdentifier = "CE86A5B255D405184FBF7B919824D937"
BuildableName = "Just.framework"
BlueprintName = "Just"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>

4
Pods/Pods.xcodeproj/xcuserdata/hrbrmstr.xcuserdatad/xcschemes/xcschememanagement.plist

@ -4,14 +4,14 @@
<dict>
<key>SchemeUserState</key>
<dict>
<key>Pods-RSwitch.xcscheme</key>
<key>Just.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>0</integer>
</dict>
<key>SwiftSoup.xcscheme</key>
<key>Pods-RSwitch.xcscheme</key>
<dict>
<key>isShown</key>
<false/>

21
Pods/SwiftSoup/LICENSE

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2016 Nabil Chatbi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

595
Pods/SwiftSoup/README.md

@ -1,595 +0,0 @@
<p align="center" >
<img src="https://raw.githubusercontent.com/scinfu/SwiftSoup/master/swifsoup.png" alt="SwiftSoup" title="SwiftSoup">
</p>
![Platform OS X | iOS | tvOS | watchOS | Linux](https://img.shields.io/badge/platform-Linux%20%7C%20OS%20X%20%7C%20iOS%20%7C%20tvOS%20%7C%20watchOS-orange.svg)
[![SPM compatible](https://img.shields.io/badge/SPM-compatible-4BC51D.svg?style=flat)](https://github.com/apple/swift-package-manager)
![🐧 linux: ready](https://img.shields.io/badge/%F0%9F%90%A7%20linux-ready-red.svg)
![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)
[![Build Status](https://travis-ci.org/scinfu/SwiftSoup.svg?branch=master)](https://travis-ci.org/scinfu/SwiftSoup)
[![Version](https://img.shields.io/cocoapods/v/SwiftSoup.svg?style=flat)](http://cocoapods.org/pods/SwiftSoup)
[![License](https://img.shields.io/cocoapods/l/SwiftSoup.svg?style=flat)](http://cocoapods.org/pods/SwiftSoup)
[![Twitter](https://img.shields.io/badge/twitter-@scinfu-blue.svg?style=flat)](http://twitter.com/scinfu)
`SwiftSoup` is a pure Swift library, cross-platform (macOS, iOS, tvOS, watchOS and Linux!), for working with real-world HTML. It provides a very convenient API for extracting and manipulating data, using the best of DOM, CSS, and jQuery-like methods.
`SwiftSoup` implements the WHATWG HTML5 specification, and parses HTML to the same DOM as modern browsers do.
* Scrape and parse HTML from a URL, file, or string
* Find and extract data, using DOM traversal or CSS selectors
* Manipulate the HTML elements, attributes, and text
* Clean user-submitted content against a safe white-list, to prevent XSS attacks
* Output tidy HTML
`SwiftSoup` is designed to deal with all varieties of HTML found in the wild; from pristine and validating, to invalid tag-soup; `SwiftSoup` will create a sensible parse tree.
## Swift
Swift 5 ```>=2.0.0```
Swift 4.2 ```1.7.4```
## Installation
### Cocoapods
SwiftSoup is available through [CocoaPods](http://cocoapods.org). To install
it, simply add the following line to your Podfile:
```ruby
pod 'SwiftSoup'
```
### Carthage
SwiftSoup is also available through [Carthage](https://github.com/Carthage/Carthage). To install
it, simply add the following line to your Cartfile:
```ruby
github "scinfu/SwiftSoup"
```
### Swift Package Manager
SwiftSoup is also available through [Swift Package Manager](https://github.com/apple/swift-package-manager).
To install it, simply add the dependency to your Package.Swift file:
```swift
...
dependencies: [
.package(url: "https://github.com/scinfu/SwiftSoup.git", from: "1.7.4"),
],
targets: [
.target( name: "YourTarget", dependencies: ["SwiftSoup"]),
]
...
```
## Try
### Try out the simple online CSS selectors site:
[SwiftSoup Test Site](https://swiftsoup.herokuapp.com/)
### Try out the example project opening Terminal and type:
```shell
pod try SwiftSoup
```
<p align="center" >
<img src="https://raw.githubusercontent.com/scinfu/SwiftSoup/master/Example/img1.png" alt="SwiftSoup" title="SwiftSoup">
<img src="https://raw.githubusercontent.com/scinfu/SwiftSoup/master/Example/img2.png" alt="SwiftSoup" title="SwiftSoup">
</p>
# To parse an HTML document:
```swift
do {
let html = "<html><head><title>First parse</title></head>"
+ "<body><p>Parsed HTML into a doc.</p></body></html>"
let doc: Document = try SwiftSoup.parse(html)
return try doc.text()
} catch Exception.Error(let type, let message) {
print(message)
} catch {
print("error")
}
```
* Unclosed tags (e.g. `<p>Lorem <p>Ipsum` parses to `<p>Lorem</p> <p>Ipsum</p>`)
* Implicit tags (e.g. a naked `<td>Table data</td>` is wrapped into a `<table><tr><td>...`)
* Reliably creating the document structure (`html` containing a `head` and `body`, and only appropriate elements within the head)
### The object model of a document
* Documents consist of Elements and TextNodes
* The inheritance chain is: `Document` extends `Element` extends `Node.TextNode` extends `Node`.
* An Element contains a list of children Nodes, and has one parent Element. They also have provide a filtered list of child Elements only.
# Extract attributes, text, and HTML from elements
### Problem
After parsing a document, and finding some elements, you'll want to get at the data inside those elements.
### Solution
- To get the value of an attribute, use the `Node.attr(_ String key)` method
- For the text on an element (and its combined children), use `Element.text()`
- For HTML, use `Element.html()`, or `Node.outerHtml()` as appropriate
```swift
do {
let html: String = "<p>An <a href='http://example.com/'><b>example</b></a> link.</p>";
let doc: Document = try SwiftSoup.parse(html)
let link: Element = try doc.select("a").first()!
let text: String = try doc.body()!.text(); // "An example link"
let linkHref: String = try link.attr("href"); // "http://example.com/"
let linkText: String = try link.text(); // "example""
let linkOuterH: String = try link.outerHtml(); // "<a href="http://example.com"><b>example</b></a>"
let linkInnerH: String = try link.html(); // "<b>example</b>"
} catch Exception.Error(let type, let message) {
print(message)
} catch {
print("error")
}
```
### Description
The methods above are the core of the element data access methods. There are additional others:
- `Element.id()`
- `Element.tagName()`
- `Element.className()` and `Element.hasClass(_ String className)`
All of these accessor methods have corresponding setter methods to change the data.
# Parse a document from a String
### Problem
You have HTML in a Swift String, and you want to parse that HTML to get at its contents, or to make sure it's well formed, or to modify it. The String may have come from user input, a file, or from the web.
### Solution
Use the static `SwiftSoup.parse(_ html: String)` method, or `SwiftSoup.parse(_ html: String, _ baseUri: String)`.
```swift
do {
let html = "<html><head><title>First parse</title></head>"
+ "<body><p>Parsed HTML into a doc.</p></body></html>"
let doc: Document = try SwiftSoup.parse(html)
return try doc.text()
} catch Exception.Error(let type, let message) {
print("")
} catch {
print("")
}
```
### Description
The `parse(_ html: String, _ baseUri: String)` method parses the input HTML into a new `Document`. The base URI argument is used to resolve relative URLs into absolute URLs, and should be set to the URL where the document was fetched from. If that's not applicable, or if you know the HTML has a base element, you can use the `parse(_ html: String)` method.
As long as you pass in a non-null string, you're guaranteed to have a successful, sensible parse, with a Document containing (at least) a `head` and a `body` element.
Once you have a `Document`, you can get at the data using the appropriate methods in `Document` and its supers `Element` and `Node`.
# Parsing a body fragment
### Problem
You have a fragment of body HTML (e.g. `div` containing a couple of p tags; as opposed to a full HTML document) that you want to parse. Perhaps it was provided by a user submitting a comment, or editing the body of a page in a CMS.
### Solution
Use the `SwiftSoup.parseBodyFragment(_ html: String)` method.
```swift
do {
let html: String = "<div><p>Lorem ipsum.</p>"
let doc: Document = try SwiftSoup.parseBodyFragment(html)
let body: Element? = doc.body()
} catch Exception.Error(let type, let message) {
print(message)
} catch {
print("error")
}
```
### Description
The `parseBodyFragment` method creates an empty shell document, and inserts the parsed HTML into the `body` element. If you used the normal `SwiftSoup(_ html: String)` method, you would generally get the same result, but explicitly treating the input as a body fragment ensures that any bozo HTML provided by the user is parsed into the `body` element.
The `Document.body()` method retrieves the element children of the document's `body` element; it is equivalent to `doc.getElementsByTag("body")`.
### Stay safe
If you are going to accept HTML input from a user, you need to be careful to avoid cross-site scripting attacks. See the documentation for the `Whitelist` based cleaner, and clean the input with `clean(String bodyHtml, Whitelist whitelist)`.
# Sanitize untrusted HTML (to prevent XSS)
### Problem
You want to allow untrusted users to supply HTML for output on your website (e.g. as comment submission). You need to clean this HTML to avoid [cross-site scripting](https://en.wikipedia.org/wiki/Cross-site_scripting) (XSS) attacks.
### Solution
Use the SwiftSoup HTML `Cleaner` with a configuration specified by a `Whitelist`.
```swift
do {
let unsafe: String = "<p><a href='http://example.com/' onclick='stealCookies()'>Link</a></p>"
let safe: String = try SwiftSoup.clean(unsafe, Whitelist.basic())!
// now: <p><a href="http://example.com/" rel="nofollow">Link</a></p>
} catch Exception.Error(let type, let message) {
print(message)
} catch {
print("error")
}
```
### Discussion
A cross-site scripting attack against your site can really ruin your day, not to mention your users'. Many sites avoid XSS attacks by not allowing HTML in user submitted content: they enforce plain text only, or use an alternative markup syntax like wiki-text or Markdown. These are seldom optimal solutions for the user, as they lower expressiveness, and force the user to learn a new syntax.
A better solution may be to use a rich text WYSIWYG editor (like [CKEditor](http://ckeditor.com) or [TinyMCE](https://www.tinymce.com)). These output HTML, and allow the user to work visually. However, their validation is done on the client side: you need to apply a server-side validation to clean up the input and ensure the HTML is safe to place on your site. Otherwise, an attacker can avoid the client-side Javascript validation and inject unsafe HMTL directly into your site
The SwiftSoup whitelist sanitizer works by parsing the input HTML (in a safe, sand-boxed environment), and then iterating through the parse tree and only allowing known-safe tags and attributes (and values) through into the cleaned output.
It does not use regular expressions, which are inappropriate for this task.
SwiftSoup provides a range of `Whitelist` configurations to suit most requirements; they can be modified if necessary, but take care.
The cleaner is useful not only for avoiding XSS, but also in limiting the range of elements the user can provide: you may be OK with textual `a`, `strong` elements, but not structural `div` or `table` elements.
### See also
- See the [XSS cheat sheet](http://ha.ckers.org/xss.html) and filter evasion guide, as an example of how regular-expression filters don't work, and why a safe whitelist parser-based sanitizer is the correct approach.
- See the `Cleaner` reference if you want to get a `Document` instead of a String return
- See the `Whitelist` reference for the different canned options, and to create a custom whitelist
- The [nofollow](https://en.wikipedia.org/wiki/Nofollow) link attribute
# Set attribute values
### Problem
You have a parsed document that you would like to update attribute values on, before saving it out to disk, or sending it on as a HTTP response.
### Solution
Use the attribute setter methods `Element.attr(_ key: String, _ value: String)`, and `Elements.attr(_ key: String, _ value: String)`.
If you need to modify the class attribute of an element, use the `Element.addClass(_ className: String)` and `Element.removeClass(_ className: String)` methods.
The `Elements` collection has bulk attribute and class methods. For example, to add a `rel="nofollow"` attribute to every `a` element inside a div:
```swift
do {
try doc.select("div.comments a").attr("rel", "nofollow")
} catch Exception.Error(let type, let message) {
print(message)
} catch {
print("error")
}
```
### Description
Like the other methods in `Element`, the attr methods return the current `Element` (or `Elements` when working on a collection from a select). This allows convenient method chaining:
```swift
do {
try doc.select("div.masthead").attr("title", "swiftsoup").addClass("round-box");
} catch Exception.Error(let type, let message) {
print(message)
} catch {
print("error")
}
```
# Set the HTML of an element
### Problem
You need to modify the HTML of an element.
### Solution
Use the HTML setter methods in `Element`:
```swift
do {
let doc: Document = try SwiftSoup.parse("<div>One</div><span>One</span>")
let div: Element = try doc.select("div").first()! // <div></div>
try div.html("<p>lorem ipsum</p>") // <div><p>lorem ipsum</p></div>
try div.prepend("<p>First</p>")
try div.append("<p>Last</p>")
print(div)
// now div is: <div><p>First</p><p>lorem ipsum</p><p>Last</p></div>
let span: Element = try doc.select("span").first()! // <span>One</span>
try span.wrap("<li><a href='http://example.com/'></a></li>")
print(doc)
// now: <li><a href="http://example.com/"><span>One</span></a></li>
} catch Exception.Error(let type, let message) {
print(message)
} catch {
print("error")
}
```
### Discussion
- `Element.html(_ html: String)` clears any existing inner HTML in an element, and replaces it with parsed HTML.
- `Element.prepend(_ first: String)` and `Element.append(_ last: String)` add HTML to the start or end of an element's inner HTML, respectively
- `Element.wrap(_ around: String)` wraps HTML around the outer HTML of an element.
### See also
You can also use the `Element.prependElement(_ tag: String)` and `Element.appendElement(_ tag: String)` methods to create new elements and insert them into the document flow as a child element.
# Setting the text content of elements
### Problem
You need to modify the text content of an HTML document.
# Solution
Use the text setter methods of `Element`:
```swift
do {
let doc: Document = try SwiftSoup.parse("")
let div: Element = try doc.select("div").first()! // <div></div>
try div.text("five > four") // <div>five &gt; four</div>
try div.prepend("First ")
try div.append(" Last")
// now: <div>First five &gt; four Last</div>
} catch Exception.Error(let type, let message) {
print(message)
} catch {
print("error")
}
```
### Discussion
The text setter methods mirror the [[HTML setter|Set the HTML of an element]] methods:
- `Element.text(_ text: String)` clears any existing inner HTML in an element, and replaces it with the supplied text.
- `Element.prepend(_ first: String)` and `Element.append(_ last: String)` add text nodes to the start or end of an element's inner HTML, respectively
The text should be supplied unencoded: characters like `<`, `>` etc will be treated as literals, not HTML.
# Use DOM methods to navigate a document
### Problem
You have a HTML document that you want to extract data from. You know generally the structure of the HTML document.
### Solution
Use the DOM-like methods available after parsing HTML into a `Document`.
```swift
do {
let html: String = "<a id=1 href='?foo=bar&mid&lt=true'>One</a> <a id=2 href='?foo=bar&lt;qux&lg=1'>Two</a>"
let els: Elements = try SwiftSoup.parse(html).select("a")
for link: Element in els.array() {
let linkHref: String = try link.attr("href")
let linkText: String = try link.text()
}
} catch Exception.Error(let type, let message) {
print(message)
} catch {
print("error")
}
```
### Description
Elements provide a range of DOM-like methods to find elements, and extract and manipulate their data. The DOM getters are contextual: called on a parent Document they find matching elements under the document; called on a child element they find elements under that child. In this way you can window in on the data you want.
### Finding elements
* `getElementById(_ id: String)`
* `getElementsByTag(_ tag:String)`
* `getElementsByClass(_ className: String)`
* `getElementsByAttribute(_ key: String)` (and related methods)
* Element siblings: `siblingElements()`, `firstElementSibling()`, `lastElementSibling()`, `nextElementSibling()`, `previousElementSibling()`
* Graph: `parent()`, `children()`, `child(_ index: Int)`
# Element data
* `attr(_ key: Strin)` to get and `attr(_ key: String, _ value: String)` to set attributes
* `attributes()` to get all attributes
* `id()`, `className()` and `classNames()`
* `text()` to get and `text(_ value: String)` to set the text content
* `html()` to get and `html(_ value: String)` to set the inner HTML content
* `outerHtml()` to get the outer HTML value
* `data()` to get data content (e.g. of script and style tags)
* `tag()` and `tagName()`
### Manipulating HTML and text
* `append(_ html: String)`, `prepend(html: String)`
* `appendText(text: String)`, `prependText(text: String)`
* `appendElement(tagName: String)`, `prependElement(tagName: String)`
* `html(_ value: String)`
# Use selector syntax to find elements
### Problem
You want to find or manipulate elements using a CSS or jQuery-like selector syntax.
### Solution
Use the `Element.select(_ selector: String)` and `Elements.select(_ selector: String)` methods:
```swift
do {
let doc: Document = try SwiftSoup.parse("...")
let links: Elements = try doc.select("a[href]") // a with href
let pngs: Elements = try doc.select("img[src$=.png]")
// img with src ending .png
let masthead: Element? = try doc.select("div.masthead").first()
// div with class=masthead
let resultLinks: Elements? = try doc.select("h3.r > a") // direct a after h3
} catch Exception.Error(let type, let message) {
print(message)
} catch {
print("error")
}
```
### Description
SwiftSoup elements support a [CSS](https://www.w3.org/TR/2009/PR-css3-selectors-20091215/) (or [jQuery](http://jquery.com)) like selector syntax to find matching elements, that allows very powerful and robust queries.
The `select` method is available in a `Document`, `Element`, or in `Elements`. It is contextual, so you can filter by selecting from a specific element, or by chaining select calls.
Select returns a list of `Elements` (as `Elements`), which provides a range of methods to extract and manipulate the results.
### Selector overview
* `tagname`: find elements by tag, e.g. `a`
* `ns|tag`: find elements by tag in a namespace, e.g. `fb|name` finds `<fb:name>` elements
* `#id`: find elements by ID, e.g. `#logo`
* `.class`: find elements by class name, e.g. `.masthead`
* `[attribute]`: elements with attribute, e.g. `[href]`
* `[^attr]`: elements with an attribute name prefix, e.g. `[^data-]` finds elements with HTML5 dataset attributes
* `[attr=value]`: elements with attribute value, e.g. `[width=500]` (also quotable, like `[data-name='launch sequence']`)
* `[attr^=value]`, `[attr$=value]`, `[attr*=value]`: elements with attributes that start with, end with, or contain the value, e.g. `[href*=/path/]`
* `[attr~=regex]`: elements with attribute values that match the regular expression; e.g. `img[src~=(?i)\.(png|jpe?g)]`
* `*`: all elements, e.g. `*`
### Selector combinations
* `el#id`: elements with ID, e.g. `div#logo`
* `el.class`: elements with class, e.g. `div.masthead`
* `el[attr]`: elements with attribute, e.g. `a[href]`
* Any combination, e.g. `a[href].highlight`
* Ancestor `child`: child elements that descend from ancestor, e.g. `.body p` finds `p` elements anywhere under a block with class "body"
* `parent > child`: child elements that descend directly from parent, e.g. `div.content > p` finds p elements; and `body > *` finds the direct children of the body tag
* `siblingA + siblingB`: finds sibling B element immediately preceded by sibling A, e.g. `div.head + div`
* `siblingA ~ siblingX`: finds sibling X element preceded by sibling A, e.g. `h1 ~ p`
* `el`, `el`, `el`: group multiple selectors, find unique elements that match any of the selectors; e.g. `div.masthead`, `div.logo`
### Pseudo selectors
* `:lt(n)`: find elements whose sibling index (i.e. its position in the DOM tree relative to its parent) is less than n; e.g. `td:lt(3)`
* `:gt(n)`: find elements whose sibling index is greater than n; e.g. `div p:gt(2)`
* `:eq(n)`: find elements whose sibling index is equal to n; e.g. `form input:eq(1)`
* `:has(seletor)`: find elements that contain elements matching the selector; e.g. `div:has(p)`
* `:not(selector)`: find elements that do not match the selector; e.g. `div:not(.logo)`
* `:contains(text)`: find elements that contain the given text. The search is case-insensitive; e.g. `p:contains(swiftsoup)`
* `:containsOwn(text)`: find elements that directly contain the given text
* `:matches(regex)`: find elements whose text matches the specified regular expression; e.g. `div:matches((?i)login)`
* `:matchesOwn(regex)`: find elements whose own text matches the specified regular expression
* Note that the above indexed pseudo-selectors are 0-based, that is, the first element is at index 0, the second at 1, etc
# Examples
## To parse an HTML document from String:
```swift
let html = "<html><head><title>First parse</title></head><body><p>Parsed HTML into a doc.</p></body></html>"
guard let doc: Document = try? SwiftSoup.parse(html) else { return }
```
## Get all text nodes:
```swift
guard let elements = try? doc.getAllElements() else { return html }
for element in elements {
for textNode in element.textNodes() {
[...]
}
}
```
## Set CSS using SwiftSoup:
```swift
try doc.head()?.append("<style>html {font-size: 2em}</style>")
```
## Get HTML value
```swift
let html = "<div class=\"container-fluid\">"
+ "<div class=\"panel panel-default \">"
+ "<div class=\"panel-body\">"
+ "<form id=\"coupon_checkout\" action=\"http://uat.all.com.my/checkout/couponcode\" method=\"post\">"
+ "<input type=\"hidden\" name=\"transaction_id\" value=\"4245\">"
+ "<input type=\"hidden\" name=\"lang\" value=\"EN\">"
+ "<input type=\"hidden\" name=\"devicetype\" value=\"\">"
+ "<div class=\"input-group\">"
+ "<input type=\"text\" class=\"form-control\" id=\"coupon_code\" name=\"coupon\" placeholder=\"Coupon Code\">"
+ "<span class=\"input-group-btn\">"
+ "<button class=\"btn btn-primary\" type=\"submit\">Enter Code</button>"
+ "</span>"
+ "</div>"
+ "</form>"
+ "</div>"
+ "</div>"
guard let doc: Document = try? SwiftSoup.parse(html) else { return } // parse html
let elements = try doc.select("[name=transaction_id]") // query
let transaction_id = try elements.get(0) // select first element
let value = try transaction_id.val() // get value
print(value) // 4245
```
## How to remove all the html from a string
```swift
guard let doc: Document = try? SwiftSoup.parse(html) else { return } // parse html
guard let txt = try? doc.text() else { return }
print(txt)
```
## How to get and update XML values
```swift
let xml = "<?xml version='1' encoding='UTF-8' something='else'?><val>One</val>"
guard let doc = try? SwiftSoup.parse(xml, "", Parser.xmlParser()) else { return }
guard let element = try? doc.getElementsByTag("val").first() // Find first element
element.text("NewValue") // Edit Value
let valueString = element.text() // "NewValue"
```
## How to get all `<img src>`
```swift
do {
let doc: Document = try SwiftSoup.parse(html)
let srcs: Elements = try doc.select("img[src]")
let srcsStringArray: [String?] = srcs.array().map { try? $0.attr("src").description }
// do something with srcsStringArray
} catch Exception.Error(_, let message) {
print(message)
} catch {
print("error")
}
```
## Get all `href` of `<a>`
```swift
let html = "<a id=1 href='?foo=bar&mid&lt=true'>One</a> <a id=2 href='?foo=bar&lt;qux&lg=1'>Two</a>"
guard let els: Elements = try? SwiftSoup.parse(html).select("a") else { return }
for element: Element in els.array() {
print(try? element.attr("href"))
}
```
Output:
```
"?foo=bar&mid&lt=true"
"?foo=bar<qux&lg=1"
```
## Escape and Enescape
```swift
let text = "Hello &<> Å å π 新 there ¾ © »"
print(Entities.escape(text))
print(Entities.unescape(text))
print(Entities.escape(text, OutputSettings().encoder(String.Encoding.ascii).escapeMode(Entities.EscapeMode.base)))
print(Entities.escape(text, OutputSettings().charset(String.Encoding.ascii).escapeMode(Entities.EscapeMode.extended)))
print(Entities.escape(text, OutputSettings().charset(String.Encoding.ascii).escapeMode(Entities.EscapeMode.xhtml)))
print(Entities.escape(text, OutputSettings().charset(String.Encoding.utf8).escapeMode(Entities.EscapeMode.extended)))
print(Entities.escape(text, OutputSettings().charset(String.Encoding.utf8).escapeMode(Entities.EscapeMode.xhtml)))
```
Output:
```
"Hello &amp;&lt;&gt; Å å π 新 there ¾ © »"
"Hello &<> Å å π 新 there ¾ © »"
"Hello &amp;&lt;&gt; &Aring; &aring; &#x3c0; &#x65b0; there &frac34; &copy; &raquo;"
"Hello &amp;&lt;&gt; &angst; &aring; &pi; &#x65b0; there &frac34; &copy; &raquo;"
"Hello &amp;&lt;&gt; &#xc5; &#xe5; &#x3c0; &#x65b0; there &#xbe; &#xa9; &#xbb;"
"Hello &amp;&lt;&gt; Å å π 新 there ¾ © »"
"Hello &amp;&lt;&gt; Å å π 新 there ¾ © »"
```
## Author
Nabil Chatbi, scinfu@gmail.com
## Note
SwiftSoup was ported to Swift from Java [Jsoup](https://jsoup.org/) library.
## License
SwiftSoup is available under the MIT license. See the LICENSE file for more info.

21
Pods/SwiftSoup/Sources/ArrayExt.swift

@ -1,21 +0,0 @@
//
// ArrayExt.swift
// SwifSoup
//
// Created by Nabil Chatbi on 05/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
extension Array where Element : Equatable {
func lastIndexOf(_ e: Element) -> Int {
for pos in (0..<self.count).reversed() {
let next = self[pos]
if (next == e) {
return pos
}
}
return -1
}
}

148
Pods/SwiftSoup/Sources/Attribute.swift

@ -1,148 +0,0 @@
//
// Attribute.swift
// SwifSoup
//
// Created by Nabil Chatbi on 29/09/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
open class Attribute {
/// The element type of a dictionary: a tuple containing an individual
/// key-value pair.
static let booleanAttributes: [String] = [
"allowfullscreen", "async", "autofocus", "checked", "compact", "declare", "default", "defer", "disabled",
"formnovalidate", "hidden", "inert", "ismap", "itemscope", "multiple", "muted", "nohref", "noresize",
"noshade", "novalidate", "nowrap", "open", "readonly", "required", "reversed", "seamless", "selected",
"sortable", "truespeed", "typemustmatch"
]
var key: String
var value: String
public init(key: String, value: String) throws {
try Validate.notEmpty(string: key)
self.key = key.trim()
self.value = value
}
/**
Get the attribute key.
@return the attribute key
*/
open func getKey() -> String {
return key
}
/**
Set the attribute key; case is preserved.
@param key the new key; must not be null
*/
open func setKey(key: String) throws {
try Validate.notEmpty(string: key)
self.key = key.trim()
}
/**
Get the attribute value.
@return the attribute value
*/
open func getValue() -> String {
return value
}
/**
Set the attribute value.
@param value the new attribute value; must not be null
*/
@discardableResult
open func setValue(value: String) -> String {
let old = self.value
self.value = value
return old
}
/**
Get the HTML representation of this attribute; e.g. {@code href="index.html"}.
@return HTML
*/
public func html() -> String {
let accum = StringBuilder()
html(accum: accum, out: (Document("")).outputSettings())
return accum.toString()
}
public func html(accum: StringBuilder, out: OutputSettings ) {
accum.append(key)
if (!shouldCollapseAttribute(out: out)) {
accum.append("=\"")
Entities.escape(accum, value, out, true, false, false)
accum.append("\"")
}
}
/**
Get the string representation of this attribute, implemented as {@link #html()}.
@return string
*/
open func toString() -> String {
return html()
}
/**
* Create a new Attribute from an unencoded key and a HTML attribute encoded value.
* @param unencodedKey assumes the key is not encoded, as can be only run of simple \w chars.
* @param encodedValue HTML attribute encoded value
* @return attribute
*/
public static func createFromEncoded(unencodedKey: String, encodedValue: String) throws ->Attribute {
let value = try Entities.unescape(string: encodedValue, strict: true)
return try Attribute(key: unencodedKey, value: value)
}
public func isDataAttribute() -> Bool {
return key.startsWith(Attributes.dataPrefix) && key.count > Attributes.dataPrefix.count
}
/**
* Collapsible if it's a boolean attribute and value is empty or same as name
*
* @param out Outputsettings
* @return Returns whether collapsible or not
*/
public final func shouldCollapseAttribute(out: OutputSettings) -> Bool {
return ("" == value || value.equalsIgnoreCase(string: key))
&& out.syntax() == OutputSettings.Syntax.html
&& isBooleanAttribute()
}
public func isBooleanAttribute() -> Bool {
return Attribute.booleanAttributes.contains(key)
}
public func hashCode() -> Int {
var result = key.hashValue
result = 31 * result + value.hashValue
return result
}
public func clone() -> Attribute {
do {
return try Attribute(key: key, value: value)
} catch Exception.Error( _, let msg) {
print(msg)
} catch {
}
return try! Attribute(key: "", value: "")
}
}
extension Attribute: Equatable {
static public func == (lhs: Attribute, rhs: Attribute) -> Bool {
return lhs.value == rhs.value && lhs.key == rhs.key
}
}

264
Pods/SwiftSoup/Sources/Attributes.swift

@ -1,264 +0,0 @@
//
// Attributes.swift
// SwifSoup
//
// Created by Nabil Chatbi on 29/09/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
* The attributes of an Element.
* <p>
* Attributes are treated as a map: there can be only one value associated with an attribute key/name.
* </p>
* <p>
* Attribute name and value comparisons are <b>case sensitive</b>. By default for HTML, attribute names are
* normalized to lower-case on parsing. That means you should use lower-case strings when referring to attributes by
* name.
* </p>
*
*
*/
open class Attributes: NSCopying {
public static var dataPrefix: String = "data-"
var attributes: OrderedDictionary<String, Attribute> = OrderedDictionary<String, Attribute>()
// linked hash map to preserve insertion order.
// null be default as so many elements have no attributes -- saves a good chunk of memory
public init() {}
/**
Get an attribute value by key.
@param key the (case-sensitive) attribute key
@return the attribute value if set; or empty string if not set.
@see #hasKey(String)
*/
open func get(key: String) -> String {
let attr: Attribute? = attributes.get(key: key)
return attr != nil ? attr!.getValue() : ""
}
/**
* Get an attribute's value by case-insensitive key
* @param key the attribute name
* @return the first matching attribute value if set; or empty string if not set.
*/
open func getIgnoreCase(key: String )throws -> String {
try Validate.notEmpty(string: key)
for attrKey in (attributes.keySet()) {
if attrKey.equalsIgnoreCase(string: key) {
return attributes.get(key: attrKey)!.getValue()
}
}
return ""
}
/**
Set a new attribute, or replace an existing one by key.
@param key attribute key
@param value attribute value
*/
open func put(_ key: String, _ value: String) throws {
let attr = try Attribute(key: key, value: value)
put(attribute: attr)
}
/**
Set a new boolean attribute, remove attribute if value is false.
@param key attribute key
@param value attribute value
*/
open func put(_ key: String, _ value: Bool) throws {
if (value) {
try put(attribute: BooleanAttribute(key: key))
} else {
try remove(key: key)
}
}
/**
Set a new attribute, or replace an existing one by key.
@param attribute attribute
*/
open func put(attribute: Attribute) {
attributes.put(value: attribute, forKey: attribute.getKey())
}
/**
Remove an attribute by key. <b>Case sensitive.</b>
@param key attribute key to remove
*/
open func remove(key: String)throws {
try Validate.notEmpty(string: key)
attributes.remove(key: key)
}
/**
Remove an attribute by key. <b>Case insensitive.</b>
@param key attribute key to remove
*/
open func removeIgnoreCase(key: String ) throws {
try Validate.notEmpty(string: key)
for attrKey in attributes.keySet() {
if (attrKey.equalsIgnoreCase(string: key)) {
attributes.remove(key: attrKey)
}
}
}
/**
Tests if these attributes contain an attribute with this key.
@param key case-sensitive key to check for
@return true if key exists, false otherwise
*/
open func hasKey(key: String) -> Bool {
return attributes.containsKey(key: key)
}
/**
Tests if these attributes contain an attribute with this key.
@param key key to check for
@return true if key exists, false otherwise
*/
open func hasKeyIgnoreCase(key: String) -> Bool {
for attrKey in attributes.keySet() {
if (attrKey.equalsIgnoreCase(string: key)) {
return true
}
}
return false
}
/**
Get the number of attributes in this set.
@return size
*/
open func size() -> Int {
return attributes.count//TODO: check retyrn right size
}
/**
Add all the attributes from the incoming set to this set.
@param incoming attributes to add to these attributes.
*/
open func addAll(incoming: Attributes?) {
guard let incoming = incoming else {
return
}
if (incoming.size() == 0) {
return
}
attributes.putAll(all: incoming.attributes)
}
// open func iterator() -> IndexingIterator<Array<Attribute>> {
// if (attributes.isEmpty) {
// let args: [Attribute] = []
// return args.makeIterator()
// }
// return attributes.orderedValues.makeIterator()
// }
/**
Get the attributes as a List, for iteration. Do not modify the keys of the attributes via this view, as changes
to keys will not be recognised in the containing set.
@return an view of the attributes as a List.
*/
open func asList() -> Array<Attribute> {
var list: Array<Attribute> = Array(/*attributes.size()*/)
for entry in attributes.orderedValues {
list.append(entry)
}
return list
}
/**
* Retrieves a filtered view of attributes that are HTML5 custom data attributes; that is, attributes with keys
* starting with {@code data-}.
* @return map of custom data attributes.
*/
//Map<String, String>
open func dataset() -> Dictionary<String, String> {
var dataset = Dictionary<String, String>()
for attribute in attributes {
let attr = attribute.1
if(attr.isDataAttribute()) {
let key = attr.getKey().substring(Attributes.dataPrefix.count)
dataset[key] = attribute.1.getValue()
}
}
return dataset
}
/**
Get the HTML representation of these attributes.
@return HTML
@throws SerializationException if the HTML representation of the attributes cannot be constructed.
*/
open func html()throws -> String {
let accum = StringBuilder()
try html(accum: accum, out: Document("").outputSettings()) // output settings a bit funky, but this html() seldom used
return accum.toString()
}
public func html(accum: StringBuilder, out: OutputSettings ) throws {
for attribute in attributes.orderedValues {
accum.append(" ")
attribute.html(accum: accum, out: out)
}
}
open func toString()throws -> String {
return try html()
}
/**
* Checks if these attributes are equal to another set of attributes, by comparing the two sets
* @param o attributes to compare with
* @return if both sets of attributes have the same content
*/
open func equals(o: AnyObject?) -> Bool {
if(o == nil) {return false}
if (self === o.self) {return true}
guard let that: Attributes = o as? Attributes else {return false}
return (attributes == that.attributes)
}
/**
* Calculates the hashcode of these attributes, by iterating all attributes and summing their hashcodes.
* @return calculated hashcode
*/
open func hashCode() -> Int {
return attributes.hashCode()
}
public func copy(with zone: NSZone? = nil) -> Any {
let clone = Attributes()
clone.attributes = attributes.clone()
return clone
}
open func clone() -> Attributes {
return self.copy() as! Attributes
}
fileprivate static func dataKey(key: String) -> String {
return dataPrefix + key
}
}
extension Attributes: Sequence {
public func makeIterator() -> AnyIterator<Attribute> {
var list = attributes.orderedValues
return AnyIterator {
return list.count > 0 ? list.removeFirst() : nil
}
}
}

26
Pods/SwiftSoup/Sources/BooleanAttribute.swift

@ -1,26 +0,0 @@
//
// BooleanAttribute.swift
// SwifSoup
//
// Created by Nabil Chatbi on 29/09/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
* A boolean attribute that is written out without any value.
*/
open class BooleanAttribute: Attribute {
/**
* Create a new boolean attribute from unencoded (raw) key.
* @param key attribute key
*/
init(key: String) throws {
try super.init(key: key, value: "")
}
override public func isBooleanAttribute() -> Bool {
return true
}
}

81
Pods/SwiftSoup/Sources/CharacterExt.swift

@ -1,81 +0,0 @@
//
// CharacterExt.swift
// SwifSoup
//
// Created by Nabil Chatbi on 08/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
extension Character {
public static let space: Character = " "
public static let BackslashT: Character = "\t"
public static let BackslashN: Character = "\n"
public static let BackslashF: Character = Character(UnicodeScalar(12))
public static let BackslashR: Character = "\r"
public static let BackshashRBackslashN: Character = "\r\n"
//http://www.unicode.org/glossary/#supplementary_code_point
public static let MIN_SUPPLEMENTARY_CODE_POINT: UInt32 = 0x010000
/// True for any space character, and the control characters \t, \n, \r, \f, \v.
var isWhitespace: Bool {
switch self {
case Character.space, Character.BackslashT, Character.BackslashN, Character.BackslashF, Character.BackslashR: return true
case Character.BackshashRBackslashN: return true
default: return false
}
}
/// `true` if `self` normalized contains a single code unit that is in the category of Decimal Numbers.
var isDigit: Bool {
return isMemberOfCharacterSet(CharacterSet.decimalDigits)
}
/// Lowercase `self`.
var lowercase: Character {
let str = String(self).lowercased()
return str[str.startIndex]
}
/// Return `true` if `self` normalized contains a single code unit that is a member of the supplied character set.
///
/// - parameter set: The `NSCharacterSet` used to test for membership.
/// - returns: `true` if `self` normalized contains a single code unit that is a member of the supplied character set.
func isMemberOfCharacterSet(_ set: CharacterSet) -> Bool {
let normalized = String(self).precomposedStringWithCanonicalMapping
let unicodes = normalized.unicodeScalars
guard unicodes.count == 1 else { return false }
return set.contains(UnicodeScalar(unicodes.first!.value)!)
}
static func convertFromIntegerLiteral(value: IntegerLiteralType) -> Character {
return Character(UnicodeScalar(value)!)
}
static func isLetter(_ char: Character) -> Bool {
return char.isLetter()
}
func isLetter() -> Bool {
return self.isMemberOfCharacterSet(CharacterSet.letters)
}
static func isLetterOrDigit(_ char: Character) -> Bool {
return char.isLetterOrDigit()
}
func isLetterOrDigit() -> Bool {
if(self.isLetter()) {return true}
return self.isDigit
}
}

463
Pods/SwiftSoup/Sources/CharacterReader.swift

@ -1,463 +0,0 @@
//
// CharacterReader.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 10/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
CharacterReader consumes tokens off a string. To replace the old TokenQueue.
*/
public final class CharacterReader {
private static let empty = ""
public static let EOF: UnicodeScalar = "\u{FFFF}"//65535
private let input: [UnicodeScalar]
private let length: Int
private var pos: Int = 0
private var mark: Int = 0
//private let stringCache: Array<String?> // holds reused strings in this doc, to lessen garbage
public init(_ input: String) {
self.input = Array(input.unicodeScalars)
self.length = self.input.count
//stringCache = Array(repeating:nil, count:512)
}
public func getPos() -> Int {
return self.pos
}
public func isEmpty() -> Bool {
return pos >= length
}
public func current() -> UnicodeScalar {
return (pos >= length) ? CharacterReader.EOF : input[pos]
}
@discardableResult
public func consume() -> UnicodeScalar {
let val = (pos >= length) ? CharacterReader.EOF : input[pos]
pos += 1
return val
}
public func unconsume() {
pos -= 1
}
public func advance() {
pos += 1
}
public func markPos() {
mark = pos
}
public func rewindToMark() {
pos = mark
}
public func consumeAsString() -> String {
let p = pos
pos+=1
return String(input[p])
//return String(input, pos+=1, 1)
}
/**
* Returns the number of characters between the current position and the next instance of the input char
* @param c scan target
* @return offset between current position and next instance of target. -1 if not found.
*/
public func nextIndexOf(_ c: UnicodeScalar) -> Int {
// doesn't handle scanning for surrogates
for i in pos..<length {
if (c == input[i]) {
return i - pos
}
}
return -1
}
/**
* Returns the number of characters between the current position and the next instance of the input sequence
*
* @param seq scan target
* @return offset between current position and next instance of target. -1 if not found.
*/
public func nextIndexOf(_ seq: String) -> Int {
// doesn't handle scanning for surrogates
if(seq.isEmpty) {return -1}
let startChar: UnicodeScalar = seq.unicodeScalar(0)
for var offset in pos..<length {
// scan to first instance of startchar:
if (startChar != input[offset]) {
offset+=1
while(offset < length && startChar != input[offset]) { offset+=1 }
}
var i = offset + 1
let last = i + seq.unicodeScalars.count-1
if (offset < length && last <= length) {
var j = 1
while i < last && seq.unicodeScalar(j) == input[i] {
j+=1
i+=1
}
// found full sequence
if (i == last) {
return offset - pos
}
}
}
return -1
}
public func consumeTo(_ c: UnicodeScalar) -> String {
let offset = nextIndexOf(c)
if (offset != -1) {
let consumed = cacheString(pos, offset)
pos += offset
return consumed
} else {
return consumeToEnd()
}
}
public func consumeTo(_ seq: String) -> String {
let offset = nextIndexOf(seq)
if (offset != -1) {
let consumed = cacheString(pos, offset)
pos += offset
return consumed
} else {
return consumeToEnd()
}
}
public func consumeToAny(_ chars: UnicodeScalar...) -> String {
return consumeToAny(chars)
}
public func consumeToAny(_ chars: [UnicodeScalar]) -> String {
let start: Int = pos
let remaining: Int = length
let val = input
OUTER: while (pos < remaining) {
if chars.contains(val[pos]) {
break OUTER
}
// for c in chars {
// if (val[pos] == c){
// break OUTER
// }
// }
pos += 1
}
return pos > start ? cacheString(start, pos-start) : CharacterReader.empty
}
public func consumeToAnySorted(_ chars: UnicodeScalar...) -> String {
return consumeToAnySorted(chars)
}
public func consumeToAnySorted(_ chars: [UnicodeScalar]) -> String {
let start = pos
let remaining = length
let val = input
while (pos < remaining) {
if chars.contains(val[pos]) {
break
}
pos += 1
}
return pos > start ? cacheString(start, pos-start) : CharacterReader.empty
}
public func consumeData() -> String {
// &, <, null
let start = pos
let remaining = length
let val = input
while (pos < remaining) {
let c: UnicodeScalar = val[pos]
if (c == UnicodeScalar.Ampersand || c == UnicodeScalar.LessThan || c == TokeniserStateVars.nullScalr) {
break
}
pos += 1
}
return pos > start ? cacheString(start, pos-start) : CharacterReader.empty
}
public func consumeTagName() -> String {
// '\t', '\n', '\r', '\f', ' ', '/', '>', nullChar
let start = pos
let remaining = length
let val = input
while (pos < remaining) {
let c: UnicodeScalar = val[pos]
if (c == UnicodeScalar.BackslashT || c == UnicodeScalar.BackslashN || c == UnicodeScalar.BackslashR || c == UnicodeScalar.BackslashF || c == UnicodeScalar.Space || c == UnicodeScalar.Slash || c == UnicodeScalar.GreaterThan || c == TokeniserStateVars.nullScalr) {
break
}
pos += 1
}
return pos > start ? cacheString(start, pos-start) : CharacterReader.empty
}
public func consumeToEnd() -> String {
let data = cacheString(pos, length-pos)
pos = length
return data
}
public func consumeLetterSequence() -> String {
let start = pos
while (pos < length) {
let c: UnicodeScalar = input[pos]
if ((c >= "A" && c <= "Z") || (c >= "a" && c <= "z") || c.isMemberOfCharacterSet(CharacterSet.letters)) {
pos += 1
} else {
break
}
}
return cacheString(start, pos - start)
}
public func consumeLetterThenDigitSequence() -> String {
let start = pos
while (pos < length) {
let c = input[pos]
if ((c >= "A" && c <= "Z") || (c >= "a" && c <= "z") || c.isMemberOfCharacterSet(CharacterSet.letters)) {
pos += 1
} else {
break
}
}
while (!isEmpty()) {
let c = input[pos]
if (c >= "0" && c <= "9") {
pos += 1
} else {
break
}
}
return cacheString(start, pos - start)
}
public func consumeHexSequence() -> String {
let start = pos
while (pos < length) {
let c = input[pos]
if ((c >= "0" && c <= "9") || (c >= "A" && c <= "F") || (c >= "a" && c <= "f")) {
pos+=1
} else {
break
}
}
return cacheString(start, pos - start)
}
public func consumeDigitSequence() -> String {
let start = pos
while (pos < length) {
let c = input[pos]
if (c >= "0" && c <= "9") {
pos+=1
} else {
break
}
}
return cacheString(start, pos - start)
}
public func matches(_ c: UnicodeScalar) -> Bool {
return !isEmpty() && input[pos] == c
}
public func matches(_ seq: String) -> Bool {
let scanLength = seq.unicodeScalars.count
if (scanLength > length - pos) {
return false
}
for offset in 0..<scanLength {
if (seq.unicodeScalar(offset) != input[pos+offset]) {
return false
}
}
return true
}
public func matchesIgnoreCase(_ seq: String ) -> Bool {
let scanLength = seq.unicodeScalars.count
if(scanLength == 0) {
return false
}
if (scanLength > length - pos) {
return false
}
for offset in 0..<scanLength {
let upScan: UnicodeScalar = seq.unicodeScalar(offset).uppercase
let upTarget: UnicodeScalar = input[pos+offset].uppercase
if (upScan != upTarget) {
return false
}
}
return true
}
public func matchesAny(_ seq: UnicodeScalar...) -> Bool {
if (isEmpty()) {
return false
}
let c: UnicodeScalar = input[pos]
for seek in seq {
if (seek == c) {
return true
}
}
return false
}
public func matchesAnySorted(_ seq: [UnicodeScalar]) -> Bool {
return !isEmpty() && seq.contains(input[pos])
}
public func matchesLetter() -> Bool {
if (isEmpty()) {
return false
}
let c = input[pos]
return (c >= "A" && c <= "Z") || (c >= "a" && c <= "z") || c.isMemberOfCharacterSet(CharacterSet.letters)
}
public func matchesDigit() -> Bool {
if (isEmpty()) {
return false
}
let c = input[pos]
return (c >= "0" && c <= "9")
}
@discardableResult
public func matchConsume(_ seq: String) -> Bool {
if (matches(seq)) {
pos += seq.unicodeScalars.count
return true
} else {
return false
}
}
@discardableResult
public func matchConsumeIgnoreCase(_ seq: String) -> Bool {
if (matchesIgnoreCase(seq)) {
pos += seq.unicodeScalars.count
return true
} else {
return false
}
}
public func containsIgnoreCase(_ seq: String ) -> Bool {
// used to check presence of </title>, </style>. only finds consistent case.
let loScan = seq.lowercased(with: Locale(identifier: "en"))
let hiScan = seq.uppercased(with: Locale(identifier: "eng"))
return (nextIndexOf(loScan) > -1) || (nextIndexOf(hiScan) > -1)
}
public func toString() -> String {
return String(input[pos..<length])
//return String.unicodescalars(Array(input[pos..<length]))
//return input.string(pos, length - pos)
}
/**
* Caches short strings, as a flywheel pattern, to reduce GC load. Just for this doc, to prevent leaks.
* <p />
* Simplistic, and on hash collisions just falls back to creating a new string, vs a full HashMap with Entry list.
* That saves both having to create objects as hash keys, and running through the entry list, at the expense of
* some more duplicates.
*/
private func cacheString(_ start: Int, _ count: Int) -> String {
return String(input[start..<start+count])
// Too Slow
// var cache: [String?] = stringCache
//
// // limit (no cache):
// if (count > CharacterReader.maxCacheLen) {
// return String(val[start..<start+count].flatMap { Character($0) })
// }
//
// // calculate hash:
// var hash: Int = 0
// var offset = start
// for _ in 0..<count {
// let ch = val[offset].value
// hash = Int.addWithOverflow(Int.multiplyWithOverflow(31, hash).0, Int(ch)).0
// offset+=1
// }
//
// // get from cache
// hash = abs(hash)
// let i = hash % cache.count
// let index: Int = abs(i) //Int(hash & Int(cache.count) - 1)
// var cached = cache[index]
//
// if (cached == nil) { // miss, add
// cached = String(val[start..<start+count].flatMap { Character($0) })
// //cached = val.string(start, count)
// cache[Int(index)] = cached
// } else { // hashcode hit, check equality
// if (rangeEquals(start, count, cached!)) { // hit
// return cached!
// } else { // hashcode conflict
// cached = String(val[start..<start+count].flatMap { Character($0) })
// //cached = val.string(start, count)
// cache[index] = cached // update the cache, as recently used strings are more likely to show up again
// }
// }
// return cached!
}
// /**
// * Check if the value of the provided range equals the string.
// */
// public func rangeEquals(_ start: Int, _ count: Int, _ cached: String) -> Bool {
// if (count == cached.unicodeScalars.count) {
// var count = count
// let one = input
// var i = start
// var j = 0
// while (count != 0) {
// count -= 1
// if (one[i] != cached.unicodeScalar(j) ) {
// return false
// }
// j += 1
// i += 1
// }
// return true
// }
// return false
// }
}
extension CharacterReader: CustomDebugStringConvertible {
public var debugDescription: String {
return self.toString()
}
}

150
Pods/SwiftSoup/Sources/Cleaner.swift

@ -1,150 +0,0 @@
//
// Cleaner.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 15/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
open class Cleaner {
fileprivate let whitelist: Whitelist
/**
Create a new cleaner, that sanitizes documents using the supplied whitelist.
@param whitelist white-list to clean with
*/
public init(_ whitelist: Whitelist) {
self.whitelist = whitelist
}
/**
Creates a new, clean document, from the original dirty document, containing only elements allowed by the whitelist.
The original document is not modified. Only elements from the dirt document's <code>body</code> are used.
@param dirtyDocument Untrusted base document to clean.
@return cleaned document.
*/
public func clean(_ dirtyDocument: Document)throws->Document {
//Validate.notNull(dirtyDocument)
let clean: Document = Document.createShell(dirtyDocument.getBaseUri())
if (dirtyDocument.body() != nil && clean.body() != nil) // frameset documents won't have a body. the clean doc will have empty body.
{
try copySafeNodes(dirtyDocument.body()!, clean.body()!)
}
return clean
}
/**
Determines if the input document is valid, against the whitelist. It is considered valid if all the tags and attributes
in the input HTML are allowed by the whitelist.
<p>
This method can be used as a validator for user input forms. An invalid document will still be cleaned successfully
using the {@link #clean(Document)} document. If using as a validator, it is recommended to still clean the document
to ensure enforced attributes are set correctly, and that the output is tidied.
</p>
@param dirtyDocument document to test
@return true if no tags or attributes need to be removed; false if they do
*/
public func isValid(_ dirtyDocument: Document)throws->Bool {
//Validate.notNull(dirtyDocument)
let clean: Document = Document.createShell(dirtyDocument.getBaseUri())
let numDiscarded: Int = try copySafeNodes(dirtyDocument.body()!, clean.body()!)
return numDiscarded == 0
}
@discardableResult
fileprivate func copySafeNodes(_ source: Element, _ dest: Element)throws->Int {
let cleaningVisitor: Cleaner.CleaningVisitor = Cleaner.CleaningVisitor(source, dest, self)
let traversor: NodeTraversor = NodeTraversor(cleaningVisitor)
try traversor.traverse(source)
return cleaningVisitor.numDiscarded
}
fileprivate func createSafeElement(_ sourceEl: Element)throws->ElementMeta {
let sourceTag: String = sourceEl.tagName()
let destAttrs: Attributes = Attributes()
let dest: Element = try Element(Tag.valueOf(sourceTag), sourceEl.getBaseUri(), destAttrs)
var numDiscarded: Int = 0
if let sourceAttrs = sourceEl.getAttributes() {
for sourceAttr: Attribute in sourceAttrs {
if (try whitelist.isSafeAttribute(sourceTag, sourceEl, sourceAttr)) {
destAttrs.put(attribute: sourceAttr)
} else {
numDiscarded+=1
}
}
}
let enforcedAttrs: Attributes = try whitelist.getEnforcedAttributes(sourceTag)
destAttrs.addAll(incoming: enforcedAttrs)
return ElementMeta(dest, numDiscarded)
}
}
extension Cleaner {
fileprivate final class CleaningVisitor: NodeVisitor {
var numDiscarded: Int = 0
let root: Element
var destination: Element? // current element to append nodes to
private var cleaner: Cleaner
public init(_ root: Element, _ destination: Element, _ cleaner: Cleaner) {
self.root = root
self.destination = destination
self.cleaner = cleaner
}
public func head(_ source: Node, _ depth: Int)throws {
if let sourceEl = (source as? Element) {
if (cleaner.whitelist.isSafeTag(sourceEl.tagName())) { // safe, clone and copy safe attrs
let meta: Cleaner.ElementMeta = try cleaner.createSafeElement(sourceEl)
let destChild: Element = meta.el
try destination?.appendChild(destChild)
numDiscarded += meta.numAttribsDiscarded
destination = destChild
} else if (source != root) { // not a safe tag, so don't add. don't count root against discarded.
numDiscarded+=1
}
} else if let sourceText = (source as? TextNode) {
let destText: TextNode = TextNode(sourceText.getWholeText(), source.getBaseUri())
try destination?.appendChild(destText)
} else if let sourceData = (source as? DataNode) {
if sourceData.parent() != nil && cleaner.whitelist.isSafeTag(sourceData.parent()!.nodeName()) {
//let sourceData: DataNode = (DataNode) source
let destData: DataNode = DataNode(sourceData.getWholeData(), source.getBaseUri())
try destination?.appendChild(destData)
} else {
numDiscarded+=1
}
} else { // else, we don't care about comments, xml proc instructions, etc
numDiscarded+=1
}
}
public func tail(_ source: Node, _ depth: Int)throws {
if let x = (source as? Element) {
if cleaner.whitelist.isSafeTag(x.nodeName()) {
// would have descended, so pop destination stack
destination = destination?.parent()
}
}
}
}
}
extension Cleaner {
fileprivate struct ElementMeta {
let el: Element
let numAttribsDiscarded: Int
init(_ el: Element, _ numAttribsDiscarded: Int) {
self.el = el
self.numAttribsDiscarded = numAttribsDiscarded
}
}
}

59
Pods/SwiftSoup/Sources/Collector.swift

@ -1,59 +0,0 @@
//
// Collector.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 22/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
* Collects a list of elements that match the supplied criteria.
*
*/
open class Collector {
private init() {
}
/**
Build a list of elements, by visiting root and every descendant of root, and testing it against the evaluator.
@param eval Evaluator to test elements against
@param root root of tree to descend
@return list of matches; empty if none
*/
public static func collect (_ eval: Evaluator, _ root: Element)throws->Elements {
let elements: Elements = Elements()
try NodeTraversor(Accumulator(root, elements, eval)).traverse(root)
return elements
}
}
private final class Accumulator: NodeVisitor {
private let root: Element
private let elements: Elements
private let eval: Evaluator
init(_ root: Element, _ elements: Elements, _ eval: Evaluator) {
self.root = root
self.elements = elements
self.eval = eval
}
public func head(_ node: Node, _ depth: Int) {
guard let el = node as? Element else {
return
}
do {
if try eval.matches(root, el) {
elements.add(el)
}
} catch {}
}
public func tail(_ node: Node, _ depth: Int) {
// void
}
}

127
Pods/SwiftSoup/Sources/CombiningEvaluator.swift

@ -1,127 +0,0 @@
//
// CombiningEvaluator.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 23/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
* Base combining (and, or) evaluator.
*/
public class CombiningEvaluator: Evaluator {
public private(set) var evaluators: Array<Evaluator>
var num: Int = 0
public override init() {
evaluators = Array<Evaluator>()
super.init()
}
public init(_ evaluators: Array<Evaluator>) {
self.evaluators = evaluators
super.init()
updateNumEvaluators()
}
public init(_ evaluators: Evaluator...) {
self.evaluators = evaluators
super.init()
updateNumEvaluators()
}
func rightMostEvaluator() -> Evaluator? {
return num > 0 && evaluators.count > 0 ? evaluators[num - 1] : nil
}
func replaceRightMostEvaluator(_ replacement: Evaluator) {
evaluators[num - 1] = replacement
}
func updateNumEvaluators() {
// used so we don't need to bash on size() for every match test
num = evaluators.count
}
public final class And: CombiningEvaluator {
public override init(_ evaluators: [Evaluator]) {
super.init(evaluators)
}
public override init(_ evaluators: Evaluator...) {
super.init(evaluators)
}
public override func matches(_ root: Element, _ node: Element) -> Bool {
for index in 0..<num {
let evaluator = evaluators[index]
do {
if (try !evaluator.matches(root, node)) {
return false
}
} catch {}
}
return true
}
public override func toString() -> String {
let array: [String] = evaluators.map { String($0.toString()) }
return StringUtil.join(array, sep: " ")
}
}
public final class Or: CombiningEvaluator {
/**
* Create a new Or evaluator. The initial evaluators are ANDed together and used as the first clause of the OR.
* @param evaluators initial OR clause (these are wrapped into an AND evaluator).
*/
public override init(_ evaluators: [Evaluator]) {
super.init()
if num > 1 {
self.evaluators.append(And(evaluators))
} else { // 0 or 1
self.evaluators.append(contentsOf: evaluators)
}
updateNumEvaluators()
}
override init(_ evaluators: Evaluator...) {
super.init()
if num > 1 {
self.evaluators.append(And(evaluators))
} else { // 0 or 1
self.evaluators.append(contentsOf: evaluators)
}
updateNumEvaluators()
}
override init() {
super.init()
}
public func add(_ evaluator: Evaluator) {
evaluators.append(evaluator)
updateNumEvaluators()
}
public override func matches(_ root: Element, _ node: Element) -> Bool {
for index in 0..<num {
let evaluator: Evaluator = evaluators[index]
do {
if (try evaluator.matches(root, node)) {
return true
}
} catch {}
}
return false
}
public override func toString() -> String {
return ":or\(evaluators.map {String($0.toString())})"
}
}
}

66
Pods/SwiftSoup/Sources/Comment.swift

@ -1,66 +0,0 @@
//
// Comment.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 22/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
A comment node.
*/
public class Comment: Node {
private static let COMMENT_KEY: String = "comment"
/**
Create a new comment node.
@param data The contents of the comment
@param baseUri base URI
*/
public init(_ data: String, _ baseUri: String) {
super.init(baseUri)
do {
try attributes?.put(Comment.COMMENT_KEY, data)
} catch {}
}
public override func nodeName() -> String {
return "#comment"
}
/**
Get the contents of the comment.
@return comment content
*/
public func getData() -> String {
return attributes!.get(key: Comment.COMMENT_KEY)
}
override func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {
if (out.prettyPrint()) {
indent(accum, depth, out)
}
accum
.append("<!--")
.append(getData())
.append("-->")
}
override func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {}
public override func copy(with zone: NSZone? = nil) -> Any {
let clone = Comment(attributes!.get(key: Comment.COMMENT_KEY), baseUri!)
return copy(clone: clone)
}
public override func copy(parent: Node?) -> Node {
let clone = Comment(attributes!.get(key: Comment.COMMENT_KEY), baseUri!)
return copy(clone: clone, parent: parent)
}
public override func copy(clone: Node, parent: Node?) -> Node {
return super.copy(clone: clone, parent: parent)
}
}

10
Pods/SwiftSoup/Sources/Connection.swift

@ -1,10 +0,0 @@
//
// Connection.swift
// SwifSoup
//
// Created by Nabil Chatbi on 29/09/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
//TODO:

85
Pods/SwiftSoup/Sources/DataNode.swift

@ -1,85 +0,0 @@
//
// DataNode.swift
// SwifSoup
//
// Created by Nabil Chatbi on 29/09/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
A data node, for contents of style, script tags etc, where contents should not show in text().
*/
open class DataNode: Node {
private static let DATA_KEY: String = "data"
/**
Create a new DataNode.
@param data data contents
@param baseUri base URI
*/
public init(_ data: String, _ baseUri: String) {
super.init(baseUri)
do {
try attributes?.put(DataNode.DATA_KEY, data)
} catch {}
}
open override func nodeName() -> String {
return "#data"
}
/**
Get the data contents of this node. Will be unescaped and with original new lines, space etc.
@return data
*/
open func getWholeData() -> String {
return attributes!.get(key: DataNode.DATA_KEY)
}
/**
* Set the data contents of this node.
* @param data unencoded data
* @return this node, for chaining
*/
@discardableResult
open func setWholeData(_ data: String) -> DataNode {
do {
try attributes?.put(DataNode.DATA_KEY, data)
} catch {}
return self
}
override func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings)throws {
accum.append(getWholeData()) // data is not escaped in return from data nodes, so " in script, style is plain
}
override func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {}
/**
Create a new DataNode from HTML encoded data.
@param encodedData encoded data
@param baseUri bass URI
@return new DataNode
*/
public static func createFromEncoded(_ encodedData: String, _ baseUri: String)throws->DataNode {
let data = try Entities.unescape(encodedData)
return DataNode(data, baseUri)
}
public override func copy(with zone: NSZone? = nil) -> Any {
let clone = DataNode(attributes!.get(key: DataNode.DATA_KEY), baseUri!)
return copy(clone: clone)
}
public override func copy(parent: Node?) -> Node {
let clone = DataNode(attributes!.get(key: DataNode.DATA_KEY), baseUri!)
return copy(clone: clone, parent: parent)
}
public override func copy(clone: Node, parent: Node?) -> Node {
return super.copy(clone: clone, parent: parent)
}
}

24
Pods/SwiftSoup/Sources/DataUtil.swift

@ -1,24 +0,0 @@
//
// DataUtil.swift
// SwifSoup
//
// Created by Nabil Chatbi on 02/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
* Internal static utilities for handling data.
*
*/
class DataUtil {
static let charsetPattern = "(?i)\\bcharset=\\s*(?:\"|')?([^\\s,;\"']*)"
static let defaultCharset = "UTF-8" // used if not found in header or meta charset
static let bufferSize = 0x20000 // ~130K.
static let UNICODE_BOM = 0xFEFF
static let mimeBoundaryChars = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
static let boundaryLength = 32
}

562
Pods/SwiftSoup/Sources/Document.swift

@ -1,562 +0,0 @@
//
// Document.swift
// SwifSoup
//
// Created by Nabil Chatbi on 29/09/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
open class Document: Element {
public enum QuirksMode {
case noQuirks, quirks, limitedQuirks
}
private var _outputSettings: OutputSettings = OutputSettings()
private var _quirksMode: Document.QuirksMode = QuirksMode.noQuirks
private let _location: String
private var updateMetaCharset: Bool = false
/**
Create a new, empty Document.
@param baseUri base URI of document
@see org.jsoup.Jsoup#parse
@see #createShell
*/
public init(_ baseUri: String) {
self._location = baseUri
super.init(try! Tag.valueOf("#root", ParseSettings.htmlDefault), baseUri)
}
/**
Create a valid, empty shell of a document, suitable for adding more elements to.
@param baseUri baseUri of document
@return document with html, head, and body elements.
*/
static public func createShell(_ baseUri: String) -> Document {
let doc: Document = Document(baseUri)
let html: Element = try! doc.appendElement("html")
try! html.appendElement("head")
try! html.appendElement("body")
return doc
}
/**
* Get the URL this Document was parsed from. If the starting URL is a redirect,
* this will return the final URL from which the document was served from.
* @return location
*/
public func location() -> String {
return _location
}
/**
Accessor to the document's {@code head} element.
@return {@code head}
*/
public func head() -> Element? {
return findFirstElementByTagName("head", self)
}
/**
Accessor to the document's {@code body} element.
@return {@code body}
*/
public func body() -> Element? {
return findFirstElementByTagName("body", self)
}
/**
Get the string contents of the document's {@code title} element.
@return Trimmed title, or empty string if none set.
*/
public func title()throws->String {
// title is a preserve whitespace tag (for document output), but normalised here
let titleEl: Element? = try getElementsByTag("title").first()
return titleEl != nil ? try StringUtil.normaliseWhitespace(titleEl!.text()).trim() : ""
}
/**
Set the document's {@code title} element. Updates the existing element, or adds {@code title} to {@code head} if
not present
@param title string to set as title
*/
public func title(_ title: String)throws {
let titleEl: Element? = try getElementsByTag("title").first()
if (titleEl == nil) { // add to head
try head()?.appendElement("title").text(title)
} else {
try titleEl?.text(title)
}
}
/**
Create a new Element, with this document's base uri. Does not make the new element a child of this document.
@param tagName element tag name (e.g. {@code a})
@return new element
*/
public func createElement(_ tagName: String)throws->Element {
return try Element(Tag.valueOf(tagName, ParseSettings.preserveCase), self.getBaseUri())
}
/**
Normalise the document. This happens after the parse phase so generally does not need to be called.
Moves any text content that is not in the body element into the body.
@return this document after normalisation
*/
@discardableResult
public func normalise()throws->Document {
var htmlE: Element? = findFirstElementByTagName("html", self)
if (htmlE == nil) {
htmlE = try appendElement("html")
}
let htmlEl: Element = htmlE!
if (head() == nil) {
try htmlEl.prependElement("head")
}
if (body() == nil) {
try htmlEl.appendElement("body")
}
// pull text nodes out of root, html, and head els, and push into body. non-text nodes are already taken care
// of. do in inverse order to maintain text order.
try normaliseTextNodes(head()!)
try normaliseTextNodes(htmlEl)
try normaliseTextNodes(self)
try normaliseStructure("head", htmlEl)
try normaliseStructure("body", htmlEl)
try ensureMetaCharsetElement()
return self
}
// does not recurse.
private func normaliseTextNodes(_ element: Element)throws {
var toMove: Array<Node> = Array<Node>()
for node: Node in element.childNodes {
if let tn = (node as? TextNode) {
if (!tn.isBlank()) {
toMove.append(tn)
}
}
}
for i in (0..<toMove.count).reversed() {
let node: Node = toMove[i]
try element.removeChild(node)
try body()?.prependChild(TextNode(" ", ""))
try body()?.prependChild(node)
}
}
// merge multiple <head> or <body> contents into one, delete the remainder, and ensure they are owned by <html>
private func normaliseStructure(_ tag: String, _ htmlEl: Element)throws {
let elements: Elements = try self.getElementsByTag(tag)
let master: Element? = elements.first() // will always be available as created above if not existent
if (elements.size() > 1) { // dupes, move contents to master
var toMove: Array<Node> = Array<Node>()
for i in 1..<elements.size() {
let dupe: Node = elements.get(i)
for node: Node in dupe.childNodes {
toMove.append(node)
}
try dupe.remove()
}
for dupe: Node in toMove {
try master?.appendChild(dupe)
}
}
// ensure parented by <html>
if (!(master != nil && master!.parent() != nil && master!.parent()!.equals(htmlEl))) {
try htmlEl.appendChild(master!) // includes remove()
}
}
// fast method to get first by tag name, used for html, head, body finders
private func findFirstElementByTagName(_ tag: String, _ node: Node) -> Element? {
if (node.nodeName()==tag) {
return node as? Element
} else {
for child: Node in node.childNodes {
let found: Element? = findFirstElementByTagName(tag, child)
if (found != nil) {
return found
}
}
}
return nil
}
open override func outerHtml()throws->String {
return try super.html() // no outer wrapper tag
}
/**
Set the text of the {@code body} of this document. Any existing nodes within the body will be cleared.
@param text unencoded text
@return this document
*/
@discardableResult
public override func text(_ text: String)throws->Element {
try body()?.text(text) // overridden to not nuke doc structure
return self
}
open override func nodeName() -> String {
return "#document"
}
/**
* Sets the charset used in this document. This method is equivalent
* to {@link OutputSettings#charset(java.nio.charset.Charset)
* OutputSettings.charset(Charset)} but in addition it updates the
* charset / encoding element within the document.
*
* <p>This enables
* {@link #updateMetaCharsetElement(boolean) meta charset update}.</p>
*
* <p>If there's no element with charset / encoding information yet it will
* be created. Obsolete charset / encoding definitions are removed!</p>
*
* <p><b>Elements used:</b></p>
*
* <ul>
* <li><b>Html:</b> <i>&lt;meta charset="CHARSET"&gt;</i></li>
* <li><b>Xml:</b> <i>&lt;?xml version="1.0" encoding="CHARSET"&gt;</i></li>
* </ul>
*
* @param charset Charset
*
* @see #updateMetaCharsetElement(boolean)
* @see OutputSettings#charset(java.nio.charset.Charset)
*/
public func charset(_ charset: String.Encoding)throws {
updateMetaCharsetElement(true)
_outputSettings.charset(charset)
try ensureMetaCharsetElement()
}
/**
* Returns the charset used in this document. This method is equivalent
* to {@link OutputSettings#charset()}.
*
* @return Current Charset
*
* @see OutputSettings#charset()
*/
public func charset()->String.Encoding {
return _outputSettings.charset()
}
/**
* Sets whether the element with charset information in this document is
* updated on changes through {@link #charset(java.nio.charset.Charset)
* Document.charset(Charset)} or not.
*
* <p>If set to <tt>false</tt> <i>(default)</i> there are no elements
* modified.</p>
*
* @param update If <tt>true</tt> the element updated on charset
* changes, <tt>false</tt> if not
*
* @see #charset(java.nio.charset.Charset)
*/
public func updateMetaCharsetElement(_ update: Bool) {
self.updateMetaCharset = update
}
/**
* Returns whether the element with charset information in this document is
* updated on changes through {@link #charset(java.nio.charset.Charset)
* Document.charset(Charset)} or not.
*
* @return Returns <tt>true</tt> if the element is updated on charset
* changes, <tt>false</tt> if not
*/
public func updateMetaCharsetElement() -> Bool {
return updateMetaCharset
}
/**
* Ensures a meta charset (html) or xml declaration (xml) with the current
* encoding used. This only applies with
* {@link #updateMetaCharsetElement(boolean) updateMetaCharset} set to
* <tt>true</tt>, otherwise this method does nothing.
*
* <ul>
* <li>An exsiting element gets updated with the current charset</li>
* <li>If there's no element yet it will be inserted</li>
* <li>Obsolete elements are removed</li>
* </ul>
*
* <p><b>Elements used:</b></p>
*
* <ul>
* <li><b>Html:</b> <i>&lt;meta charset="CHARSET"&gt;</i></li>
* <li><b>Xml:</b> <i>&lt;?xml version="1.0" encoding="CHARSET"&gt;</i></li>
* </ul>
*/
private func ensureMetaCharsetElement()throws {
if (updateMetaCharset) {
let syntax: OutputSettings.Syntax = outputSettings().syntax()
if (syntax == OutputSettings.Syntax.html) {
let metaCharset: Element? = try select("meta[charset]").first()
if (metaCharset != nil) {
try metaCharset?.attr("charset", charset().displayName())
} else {
let head: Element? = self.head()
if (head != nil) {
try head?.appendElement("meta").attr("charset", charset().displayName())
}
}
// Remove obsolete elements
let s = try select("meta[name=charset]")
try s.remove()
} else if (syntax == OutputSettings.Syntax.xml) {
let node: Node = getChildNodes()[0]
if let decl = (node as? XmlDeclaration) {
if (decl.name()=="xml") {
try decl.attr("encoding", charset().displayName())
_ = try decl.attr("version")
try decl.attr("version", "1.0")
} else {
try Validate.notNull(obj: baseUri)
let decl = XmlDeclaration("xml", baseUri!, false)
try decl.attr("version", "1.0")
try decl.attr("encoding", charset().displayName())
try prependChild(decl)
}
} else {
try Validate.notNull(obj: baseUri)
let decl = XmlDeclaration("xml", baseUri!, false)
try decl.attr("version", "1.0")
try decl.attr("encoding", charset().displayName())
try prependChild(decl)
}
}
}
}
/**
* Get the document's current output settings.
* @return the document's current output settings.
*/
public func outputSettings() -> OutputSettings {
return _outputSettings
}
/**
* Set the document's output settings.
* @param outputSettings new output settings.
* @return this document, for chaining.
*/
@discardableResult
public func outputSettings(_ outputSettings: OutputSettings) -> Document {
self._outputSettings = outputSettings
return self
}
public func quirksMode()->Document.QuirksMode {
return _quirksMode
}
@discardableResult
public func quirksMode(_ quirksMode: Document.QuirksMode) -> Document {
self._quirksMode = quirksMode
return self
}
public override func copy(with zone: NSZone? = nil) -> Any {
let clone = Document(_location)
return copy(clone: clone)
}
public override func copy(parent: Node?) -> Node {
let clone = Document(_location)
return copy(clone: clone, parent: parent)
}
public override func copy(clone: Node, parent: Node?) -> Node {
let clone = clone as! Document
clone._outputSettings = _outputSettings.copy() as! OutputSettings
clone._quirksMode = _quirksMode
clone.updateMetaCharset = updateMetaCharset
return super.copy(clone: clone, parent: parent)
}
}
public class OutputSettings: NSCopying {
/**
* The output serialization syntax.
*/
public enum Syntax {case html, xml}
private var _escapeMode: Entities.EscapeMode = Entities.EscapeMode.base
private var _encoder: String.Encoding = String.Encoding.utf8 // Charset.forName("UTF-8")
private var _prettyPrint: Bool = true
private var _outline: Bool = false
private var _indentAmount: UInt = 1
private var _syntax = Syntax.html
public init() {}
/**
* Get the document's current HTML escape mode: <code>base</code>, which provides a limited set of named HTML
* entities and escapes other characters as numbered entities for maximum compatibility; or <code>extended</code>,
* which uses the complete set of HTML named entities.
* <p>
* The default escape mode is <code>base</code>.
* @return the document's current escape mode
*/
public func escapeMode() -> Entities.EscapeMode {
return _escapeMode
}
/**
* Set the document's escape mode, which determines how characters are escaped when the output character set
* does not support a given character:- using either a named or a numbered escape.
* @param escapeMode the new escape mode to use
* @return the document's output settings, for chaining
*/
@discardableResult
public func escapeMode(_ escapeMode: Entities.EscapeMode) -> OutputSettings {
self._escapeMode = escapeMode
return self
}
/**
* Get the document's current output charset, which is used to control which characters are escaped when
* generating HTML (via the <code>html()</code> methods), and which are kept intact.
* <p>
* Where possible (when parsing from a URL or File), the document's output charset is automatically set to the
* input charset. Otherwise, it defaults to UTF-8.
* @return the document's current charset.
*/
public func encoder() -> String.Encoding {
return _encoder
}
public func charset() -> String.Encoding {
return _encoder
}
/**
* Update the document's output charset.
* @param charset the new charset to use.
* @return the document's output settings, for chaining
*/
@discardableResult
public func encoder(_ encoder: String.Encoding) -> OutputSettings {
self._encoder = encoder
return self
}
@discardableResult
public func charset(_ e: String.Encoding) -> OutputSettings {
return encoder(e)
}
/**
* Get the document's current output syntax.
* @return current syntax
*/
public func syntax() -> Syntax {
return _syntax
}
/**
* Set the document's output syntax. Either {@code html}, with empty tags and boolean attributes (etc), or
* {@code xml}, with self-closing tags.
* @param syntax serialization syntax
* @return the document's output settings, for chaining
*/
@discardableResult
public func syntax(syntax: Syntax) -> OutputSettings {
_syntax = syntax
return self
}
/**
* Get if pretty printing is enabled. Default is true. If disabled, the HTML output methods will not re-format
* the output, and the output will generally look like the input.
* @return if pretty printing is enabled.
*/
public func prettyPrint() -> Bool {
return _prettyPrint
}
/**
* Enable or disable pretty printing.
* @param pretty new pretty print setting
* @return this, for chaining
*/
@discardableResult
public func prettyPrint(pretty: Bool) -> OutputSettings {
_prettyPrint = pretty
return self
}
/**
* Get if outline mode is enabled. Default is false. If enabled, the HTML output methods will consider
* all tags as block.
* @return if outline mode is enabled.
*/
public func outline() -> Bool {
return _outline
}
/**
* Enable or disable HTML outline mode.
* @param outlineMode new outline setting
* @return this, for chaining
*/
@discardableResult
public func outline(outlineMode: Bool) -> OutputSettings {
_outline = outlineMode
return self
}
/**
* Get the current tag indent amount, used when pretty printing.
* @return the current indent amount
*/
public func indentAmount() -> UInt {
return _indentAmount
}
/**
* Set the indent amount for pretty printing
* @param indentAmount number of spaces to use for indenting each level. Must be {@literal >=} 0.
* @return this, for chaining
*/
@discardableResult
public func indentAmount(indentAmount: UInt) -> OutputSettings {
_indentAmount = indentAmount
return self
}
public func copy(with zone: NSZone? = nil) -> Any {
let clone: OutputSettings = OutputSettings()
clone.charset(_encoder) // new charset and charset encoder
clone._escapeMode = _escapeMode//Entities.EscapeMode.valueOf(escapeMode.name())
// indentAmount, prettyPrint are primitives so object.clone() will handle
return clone
}
}

129
Pods/SwiftSoup/Sources/DocumentType.swift

@ -1,129 +0,0 @@
//
// DocumentType.swift
// SwifSoup
//
// Created by Nabil Chatbi on 29/09/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
* A {@code <!DOCTYPE>} node.
*/
public class DocumentType: Node {
static let PUBLIC_KEY: String = "PUBLIC"
static let SYSTEM_KEY: String = "SYSTEM"
private static let NAME: String = "name"
private static let PUB_SYS_KEY: String = "pubSysKey"; // PUBLIC or SYSTEM
private static let PUBLIC_ID: String = "publicId"
private static let SYSTEM_ID: String = "systemId"
// todo: quirk mode from publicId and systemId
/**
* Create a new doctype element.
* @param name the doctype's name
* @param publicId the doctype's public ID
* @param systemId the doctype's system ID
* @param baseUri the doctype's base URI
*/
public init(_ name: String, _ publicId: String, _ systemId: String, _ baseUri: String) {
super.init(baseUri)
do {
try attr(DocumentType.NAME, name)
try attr(DocumentType.PUBLIC_ID, publicId)
if (has(DocumentType.PUBLIC_ID)) {
try attr(DocumentType.PUB_SYS_KEY, DocumentType.PUBLIC_KEY)
}
try attr(DocumentType.SYSTEM_ID, systemId)
} catch {}
}
/**
* Create a new doctype element.
* @param name the doctype's name
* @param publicId the doctype's public ID
* @param systemId the doctype's system ID
* @param baseUri the doctype's base URI
*/
public init(_ name: String, _ pubSysKey: String?, _ publicId: String, _ systemId: String, _ baseUri: String) {
super.init(baseUri)
do {
try attr(DocumentType.NAME, name)
if(pubSysKey != nil) {
try attr(DocumentType.PUB_SYS_KEY, pubSysKey!)
}
try attr(DocumentType.PUBLIC_ID, publicId)
try attr(DocumentType.SYSTEM_ID, systemId)
} catch {}
}
public override func nodeName() -> String {
return "#doctype"
}
override func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {
if (out.syntax() == OutputSettings.Syntax.html && !has(DocumentType.PUBLIC_ID) && !has(DocumentType.SYSTEM_ID)) {
// looks like a html5 doctype, go lowercase for aesthetics
accum.append("<!doctype")
} else {
accum.append("<!DOCTYPE")
}
if (has(DocumentType.NAME)) {
do {
accum.append(" ").append(try attr(DocumentType.NAME))
} catch {}
}
if (has(DocumentType.PUB_SYS_KEY)) {
do {
try accum.append(" ").append(attr(DocumentType.PUB_SYS_KEY))
} catch {}
}
if (has(DocumentType.PUBLIC_ID)) {
do {
try accum.append(" \"").append(attr(DocumentType.PUBLIC_ID)).append("\"")
} catch {}
}
if (has(DocumentType.SYSTEM_ID)) {
do {
accum.append(" \"").append(try attr(DocumentType.SYSTEM_ID)).append("\"")
} catch {}
}
accum.append(">")
}
override func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {
}
private func has(_ attribute: String) -> Bool {
do {
return !StringUtil.isBlank(try attr(attribute))
} catch {return false}
}
public override func copy(with zone: NSZone? = nil) -> Any {
let clone = DocumentType(attributes!.get(key: DocumentType.NAME),
attributes!.get(key: DocumentType.PUBLIC_ID),
attributes!.get(key: DocumentType.SYSTEM_ID),
baseUri!)
return copy(clone: clone)
}
public override func copy(parent: Node?) -> Node {
let clone = DocumentType(attributes!.get(key: DocumentType.NAME),
attributes!.get(key: DocumentType.PUBLIC_ID),
attributes!.get(key: DocumentType.SYSTEM_ID),
baseUri!)
return copy(clone: clone, parent: parent)
}
public override func copy(clone: Node, parent: Node?) -> Node {
return super.copy(clone: clone, parent: parent)
}
}

1295
Pods/SwiftSoup/Sources/Element.swift

File diff suppressed because it is too large

602
Pods/SwiftSoup/Sources/Elements.swift

@ -1,602 +0,0 @@
//
// Elements.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 20/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
/**
A list of {@link Element}s, with methods that act on every element in the list.
<p>
To get an {@code Elements} object, use the {@link Element#select(String)} method.
</p>
*/
import Foundation
//open typealias Elements = Array<Element>
//typealias E = Element
open class Elements: NSCopying {
fileprivate var this: Array<Element> = Array<Element>()
///base init
public init() {
}
///Initialized with an array
public init(_ a: Array<Element>) {
this = a
}
///Initialized with an order set
public init(_ a: OrderedSet<Element>) {
this.append(contentsOf: a)
}
/**
* Creates a deep copy of these elements.
* @return a deep copy
*/
public func copy(with zone: NSZone? = nil) -> Any {
let clone: Elements = Elements()
for e: Element in this {
clone.add(e.copy() as! Element)
}
return clone
}
// attribute methods
/**
Get an attribute value from the first matched element that has the attribute.
@param attributeKey The attribute key.
@return The attribute value from the first matched element that has the attribute.. If no elements were matched (isEmpty() == true),
or if the no elements have the attribute, returns empty string.
@see #hasAttr(String)
*/
open func attr(_ attributeKey: String)throws->String {
for element in this {
if (element.hasAttr(attributeKey)) {
return try element.attr(attributeKey)
}
}
return ""
}
/**
Checks if any of the matched elements have this attribute set.
@param attributeKey attribute key
@return true if any of the elements have the attribute; false if none do.
*/
open func hasAttr(_ attributeKey: String) -> Bool {
for element in this {
if element.hasAttr(attributeKey) {return true}
}
return false
}
/**
* Set an attribute on all matched elements.
* @param attributeKey attribute key
* @param attributeValue attribute value
* @return this
*/
@discardableResult
open func attr(_ attributeKey: String, _ attributeValue: String)throws->Elements {
for element in this {
try element.attr(attributeKey, attributeValue)
}
return self
}
/**
* Remove an attribute from every matched element.
* @param attributeKey The attribute to remove.
* @return this (for chaining)
*/
@discardableResult
open func removeAttr(_ attributeKey: String)throws->Elements {
for element in this {
try element.removeAttr(attributeKey)
}
return self
}
/**
Add the class name to every matched element's {@code class} attribute.
@param className class name to add
@return this
*/
@discardableResult
open func addClass(_ className: String)throws->Elements {
for element in this {
try element.addClass(className)
}
return self
}
/**
Remove the class name from every matched element's {@code class} attribute, if present.
@param className class name to remove
@return this
*/
@discardableResult
open func removeClass(_ className: String)throws->Elements {
for element: Element in this {
try element.removeClass(className)
}
return self
}
/**
Toggle the class name on every matched element's {@code class} attribute.
@param className class name to add if missing, or remove if present, from every element.
@return this
*/
@discardableResult
open func toggleClass(_ className: String)throws->Elements {
for element: Element in this {
try element.toggleClass(className)
}
return self
}
/**
Determine if any of the matched elements have this class name set in their {@code class} attribute.
@param className class name to check for
@return true if any do, false if none do
*/
open func hasClass(_ className: String) -> Bool {
for element: Element in this {
if (element.hasClass(className)) {
return true
}
}
return false
}
/**
* Get the form element's value of the first matched element.
* @return The form element's value, or empty if not set.
* @see Element#val()
*/
open func val()throws->String {
if (size() > 0) {
return try first()!.val()
}
return ""
}
/**
* Set the form element's value in each of the matched elements.
* @param value The value to set into each matched element
* @return this (for chaining)
*/
@discardableResult
open func val(_ value: String)throws->Elements {
for element: Element in this {
try element.val(value)
}
return self
}
/**
* Get the combined text of all the matched elements.
* <p>
* Note that it is possible to get repeats if the matched elements contain both parent elements and their own
* children, as the Element.text() method returns the combined text of a parent and all its children.
* @return string of all text: unescaped and no HTML.
* @see Element#text()
*/
open func text()throws->String {
let sb: StringBuilder = StringBuilder()
for element: Element in this {
if (sb.length != 0) {
sb.append(" ")
}
sb.append(try element.text())
}
return sb.toString()
}
/// Check if an element has text
open func hasText() -> Bool {
for element: Element in this {
if (element.hasText()) {
return true
}
}
return false
}
/**
* Get the combined inner HTML of all matched elements.
* @return string of all element's inner HTML.
* @see #text()
* @see #outerHtml()
*/
open func html()throws->String {
var text = try this.reduce("") {result, name in "\(result)\(try name.html())\n"}
text.removeLast()
return text
}
/**
* Get the combined outer HTML of all matched elements.
* @return string of all element's outer HTML.
* @see #text()
* @see #html()
*/
open func outerHtml()throws->String {
let sb: StringBuilder = StringBuilder()
for element in this {
if (sb.length != 0) {
sb.append("\n")
}
sb.append(try element.outerHtml())
}
return sb.toString()
}
/**
* Get the combined outer HTML of all matched elements. Alias of {@link #outerHtml()}.
* @return string of all element's outer HTML.
* @see #text()
* @see #html()
*/
open func toString()throws->String {
return try outerHtml()
}
/**
* Update the tag name of each matched element. For example, to change each {@code <i>} to a {@code <em>}, do
* {@code doc.select("i").tagName("em");}
* @param tagName the new tag name
* @return this, for chaining
* @see Element#tagName(String)
*/
@discardableResult
open func tagName(_ tagName: String)throws->Elements {
for element: Element in this {
try element.tagName(tagName)
}
return self
}
/**
* Set the inner HTML of each matched element.
* @param html HTML to parse and set into each matched element.
* @return this, for chaining
* @see Element#html(String)
*/
@discardableResult
open func html(_ html: String)throws->Elements {
for element: Element in this {
try element.html(html)
}
return self
}
/**
* Add the supplied HTML to the start of each matched element's inner HTML.
* @param html HTML to add inside each element, before the existing HTML
* @return this, for chaining
* @see Element#prepend(String)
*/
@discardableResult
open func prepend(_ html: String)throws->Elements {
for element: Element in this {
try element.prepend(html)
}
return self
}
/**
* Add the supplied HTML to the end of each matched element's inner HTML.
* @param html HTML to add inside each element, after the existing HTML
* @return this, for chaining
* @see Element#append(String)
*/
@discardableResult
open func append(_ html: String)throws->Elements {
for element: Element in this {
try element.append(html)
}
return self
}
/**
* Insert the supplied HTML before each matched element's outer HTML.
* @param html HTML to insert before each element
* @return this, for chaining
* @see Element#before(String)
*/
@discardableResult
open func before(_ html: String)throws->Elements {
for element: Element in this {
try element.before(html)
}
return self
}
/**
* Insert the supplied HTML after each matched element's outer HTML.
* @param html HTML to insert after each element
* @return this, for chaining
* @see Element#after(String)
*/
@discardableResult
open func after(_ html: String)throws->Elements {
for element: Element in this {
try element.after(html)
}
return self
}
/**
Wrap the supplied HTML around each matched elements. For example, with HTML
{@code <p><b>This</b> is <b>Jsoup</b></p>},
<code>doc.select("b").wrap("&lt;i&gt;&lt;/i&gt;");</code>
becomes {@code <p><i><b>This</b></i> is <i><b>jsoup</b></i></p>}
@param html HTML to wrap around each element, e.g. {@code <div class="head"></div>}. Can be arbitrarily deep.
@return this (for chaining)
@see Element#wrap
*/
@discardableResult
open func wrap(_ html: String)throws->Elements {
try Validate.notEmpty(string: html)
for element: Element in this {
try element.wrap(html)
}
return self
}
/**
* Removes the matched elements from the DOM, and moves their children up into their parents. This has the effect of
* dropping the elements but keeping their children.
* <p>
* This is useful for e.g removing unwanted formatting elements but keeping their contents.
* </p>
*
* E.g. with HTML: <p>{@code <div><font>One</font> <font><a href="/">Two</a></font></div>}</p>
* <p>{@code doc.select("font").unwrap();}</p>
* <p>HTML = {@code <div>One <a href="/">Two</a></div>}</p>
*
* @return this (for chaining)
* @see Node#unwrap
*/
@discardableResult
open func unwrap()throws->Elements {
for element: Element in this {
try element.unwrap()
}
return self
}
/**
* Empty (remove all child nodes from) each matched element. This is similar to setting the inner HTML of each
* element to nothing.
* <p>
* E.g. HTML: {@code <div><p>Hello <b>there</b></p> <p>now</p></div>}<br>
* <code>doc.select("p").empty();</code><br>
* HTML = {@code <div><p></p> <p></p></div>}
* @return this, for chaining
* @see Element#empty()
* @see #remove()
*/
@discardableResult
open func empty() -> Elements {
for element: Element in this {
element.empty()
}
return self
}
/**
* Remove each matched element from the DOM. This is similar to setting the outer HTML of each element to nothing.
* <p>
* E.g. HTML: {@code <div><p>Hello</p> <p>there</p> <img /></div>}<br>
* <code>doc.select("p").remove();</code><br>
* HTML = {@code <div> <img /></div>}
* <p>
* Note that this method should not be used to clean user-submitted HTML; rather, use {@link org.jsoup.safety.Cleaner} to clean HTML.
* @return this, for chaining
* @see Element#empty()
* @see #empty()
*/
@discardableResult
open func remove()throws->Elements {
for element in this {
try element.remove()
}
return self
}
// filters
/**
* Find matching elements within this element list.
* @param query A {@link Selector} query
* @return the filtered list of elements, or an empty list if none match.
*/
open func select(_ query: String)throws->Elements {
return try Selector.select(query, this)
}
/**
* Remove elements from this list that match the {@link Selector} query.
* <p>
* E.g. HTML: {@code <div class=logo>One</div> <div>Two</div>}<br>
* <code>Elements divs = doc.select("div").not(".logo");</code><br>
* Result: {@code divs: [<div>Two</div>]}
* <p>
* @param query the selector query whose results should be removed from these elements
* @return a new elements list that contains only the filtered results
*/
open func not(_ query: String)throws->Elements {
let out: Elements = try Selector.select(query, this)
return Selector.filterOut(this, out.this)
}
/**
* Get the <i>nth</i> matched element as an Elements object.
* <p>
* See also {@link #get(int)} to retrieve an Element.
* @param index the (zero-based) index of the element in the list to retain
* @return Elements containing only the specified element, or, if that element did not exist, an empty list.
*/
open func eq(_ index: Int) -> Elements {
return size() > index ? Elements([get(index)]) : Elements()
}
/**
* Test if any of the matched elements match the supplied query.
* @param query A selector
* @return true if at least one element in the list matches the query.
*/
open func iS(_ query: String)throws->Bool {
let eval: Evaluator = try QueryParser.parse(query)
for e: Element in this {
if (try e.iS(eval)) {
return true
}
}
return false
}
/**
* Get all of the parents and ancestor elements of the matched elements.
* @return all of the parents and ancestor elements of the matched elements
*/
open func parents() -> Elements {
let combo: OrderedSet<Element> = OrderedSet<Element>()
for e: Element in this {
combo.append(contentsOf: e.parents().array())
}
return Elements(combo)
}
// list-like methods
/**
Get the first matched element.
@return The first matched element, or <code>null</code> if contents is empty.
*/
open func first() -> Element? {
return isEmpty() ? nil : get(0)
}
/// Check if no element stored
open func isEmpty() -> Bool {
return array().count == 0
}
/// Count
open func size() -> Int {
return array().count
}
/**
Get the last matched element.
@return The last matched element, or <code>null</code> if contents is empty.
*/
open func last() -> Element? {
return isEmpty() ? nil : get(size() - 1)
}
/**
* Perform a depth-first traversal on each of the selected elements.
* @param nodeVisitor the visitor callbacks to perform on each node
* @return this, for chaining
*/
@discardableResult
open func traverse(_ nodeVisitor: NodeVisitor)throws->Elements {
let traversor: NodeTraversor = NodeTraversor(nodeVisitor)
for el: Element in this {
try traversor.traverse(el)
}
return self
}
/**
* Get the {@link FormElement} forms from the selected elements, if any.
* @return a list of {@link FormElement}s pulled from the matched elements. The list will be empty if the elements contain
* no forms.
*/
open func forms()->Array<FormElement> {
var forms: Array<FormElement> = Array<FormElement>()
for el: Element in this {
if let el = el as? FormElement {
forms.append(el)
}
}
return forms
}
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
open func add(_ e: Element) {
this.append(e)
}
/**
* Insert the specified element at index.
*/
open func add(_ index: Int, _ element: Element) {
this.insert(element, at: index)
}
/// Return element at index
open func get(_ i: Int) -> Element {
return this[i]
}
/// Returns all elements
open func array()->Array<Element> {
return this
}
}
/**
* Elements extension Equatable.
*/
extension Elements: Equatable {
/// Returns a Boolean value indicating whether two values are equal.
///
/// Equality is the inverse of inequality. For any values `a` and `b`,
/// `a == b` implies that `a != b` is `false`.
///
/// - Parameters:
/// - lhs: A value to compare.
/// - rhs: Another value to compare.
public static func ==(lhs: Elements, rhs: Elements) -> Bool {
return lhs.this == rhs.this
}
}
/**
* Elements RandomAccessCollection
*/
extension Elements: RandomAccessCollection {
public subscript(position: Int) -> Element {
return this[position]
}
public var startIndex: Int {
return this.startIndex
}
public var endIndex: Int {
return this.endIndex
}
/// The number of Element objects in the collection.
/// Equivalent to `size()`
public var count: Int {
return this.count
}
}

382
Pods/SwiftSoup/Sources/Entities.swift

File diff suppressed because one or more lines are too long

720
Pods/SwiftSoup/Sources/Evaluator.swift

@ -1,720 +0,0 @@
//
// Evaluator.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 22/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
* Evaluates that an element matches the selector.
*/
public class Evaluator {
init () {}
/**
* Test if the element meets the evaluator's requirements.
*
* @param root Root of the matching subtree
* @param element tested element
* @return Returns <tt>true</tt> if the requirements are met or
* <tt>false</tt> otherwise
*/
open func matches(_ root: Element, _ element: Element)throws->Bool {
preconditionFailure("self method must be overridden")
}
open func toString() -> String {
preconditionFailure("self method must be overridden")
}
/**
* Evaluator for tag name
*/
public class Tag: Evaluator {
private let tagName: String
private let tagNameNormal: String
public init(_ tagName: String) {
self.tagName = tagName
self.tagNameNormal = tagName.lowercased()
}
open override func matches(_ root: Element, _ element: Element)throws->Bool {
return element.tagNameNormal() == tagNameNormal
}
open override func toString() -> String {
return String(tagName)
}
}
/**
* Evaluator for tag name that ends with
*/
public final class TagEndsWith: Evaluator {
private let tagName: String
public init(_ tagName: String) {
self.tagName = tagName
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
return (element.tagName().hasSuffix(tagName))
}
public override func toString() -> String {
return String(tagName)
}
}
/**
* Evaluator for element id
*/
public final class Id: Evaluator {
private let id: String
public init(_ id: String) {
self.id = id
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
return (id == element.id())
}
public override func toString() -> String {
return "#\(id)"
}
}
/**
* Evaluator for element class
*/
public final class Class: Evaluator {
private let className: String
public init(_ className: String) {
self.className = className
}
public override func matches(_ root: Element, _ element: Element) -> Bool {
return (element.hasClass(className))
}
public override func toString() -> String {
return ".\(className)"
}
}
/**
* Evaluator for attribute name matching
*/
public final class Attribute: Evaluator {
private let key: String
public init(_ key: String) {
self.key = key
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
return element.hasAttr(key)
}
public override func toString() -> String {
return "[\(key)]"
}
}
/**
* Evaluator for attribute name prefix matching
*/
public final class AttributeStarting: Evaluator {
private let keyPrefix: String
public init(_ keyPrefix: String)throws {
try Validate.notEmpty(string: keyPrefix)
self.keyPrefix = keyPrefix.lowercased()
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
if let values = element.getAttributes() {
for attribute in values where attribute.getKey().lowercased().hasPrefix(keyPrefix) {
return true
}
}
return false
}
public override func toString() -> String {
return "[^\(keyPrefix)]"
}
}
/**
* Evaluator for attribute name/value matching
*/
public final class AttributeWithValue: AttributeKeyPair {
public override init(_ key: String, _ value: String)throws {
try super.init(key, value)
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
if element.hasAttr(key) {
let string = try element.attr(key)
return value.equalsIgnoreCase(string: string.trim())
}
return false
}
public override func toString() -> String {
return "[\(key)=\(value)]"
}
}
/**
* Evaluator for attribute name != value matching
*/
public final class AttributeWithValueNot: AttributeKeyPair {
public override init(_ key: String, _ value: String)throws {
try super.init(key, value)
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
let string = try element.attr(key)
return !value.equalsIgnoreCase(string: string)
}
public override func toString() -> String {
return "[\(key)!=\(value)]"
}
}
/**
* Evaluator for attribute name/value matching (value prefix)
*/
public final class AttributeWithValueStarting: AttributeKeyPair {
public override init(_ key: String, _ value: String)throws {
try super.init(key, value)
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
if element.hasAttr(key) {
return try element.attr(key).lowercased().hasPrefix(value) // value is lower case already
}
return false
}
public override func toString() -> String {
return "[\(key)^=\(value)]"
}
}
/**
* Evaluator for attribute name/value matching (value ending)
*/
public final class AttributeWithValueEnding: AttributeKeyPair {
public override init(_ key: String, _ value: String)throws {
try super.init(key, value)
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
if element.hasAttr(key) {
return try element.attr(key).lowercased().hasSuffix(value) // value is lower case
}
return false
}
public override func toString() -> String {
return "[\(key)$=\(value)]"
}
}
/**
* Evaluator for attribute name/value matching (value containing)
*/
public final class AttributeWithValueContaining: AttributeKeyPair {
public override init(_ key: String, _ value: String)throws {
try super.init(key, value)
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
if element.hasAttr(key) {
return try element.attr(key).lowercased().contains(value) // value is lower case
}
return false
}
public override func toString() -> String {
return "[\(key)*=\(value)]"
}
}
/**
* Evaluator for attribute name/value matching (value regex matching)
*/
public final class AttributeWithValueMatching: Evaluator {
let key: String
let pattern: Pattern
public init(_ key: String, _ pattern: Pattern) {
self.key = key.trim().lowercased()
self.pattern = pattern
super.init()
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
if element.hasAttr(key) {
let string = try element.attr(key)
return pattern.matcher(in: string).find()
}
return false
}
public override func toString() -> String {
return "[\(key)~=\(pattern.toString())]"
}
}
/**
* Abstract evaluator for attribute name/value matching
*/
public class AttributeKeyPair: Evaluator {
let key: String
var value: String
public init(_ key: String, _ value2: String)throws {
var value2 = value2
try Validate.notEmpty(string: key)
try Validate.notEmpty(string: value2)
self.key = key.trim().lowercased()
if value2.startsWith("\"") && value2.hasSuffix("\"") || value2.startsWith("'") && value2.hasSuffix("'") {
value2 = value2.substring(1, value2.count-2)
}
self.value = value2.trim().lowercased()
}
open override func matches(_ root: Element, _ element: Element)throws->Bool {
preconditionFailure("self method must be overridden")
}
}
/**
* Evaluator for any / all element matching
*/
public final class AllElements: Evaluator {
public override func matches(_ root: Element, _ element: Element)throws->Bool {
return true
}
public override func toString() -> String {
return "*"
}
}
/**
* Evaluator for matching by sibling index number (e {@literal <} idx)
*/
public final class IndexLessThan: IndexEvaluator {
public override init(_ index: Int) {
super.init(index)
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
return try element.elementSiblingIndex() < index
}
public override func toString() -> String {
return ":lt(\(index))"
}
}
/**
* Evaluator for matching by sibling index number (e {@literal >} idx)
*/
public final class IndexGreaterThan: IndexEvaluator {
public override init(_ index: Int) {
super.init(index)
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
return try element.elementSiblingIndex() > index
}
public override func toString() -> String {
return ":gt(\(index))"
}
}
/**
* Evaluator for matching by sibling index number (e = idx)
*/
public final class IndexEquals: IndexEvaluator {
public override init(_ index: Int) {
super.init(index)
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
return try element.elementSiblingIndex() == index
}
public override func toString() -> String {
return ":eq(\(index))"
}
}
/**
* Evaluator for matching the last sibling (css :last-child)
*/
public final class IsLastChild: Evaluator {
public override func matches(_ root: Element, _ element: Element)throws->Bool {
if let parent = element.parent() {
let index = try element.elementSiblingIndex()
return !(parent is Document) && index == (parent.getChildNodes().count - 1)
}
return false
}
public override func toString() -> String {
return ":last-child"
}
}
public final class IsFirstOfType: IsNthOfType {
public init() {
super.init(0, 1)
}
public override func toString() -> String {
return ":first-of-type"
}
}
public final class IsLastOfType: IsNthLastOfType {
public init() {
super.init(0, 1)
}
public override func toString() -> String {
return ":last-of-type"
}
}
public class CssNthEvaluator: Evaluator {
public let a: Int
public let b: Int
public init(_ a: Int, _ b: Int) {
self.a = a
self.b = b
}
public init(_ b: Int) {
self.a = 0
self.b = b
}
open override func matches(_ root: Element, _ element: Element)throws->Bool {
let p: Element? = element.parent()
if (p == nil || (((p as? Document) != nil))) {return false}
let pos: Int = try calculatePosition(root, element)
if (a == 0) {return pos == b}
return (pos-b)*a >= 0 && (pos-b)%a==0
}
open override func toString() -> String {
if (a == 0) {
return ":\(getPseudoClass())(\(b))"
}
if (b == 0) {
return ":\(getPseudoClass())(\(a))"
}
return ":\(getPseudoClass())(\(a)\(b))"
}
open func getPseudoClass() -> String {
preconditionFailure("self method must be overridden")
}
open func calculatePosition(_ root: Element, _ element: Element)throws->Int {
preconditionFailure("self method must be overridden")
}
}
/**
* css-compatible Evaluator for :eq (css :nth-child)
*
* @see IndexEquals
*/
public final class IsNthChild: CssNthEvaluator {
public override init(_ a: Int, _ b: Int) {
super.init(a, b)
}
public override func calculatePosition(_ root: Element, _ element: Element)throws->Int {
return try element.elementSiblingIndex()+1
}
public override func getPseudoClass() -> String {
return "nth-child"
}
}
/**
* css pseudo class :nth-last-child)
*
* @see IndexEquals
*/
public final class IsNthLastChild: CssNthEvaluator {
public override init(_ a: Int, _ b: Int) {
super.init(a, b)
}
public override func calculatePosition(_ root: Element, _ element: Element)throws->Int {
var i = 0
if let l = element.parent() {
i = l.children().array().count
}
return i - (try element.elementSiblingIndex())
}
public override func getPseudoClass() -> String {
return "nth-last-child"
}
}
/**
* css pseudo class nth-of-type
*
*/
public class IsNthOfType: CssNthEvaluator {
public override init(_ a: Int, _ b: Int) {
super.init(a, b)
}
open override func calculatePosition(_ root: Element, _ element: Element) -> Int {
var pos = 0
let family: Elements? = element.parent()?.children()
if let array = family?.array() {
for el in array {
if (el.tag() == element.tag()) {pos+=1}
if (el === element) {break}
}
}
return pos
}
open override func getPseudoClass() -> String {
return "nth-of-type"
}
}
public class IsNthLastOfType: CssNthEvaluator {
public override init(_ a: Int, _ b: Int) {
super.init(a, b)
}
open override func calculatePosition(_ root: Element, _ element: Element)throws->Int {
var pos = 0
if let family = element.parent()?.children() {
let x = try element.elementSiblingIndex()
for i in x..<family.array().count {
if (family.get(i).tag() == element.tag()) {
pos+=1
}
}
}
return pos
}
open override func getPseudoClass() -> String {
return "nth-last-of-type"
}
}
/**
* Evaluator for matching the first sibling (css :first-child)
*/
public final class IsFirstChild: Evaluator {
public override func matches(_ root: Element, _ element: Element)throws->Bool {
let p = element.parent()
if(p != nil && !(((p as? Document) != nil))) {
return (try element.elementSiblingIndex()) == 0
}
return false
}
public override func toString() -> String {
return ":first-child"
}
}
/**
* css3 pseudo-class :root
* @see <a href="http://www.w3.org/TR/selectors/#root-pseudo">:root selector</a>
*
*/
public final class IsRoot: Evaluator {
public override func matches(_ root: Element, _ element: Element)throws->Bool {
let r: Element = ((root as? Document) != nil) ? root.child(0) : root
return element === r
}
public override func toString() -> String {
return ":root"
}
}
public final class IsOnlyChild: Evaluator {
public override func matches(_ root: Element, _ element: Element)throws->Bool {
let p = element.parent()
return p != nil && !((p as? Document) != nil) && element.siblingElements().array().count == 0
}
public override func toString() -> String {
return ":only-child"
}
}
public final class IsOnlyOfType: Evaluator {
public override func matches(_ root: Element, _ element: Element)throws->Bool {
let p = element.parent()
if (p == nil || (p as? Document) != nil) {return false}
var pos = 0
if let family = p?.children().array() {
for el in family {
if (el.tag() == element.tag()) {pos+=1}
}
}
return pos == 1
}
public override func toString() -> String {
return ":only-of-type"
}
}
public final class IsEmpty: Evaluator {
public override func matches(_ root: Element, _ element: Element)throws->Bool {
let family: Array<Node> = element.getChildNodes()
for n in family {
if (!((n as? Comment) != nil || (n as? XmlDeclaration) != nil || (n as? DocumentType) != nil)) {return false}
}
return true
}
public override func toString() -> String {
return ":empty"
}
}
/**
* Abstract evaluator for sibling index matching
*
* @author ant
*/
public class IndexEvaluator: Evaluator {
let index: Int
public init(_ index: Int) {
self.index = index
}
}
/**
* Evaluator for matching Element (and its descendants) text
*/
public final class ContainsText: Evaluator {
private let searchText: String
public init(_ searchText: String) {
self.searchText = searchText.lowercased()
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
return (try element.text().lowercased().contains(searchText))
}
public override func toString() -> String {
return ":contains(\(searchText)"
}
}
/**
* Evaluator for matching Element's own text
*/
public final class ContainsOwnText: Evaluator {
private let searchText: String
public init(_ searchText: String) {
self.searchText = searchText.lowercased()
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
return (element.ownText().lowercased().contains(searchText))
}
public override func toString() -> String {
return ":containsOwn(\(searchText)"
}
}
/**
* Evaluator for matching Element (and its descendants) text with regex
*/
public final class Matches: Evaluator {
private let pattern: Pattern
public init(_ pattern: Pattern) {
self.pattern = pattern
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
let m = try pattern.matcher(in: element.text())
return m.find()
}
public override func toString() -> String {
return ":matches(\(pattern)"
}
}
/**
* Evaluator for matching Element's own text with regex
*/
public final class MatchesOwn: Evaluator {
private let pattern: Pattern
public init(_ pattern: Pattern) {
self.pattern = pattern
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
let m = pattern.matcher(in: element.ownText())
return m.find()
}
public override func toString() -> String {
return ":matchesOwn(\(pattern.toString())"
}
}
}

22
Pods/SwiftSoup/Sources/Exception.swift

@ -1,22 +0,0 @@
//
// Exception.swift
// SwifSoup
//
// Created by Nabil Chatbi on 02/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
public enum ExceptionType {
case IllegalArgumentException
case IOException
case XmlDeclaration
case MalformedURLException
case CloneNotSupportedException
case SelectorParseException
}
public enum Exception: Error {
case Error(type:ExceptionType, Message: String)
}

125
Pods/SwiftSoup/Sources/FormElement.swift

@ -1,125 +0,0 @@
//
// FormElement.swift
// SwifSoup
//
// Created by Nabil Chatbi on 29/09/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
* A HTML Form Element provides ready access to the form fields/controls that are associated with it. It also allows a
* form to easily be submitted.
*/
public class FormElement: Element {
private let _elements: Elements = Elements()
/**
* Create a new, standalone form element.
*
* @param tag tag of this element
* @param baseUri the base URI
* @param attributes initial attributes
*/
public override init(_ tag: Tag, _ baseUri: String, _ attributes: Attributes) {
super.init(tag, baseUri, attributes)
}
/**
* Get the list of form control elements associated with this form.
* @return form controls associated with this element.
*/
public func elements() -> Elements {
return _elements
}
/**
* Add a form control element to this form.
* @param element form control to add
* @return this form element, for chaining
*/
@discardableResult
public func addElement(_ element: Element) -> FormElement {
_elements.add(element)
return self
}
//todo:
/**
* Prepare to submit this form. A Connection object is created with the request set up from the form values. You
* can then set up other options (like user-agent, timeout, cookies), then execute it.
* @return a connection prepared from the values of this form.
* @throws IllegalArgumentException if the form's absolute action URL cannot be determined. Make sure you pass the
* document's base URI when parsing.
*/
// public func submit()throws->Connection {
// let action: String = hasAttr("action") ? try absUrl("action") : try baseUri()
// Validate.notEmpty(action, "Could not determine a form action URL for submit. Ensure you set a base URI when parsing.")
// Connection.Method method = attr("method").toUpperCase().equals("POST") ?
// Connection.Method.POST : Connection.Method.GET
//
// return Jsoup.connect(action)
// .data(formData())
// .method(method)
// }
//todo:
/**
* Get the data that this form submits. The returned list is a copy of the data, and changes to the contents of the
* list will not be reflected in the DOM.
* @return a list of key vals
*/
// public List<Connection.KeyVal> formData() {
// ArrayList<Connection.KeyVal> data = new ArrayList<Connection.KeyVal>();
//
// // iterate the form control elements and accumulate their values
// for (Element el: elements) {
// if (!el.tag().isFormSubmittable()) continue; // contents are form listable, superset of submitable
// if (el.hasAttr("disabled")) continue; // skip disabled form inputs
// String name = el.attr("name");
// if (name.length() == 0) continue;
// String type = el.attr("type");
//
// if ("select".equals(el.tagName())) {
// Elements options = el.select("option[selected]");
// boolean set = false;
// for (Element option: options) {
// data.add(HttpConnection.KeyVal.create(name, option.val()));
// set = true;
// }
// if (!set) {
// Element option = el.select("option").first();
// if (option != null)
// data.add(HttpConnection.KeyVal.create(name, option.val()));
// }
// } else if ("checkbox".equalsIgnoreCase(type) || "radio".equalsIgnoreCase(type)) {
// // only add checkbox or radio if they have the checked attribute
// if (el.hasAttr("checked")) {
// final String val = el.val().length() > 0 ? el.val() : "on";
// data.add(HttpConnection.KeyVal.create(name, val));
// }
// } else {
// data.add(HttpConnection.KeyVal.create(name, el.val()));
// }
// }
// return data;
// }
public override func copy(with zone: NSZone? = nil) -> Any {
let clone = FormElement(_tag, baseUri!, attributes!)
return copy(clone: clone)
}
public override func copy(parent: Node?) -> Node {
let clone = FormElement(_tag, baseUri!, attributes!)
return copy(clone: clone, parent: parent)
}
public override func copy(clone: Node, parent: Node?) -> Node {
let clone = clone as! FormElement
for att in _elements.array() {
clone._elements.add(att)
}
return super.copy(clone: clone, parent: parent)
}
}

771
Pods/SwiftSoup/Sources/HtmlTreeBuilder.swift

@ -1,771 +0,0 @@
//
// HtmlTreeBuilder.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 24/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
* HTML Tree Builder; creates a DOM from Tokens.
*/
class HtmlTreeBuilder: TreeBuilder {
// tag searches
public static let TagsSearchInScope: [String] = ["applet", "caption", "html", "table", "td", "th", "marquee", "object"]
private static let TagSearchList: [String] = ["ol", "ul"]
private static let TagSearchButton: [String] = ["button"]
private static let TagSearchTableScope: [String] = ["html", "table"]
private static let TagSearchSelectScope: [String] = ["optgroup", "option"]
private static let TagSearchEndTags: [String] = ["dd", "dt", "li", "option", "optgroup", "p", "rp", "rt"]
private static let TagSearchSpecial: [String] = ["address", "applet", "area", "article", "aside", "base", "basefont", "bgsound",
"blockquote", "body", "br", "button", "caption", "center", "col", "colgroup", "command", "dd",
"details", "dir", "div", "dl", "dt", "embed", "fieldset", "figcaption", "figure", "footer", "form",
"frame", "frameset", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html",
"iframe", "img", "input", "isindex", "li", "link", "listing", "marquee", "menu", "meta", "nav",
"noembed", "noframes", "noscript", "object", "ol", "p", "param", "plaintext", "pre", "script",
"section", "select", "style", "summary", "table", "tbody", "td", "textarea", "tfoot", "th", "thead",
"title", "tr", "ul", "wbr", "xmp"]
private var _state: HtmlTreeBuilderState = HtmlTreeBuilderState.Initial // the current state
private var _originalState: HtmlTreeBuilderState = HtmlTreeBuilderState.Initial // original / marked state
private var baseUriSetFromDoc: Bool = false
private var headElement: Element? // the current head element
private var formElement: FormElement? // the current form element
private var contextElement: Element? // fragment parse context -- could be null even if fragment parsing
private var formattingElements: Array<Element?> = Array<Element?>() // active (open) formatting elements
private var pendingTableCharacters: Array<String> = Array<String>() // chars in table to be shifted out
private var emptyEnd: Token.EndTag = Token.EndTag() // reused empty end tag
private var _framesetOk: Bool = true // if ok to go into frameset
private var fosterInserts: Bool = false // if next inserts should be fostered
private var fragmentParsing: Bool = false // if parsing a fragment of html
public override init() {
super.init()
}
public override func defaultSettings() -> ParseSettings {
return ParseSettings.htmlDefault
}
override func parse(_ input: String, _ baseUri: String, _ errors: ParseErrorList, _ settings: ParseSettings)throws->Document {
_state = HtmlTreeBuilderState.Initial
baseUriSetFromDoc = false
return try super.parse(input, baseUri, errors, settings)
}
func parseFragment(_ inputFragment: String, _ context: Element?, _ baseUri: String, _ errors: ParseErrorList, _ settings: ParseSettings)throws->Array<Node> {
// context may be null
_state = HtmlTreeBuilderState.Initial
initialiseParse(inputFragment, baseUri, errors, settings)
contextElement = context
fragmentParsing = true
var root: Element? = nil
if let context = context {
if let d = context.ownerDocument() { // quirks setup:
doc.quirksMode(d.quirksMode())
}
// initialise the tokeniser state:
let contextTag: String = context.tagName()
if (StringUtil.inString(contextTag, haystack: "title", "textarea")) {
tokeniser.transition(TokeniserState.Rcdata)
} else if (StringUtil.inString(contextTag, haystack: "iframe", "noembed", "noframes", "style", "xmp")) {
tokeniser.transition(TokeniserState.Rawtext)
} else if (contextTag=="script") {
tokeniser.transition(TokeniserState.ScriptData)
} else if (contextTag==("noscript")) {
tokeniser.transition(TokeniserState.Data) // if scripting enabled, rawtext
} else if (contextTag=="plaintext") {
tokeniser.transition(TokeniserState.Data)
} else {
tokeniser.transition(TokeniserState.Data) // default
}
root = try Element(Tag.valueOf("html", settings), baseUri)
try Validate.notNull(obj: root)
try doc.appendChild(root!)
stack.append(root!)
resetInsertionMode()
// setup form element to nearest form on context (up ancestor chain). ensures form controls are associated
// with form correctly
let contextChain: Elements = context.parents()
contextChain.add(0, context)
for parent: Element in contextChain.array() {
if let x = (parent as? FormElement) {
formElement = x
break
}
}
}
try runParser()
if (context != nil && root != nil) {
return root!.getChildNodes()
} else {
return doc.getChildNodes()
}
}
@discardableResult
public override func process(_ token: Token)throws->Bool {
currentToken = token
return try self._state.process(token, self)
}
@discardableResult
func process(_ token: Token, _ state: HtmlTreeBuilderState)throws->Bool {
currentToken = token
return try state.process(token, self)
}
func transition(_ state: HtmlTreeBuilderState) {
self._state = state
}
func state() -> HtmlTreeBuilderState {
return _state
}
func markInsertionMode() {
_originalState = _state
}
func originalState() -> HtmlTreeBuilderState {
return _originalState
}
func framesetOk(_ framesetOk: Bool) {
self._framesetOk = framesetOk
}
func framesetOk() -> Bool {
return _framesetOk
}
func getDocument() -> Document {
return doc
}
func getBaseUri() -> String {
return baseUri
}
func maybeSetBaseUri(_ base: Element)throws {
if (baseUriSetFromDoc) { // only listen to the first <base href> in parse
return
}
let href: String = try base.absUrl("href")
if (href.count != 0) { // ignore <base target> etc
baseUri = href
baseUriSetFromDoc = true
try doc.setBaseUri(href) // set on the doc so doc.createElement(Tag) will get updated base, and to update all descendants
}
}
func isFragmentParsing() -> Bool {
return fragmentParsing
}
func error(_ state: HtmlTreeBuilderState) {
if (errors.canAddError() && currentToken != nil) {
errors.add(ParseError(reader.getPos(), "Unexpected token [\(currentToken!.tokenType())] when in state [\(state.rawValue)]"))
}
}
@discardableResult
func insert(_ startTag: Token.StartTag)throws->Element {
// handle empty unknown tags
// when the spec expects an empty tag, will directly hit insertEmpty, so won't generate this fake end tag.
if (startTag.isSelfClosing()) {
let el: Element = try insertEmpty(startTag)
stack.append(el)
tokeniser.transition(TokeniserState.Data) // handles <script />, otherwise needs breakout steps from script data
try tokeniser.emit(emptyEnd.reset().name(el.tagName())) // ensure we get out of whatever state we are in. emitted for yielded processing
return el
}
try Validate.notNull(obj: startTag._attributes)
let el: Element = try Element(Tag.valueOf(startTag.name(), settings), baseUri, settings.normalizeAttributes(startTag._attributes))
try insert(el)
return el
}
@discardableResult
func insertStartTag(_ startTagName: String)throws->Element {
let el: Element = try Element(Tag.valueOf(startTagName, settings), baseUri)
try insert(el)
return el
}
func insert(_ el: Element)throws {
try insertNode(el)
stack.append(el)
}
@discardableResult
func insertEmpty(_ startTag: Token.StartTag)throws->Element {
let tag: Tag = try Tag.valueOf(startTag.name(), settings)
try Validate.notNull(obj: startTag._attributes)
let el: Element = Element(tag, baseUri, startTag._attributes)
try insertNode(el)
if (startTag.isSelfClosing()) {
if (tag.isKnownTag()) {
if (tag.isSelfClosing()) {tokeniser.acknowledgeSelfClosingFlag()} // if not acked, promulagates error
} else {
// unknown tag, remember this is self closing for output
tag.setSelfClosing()
tokeniser.acknowledgeSelfClosingFlag() // not an distinct error
}
}
return el
}
@discardableResult
func insertForm(_ startTag: Token.StartTag, _ onStack: Bool)throws->FormElement {
let tag: Tag = try Tag.valueOf(startTag.name(), settings)
try Validate.notNull(obj: startTag._attributes)
let el: FormElement = FormElement(tag, baseUri, startTag._attributes)
setFormElement(el)
try insertNode(el)
if (onStack) {
stack.append(el)
}
return el
}
func insert(_ commentToken: Token.Comment)throws {
let comment: Comment = Comment(commentToken.getData(), baseUri)
try insertNode(comment)
}
func insert(_ characterToken: Token.Char)throws {
var node: Node
// characters in script and style go in as datanodes, not text nodes
let tagName: String? = currentElement()?.tagName()
if (tagName=="script" || tagName=="style") {
try Validate.notNull(obj: characterToken.getData())
node = DataNode(characterToken.getData()!, baseUri)
} else {
try Validate.notNull(obj: characterToken.getData())
node = TextNode(characterToken.getData()!, baseUri)
}
try currentElement()?.appendChild(node) // doesn't use insertNode, because we don't foster these; and will always have a stack.
}
private func insertNode(_ node: Node)throws {
// if the stack hasn't been set up yet, elements (doctype, comments) go into the doc
if (stack.count == 0) {
try doc.appendChild(node)
} else if (isFosterInserts()) {
try insertInFosterParent(node)
} else {
try currentElement()?.appendChild(node)
}
// connect form controls to their form element
if let n = (node as? Element) {
if(n.tag().isFormListed()) {
if ( formElement != nil) {
formElement!.addElement(n)
}
}
}
}
@discardableResult
func pop() -> Element {
let size: Int = stack.count
return stack.remove(at: size-1)
}
func push(_ element: Element) {
stack.append(element)
}
func getStack()->Array<Element> {
return stack
}
@discardableResult
func onStack(_ el: Element) -> Bool {
return isElementInQueue(stack, el)
}
private func isElementInQueue(_ queue: Array<Element?>, _ element: Element?) -> Bool {
for pos in (0..<queue.count).reversed() {
let next: Element? = queue[pos]
if (next == element) {
return true
}
}
return false
}
func getFromStack(_ elName: String) -> Element? {
for pos in (0..<stack.count).reversed() {
let next: Element = stack[pos]
if next.nodeName() == elName {
return next
}
}
return nil
}
@discardableResult
func removeFromStack(_ el: Element) -> Bool {
for pos in (0..<stack.count).reversed() {
let next: Element = stack[pos]
if (next == el) {
stack.remove(at: pos)
return true
}
}
return false
}
func popStackToClose(_ elName: String) {
for pos in (0..<stack.count).reversed() {
let next: Element = stack[pos]
stack.remove(at: pos)
if (next.nodeName() == elName) {
break
}
}
}
func popStackToClose(_ elNames: String...) {
popStackToClose(elNames)
}
func popStackToClose(_ elNames: [String]) {
for pos in (0..<stack.count).reversed() {
let next: Element = stack[pos]
stack.remove(at: pos)
if (StringUtil.inString(next.nodeName(), elNames)) {
break
}
}
}
func popStackToBefore(_ elName: String) {
for pos in (0..<stack.count).reversed() {
let next: Element = stack[pos]
if (next.nodeName() == elName) {
break
} else {
stack.remove(at: pos)
}
}
}
func clearStackToTableContext() {
clearStackToContext("table")
}
func clearStackToTableBodyContext() {
clearStackToContext("tbody", "tfoot", "thead")
}
func clearStackToTableRowContext() {
clearStackToContext("tr")
}
private func clearStackToContext(_ nodeNames: String...) {
clearStackToContext(nodeNames)
}
private func clearStackToContext(_ nodeNames: [String]) {
for pos in (0..<stack.count).reversed() {
let next: Element = stack[pos]
if (StringUtil.inString(next.nodeName(), nodeNames) || next.nodeName()=="html") {
break
} else {
stack.remove(at: pos)
}
}
}
func aboveOnStack(_ el: Element) -> Element? {
//assert(onStack(el), "Invalid parameter")
onStack(el)
for pos in (0..<stack.count).reversed() {
let next: Element = stack[pos]
if (next == el) {
return stack[pos-1]
}
}
return nil
}
func insertOnStackAfter(_ after: Element, _ input: Element)throws {
let i: Int = stack.lastIndexOf(after)
try Validate.isTrue(val: i != -1)
stack.insert(input, at: i + 1 )
}
func replaceOnStack(_ out: Element, _ input: Element)throws {
try stack = replaceInQueue(stack, out, input)
}
private func replaceInQueue(_ queue: Array<Element>, _ out: Element, _ input: Element)throws->Array<Element> {
var queue = queue
let i: Int = queue.lastIndexOf(out)
try Validate.isTrue(val: i != -1)
queue[i] = input
return queue
}
private func replaceInQueue(_ queue: Array<Element?>, _ out: Element, _ input: Element)throws->Array<Element?> {
var queue = queue
var i: Int = -1
for index in 0..<queue.count {
if(out == queue[index]) {
i = index
}
}
try Validate.isTrue(val: i != -1)
queue[i] = input
return queue
}
func resetInsertionMode() {
var last = false
for pos in (0..<stack.count).reversed() {
var node: Element = stack[pos]
if (pos == 0) {
last = true
//Validate node
node = contextElement!
}
let name: String = node.nodeName()
if ("select".equals(name)) {
transition(HtmlTreeBuilderState.InSelect)
break // frag
} else if (("td".equals(name) || "th".equals(name) && !last)) {
transition(HtmlTreeBuilderState.InCell)
break
} else if ("tr".equals(name)) {
transition(HtmlTreeBuilderState.InRow)
break
} else if ("tbody".equals(name) || "thead".equals(name) || "tfoot".equals(name)) {
transition(HtmlTreeBuilderState.InTableBody)
break
} else if ("caption".equals(name)) {
transition(HtmlTreeBuilderState.InCaption)
break
} else if ("colgroup".equals(name)) {
transition(HtmlTreeBuilderState.InColumnGroup)
break // frag
} else if ("table".equals(name)) {
transition(HtmlTreeBuilderState.InTable)
break
} else if ("head".equals(name)) {
transition(HtmlTreeBuilderState.InBody)
break // frag
} else if ("body".equals(name)) {
transition(HtmlTreeBuilderState.InBody)
break
} else if ("frameset".equals(name)) {
transition(HtmlTreeBuilderState.InFrameset)
break // frag
} else if ("html".equals(name)) {
transition(HtmlTreeBuilderState.BeforeHead)
break // frag
} else if (last) {
transition(HtmlTreeBuilderState.InBody)
break // frag
}
}
}
// todo: tidy up in specific scope methods
private var specificScopeTarget: [String?] = [nil]
private func inSpecificScope(_ targetName: String, _ baseTypes: [String], _ extraTypes: [String]?)throws->Bool {
specificScopeTarget[0] = targetName
return try inSpecificScope(specificScopeTarget, baseTypes, extraTypes)
}
private func inSpecificScope(_ targetNames: [String?], _ baseTypes: [String], _ extraTypes: [String]?)throws->Bool {
for pos in (0..<stack.count).reversed() {
let el: Element = stack[pos]
let elName: String = el.nodeName()
if (StringUtil.inString(elName, targetNames)) {
return true
}
if (StringUtil.inString(elName, baseTypes)) {
return false
}
if (extraTypes != nil && StringUtil.inString(elName, extraTypes!)) {
return false
}
}
try Validate.fail(msg: "Should not be reachable")
return false
}
func inScope(_ targetNames: [String])throws->Bool {
return try inSpecificScope(targetNames, HtmlTreeBuilder.TagsSearchInScope, nil)
}
func inScope(_ targetName: String)throws->Bool {
return try inScope(targetName, nil)
}
func inScope(_ targetName: String, _ extras: [String]?)throws->Bool {
return try inSpecificScope(targetName, HtmlTreeBuilder.TagsSearchInScope, extras)
// todo: in mathml namespace: mi, mo, mn, ms, mtext annotation-xml
// todo: in svg namespace: forignOjbect, desc, title
}
func inListItemScope(_ targetName: String)throws->Bool {
return try inScope(targetName, HtmlTreeBuilder.TagSearchList)
}
func inButtonScope(_ targetName: String)throws->Bool {
return try inScope(targetName, HtmlTreeBuilder.TagSearchButton)
}
func inTableScope(_ targetName: String)throws->Bool {
return try inSpecificScope(targetName, HtmlTreeBuilder.TagSearchTableScope, nil)
}
func inSelectScope(_ targetName: String)throws->Bool {
for pos in (0..<stack.count).reversed() {
let el: Element = stack[pos]
let elName: String = el.nodeName()
if (elName.equals(targetName)) {
return true
}
if (!StringUtil.inString(elName, HtmlTreeBuilder.TagSearchSelectScope)) { // all elements except
return false
}
}
try Validate.fail(msg: "Should not be reachable")
return false
}
func setHeadElement(_ headElement: Element) {
self.headElement = headElement
}
func getHeadElement() -> Element? {
return headElement
}
func isFosterInserts() -> Bool {
return fosterInserts
}
func setFosterInserts(_ fosterInserts: Bool) {
self.fosterInserts = fosterInserts
}
func getFormElement() -> FormElement? {
return formElement
}
func setFormElement(_ formElement: FormElement?) {
self.formElement = formElement
}
func newPendingTableCharacters() {
pendingTableCharacters = Array<String>()
}
func getPendingTableCharacters()->Array<String> {
return pendingTableCharacters
}
func setPendingTableCharacters(_ pendingTableCharacters: Array<String>) {
self.pendingTableCharacters = pendingTableCharacters
}
/**
11.2.5.2 Closing elements that have implied end tags<p/>
When the steps below require the UA to generate implied end tags, then, while the current node is a dd element, a
dt element, an li element, an option element, an optgroup element, a p element, an rp element, or an rt element,
the UA must pop the current node off the stack of open elements.
@param excludeTag If a step requires the UA to generate implied end tags but lists an element to exclude from the
process, then the UA must perform the above steps as if that element was not in the above list.
*/
func generateImpliedEndTags(_ excludeTag: String?) {
while ((excludeTag != nil && !currentElement()!.nodeName().equals(excludeTag!)) &&
StringUtil.inString(currentElement()!.nodeName(), HtmlTreeBuilder.TagSearchEndTags)) {
pop()
}
}
func generateImpliedEndTags() {
generateImpliedEndTags(nil)
}
func isSpecial(_ el: Element) -> Bool {
// todo: mathml's mi, mo, mn
// todo: svg's foreigObject, desc, title
let name: String = el.nodeName()
return StringUtil.inString(name, HtmlTreeBuilder.TagSearchSpecial)
}
func lastFormattingElement() -> Element? {
return formattingElements.count > 0 ? formattingElements[formattingElements.count-1] : nil
}
func removeLastFormattingElement() -> Element? {
let size: Int = formattingElements.count
if (size > 0) {
return formattingElements.remove(at: size-1)
} else {
return nil
}
}
// active formatting elements
func pushActiveFormattingElements(_ input: Element) {
var numSeen: Int = 0
for pos in (0..<formattingElements.count).reversed() {
let el: Element? = formattingElements[pos]
if (el == nil) { // marker
break
}
if (isSameFormattingElement(input, el!)) {
numSeen += 1
}
if (numSeen == 3) {
formattingElements.remove(at: pos)
break
}
}
formattingElements.append(input)
}
private func isSameFormattingElement(_ a: Element, _ b: Element) -> Bool {
// same if: same namespace, tag, and attributes. Element.equals only checks tag, might in future check children
if(a.attributes == nil) {
return false
}
return a.nodeName().equals(b.nodeName()) &&
// a.namespace().equals(b.namespace()) &&
a.getAttributes()!.equals(o: b.getAttributes())
// todo: namespaces
}
func reconstructFormattingElements()throws {
let last: Element? = lastFormattingElement()
if (last == nil || onStack(last!)) {
return
}
var entry: Element? = last
let size: Int = formattingElements.count
var pos: Int = size - 1
var skip: Bool = false
while (true) {
if (pos == 0) { // step 4. if none before, skip to 8
skip = true
break
}
pos -= 1
entry = formattingElements[pos] // step 5. one earlier than entry
if (entry == nil || onStack(entry!)) // step 6 - neither marker nor on stack
{break} // jump to 8, else continue back to 4
}
while(true) {
if (!skip) // step 7: on later than entry
{
pos += 1
entry = formattingElements[pos]
}
try Validate.notNull(obj: entry) // should not occur, as we break at last element
// 8. create new element from element, 9 insert into current node, onto stack
skip = false // can only skip increment from 4.
let newEl: Element = try insertStartTag(entry!.nodeName()) // todo: avoid fostering here?
// newEl.namespace(entry.namespace()) // todo: namespaces
newEl.getAttributes()?.addAll(incoming: entry!.getAttributes())
// 10. replace entry with new entry
formattingElements[pos] = newEl
// 11
if (pos == size-1) // if not last entry in list, jump to 7
{break}
}
}
func clearFormattingElementsToLastMarker() {
while (!formattingElements.isEmpty) {
let el: Element? = removeLastFormattingElement()
if (el == nil) {
break
}
}
}
func removeFromActiveFormattingElements(_ el: Element?) {
for pos in (0..<formattingElements.count).reversed() {
let next: Element? = formattingElements[pos]
if (next == el) {
formattingElements.remove(at: pos)
break
}
}
}
func isInActiveFormattingElements(_ el: Element) -> Bool {
return isElementInQueue(formattingElements, el)
}
func getActiveFormattingElement(_ nodeName: String) -> Element? {
for pos in (0..<formattingElements.count).reversed() {
let next: Element? = formattingElements[pos]
if (next == nil) { // scope marker
break
} else if (next!.nodeName().equals(nodeName)) {
return next
}
}
return nil
}
func replaceActiveFormattingElement(_ out: Element, _ input: Element)throws {
try formattingElements = replaceInQueue(formattingElements, out, input)
}
func insertMarkerToFormattingElements() {
formattingElements.append(nil)
}
func insertInFosterParent(_ input: Node)throws {
let fosterParent: Element?
let lastTable: Element? = getFromStack("table")
var isLastTableParent: Bool = false
if let lastTable = lastTable {
if (lastTable.parent() != nil) {
fosterParent = lastTable.parent()!
isLastTableParent = true
} else {
fosterParent = aboveOnStack(lastTable)
}
} else { // no table == frag
fosterParent = stack[0]
}
if (isLastTableParent) {
try Validate.notNull(obj: lastTable) // last table cannot be null by this point.
try lastTable!.before(input)
} else {
try fosterParent?.appendChild(input)
}
}
}

1549
Pods/SwiftSoup/Sources/HtmlTreeBuilderState.swift

File diff suppressed because it is too large

10
Pods/SwiftSoup/Sources/HttpStatusException.swift

@ -1,10 +0,0 @@
//
// HttpStatusException.swift
// SwifSoup
//
// Created by Nabil Chatbi on 29/09/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
//TODO:

808
Pods/SwiftSoup/Sources/Node.swift

@ -1,808 +0,0 @@
//
// Node.swift
// SwifSoup
//
// Created by Nabil Chatbi on 29/09/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
open class Node: Equatable, Hashable {
private static let abs = "abs:"
fileprivate static let empty = ""
private static let EMPTY_NODES: Array<Node> = Array<Node>()
weak var parentNode: Node?
var childNodes: Array <Node>
var attributes: Attributes?
var baseUri: String?
/**
* Get the list index of this node in its node sibling list. I.e. if this is the first node
* sibling, returns 0.
* @return position in node sibling list
* @see org.jsoup.nodes.Element#elementSiblingIndex()
*/
public private(set) var siblingIndex: Int = 0
/**
Create a new Node.
@param baseUri base URI
@param attributes attributes (not null, but may be empty)
*/
public init(_ baseUri: String, _ attributes: Attributes) {
self.childNodes = Node.EMPTY_NODES
self.baseUri = baseUri.trim()
self.attributes = attributes
}
public init(_ baseUri: String) {
childNodes = Node.EMPTY_NODES
self.baseUri = baseUri.trim()
self.attributes = Attributes()
}
/**
* Default constructor. Doesn't setup base uri, children, or attributes; use with caution.
*/
public init() {
self.childNodes = Node.EMPTY_NODES
self.attributes = nil
self.baseUri = nil
}
/**
Get the node name of this node. Use for debugging purposes and not logic switching (for that, use instanceof).
@return node name
*/
public func nodeName() -> String {
preconditionFailure("This method must be overridden")
}
/**
* Get an attribute's value by its key. <b>Case insensitive</b>
* <p>
* To get an absolute URL from an attribute that may be a relative URL, prefix the key with <code><b>abs</b></code>,
* which is a shortcut to the {@link #absUrl} method.
* </p>
* E.g.:
* <blockquote><code>String url = a.attr("abs:href");</code></blockquote>
*
* @param attributeKey The attribute key.
* @return The attribute, or empty string if not present (to avoid nulls).
* @see #attributes()
* @see #hasAttr(String)
* @see #absUrl(String)
*/
open func attr(_ attributeKey: String)throws ->String {
let val: String = try attributes!.getIgnoreCase(key: attributeKey)
if (val.count > 0) {
return val
} else if (attributeKey.lowercased().startsWith(Node.abs)) {
return try absUrl(attributeKey.substring(Node.abs.count))
} else {return Node.empty}
}
/**
* Get all of the element's attributes.
* @return attributes (which implements iterable, in same order as presented in original HTML).
*/
open func getAttributes() -> Attributes? {
return attributes
}
/**
* Set an attribute (key=value). If the attribute already exists, it is replaced.
* @param attributeKey The attribute key.
* @param attributeValue The attribute value.
* @return this (for chaining)
*/
@discardableResult
open func attr(_ attributeKey: String, _ attributeValue: String)throws->Node {
try attributes?.put(attributeKey, attributeValue)
return self
}
/**
* Test if this element has an attribute. <b>Case insensitive</b>
* @param attributeKey The attribute key to check.
* @return true if the attribute exists, false if not.
*/
open func hasAttr(_ attributeKey: String) -> Bool {
guard let attributes = attributes else {
return false
}
if (attributeKey.startsWith(Node.abs)) {
let key: String = attributeKey.substring(Node.abs.count)
do {
let abs = try absUrl(key)
if (attributes.hasKeyIgnoreCase(key: key) && !Node.empty.equals(abs)) {
return true
}
} catch {
return false
}
}
return attributes.hasKeyIgnoreCase(key: attributeKey)
}
/**
* Remove an attribute from this element.
* @param attributeKey The attribute to remove.
* @return this (for chaining)
*/
@discardableResult
open func removeAttr(_ attributeKey: String)throws->Node {
try attributes?.removeIgnoreCase(key: attributeKey)
return self
}
/**
Get the base URI of this node.
@return base URI
*/
open func getBaseUri() -> String {
return baseUri!
}
/**
Update the base URI of this node and all of its descendants.
@param baseUri base URI to set
*/
open func setBaseUri(_ baseUri: String)throws {
class nodeVisitor: NodeVisitor {
private let baseUri: String
init(_ baseUri: String) {
self.baseUri = baseUri
}
func head(_ node: Node, _ depth: Int)throws {
node.baseUri = baseUri
}
func tail(_ node: Node, _ depth: Int)throws {
}
}
try traverse(nodeVisitor(baseUri))
}
/**
* Get an absolute URL from a URL attribute that may be relative (i.e. an <code>&lta href&gt;</code> or
* <code>&lt;img src&gt;</code>).
* <p>
* E.g.: <code>String absUrl = linkEl.absUrl("href");</code>
* </p>
* <p>
* If the attribute value is already absolute (i.e. it starts with a protocol, like
* <code>http://</code> or <code>https://</code> etc), and it successfully parses as a URL, the attribute is
* returned directly. Otherwise, it is treated as a URL relative to the element's {@link #baseUri}, and made
* absolute using that.
* </p>
* <p>
* As an alternate, you can use the {@link #attr} method with the <code>abs:</code> prefix, e.g.:
* <code>String absUrl = linkEl.attr("abs:href");</code>
* </p>
*
* @param attributeKey The attribute key
* @return An absolute URL if one could be made, or an empty string (not null) if the attribute was missing or
* could not be made successfully into a URL.
* @see #attr
* @see java.net.URL#URL(java.net.URL, String)
*/
open func absUrl(_ attributeKey: String)throws->String {
try Validate.notEmpty(string: attributeKey)
if (!hasAttr(attributeKey)) {
return Node.empty // nothing to make absolute with
} else {
return StringUtil.resolve(baseUri!, relUrl: try attr(attributeKey))
}
}
/**
Get a child node by its 0-based index.
@param index index of child node
@return the child node at this index. Throws a {@code IndexOutOfBoundsException} if the index is out of bounds.
*/
open func childNode(_ index: Int) -> Node {
return childNodes[index]
}
/**
Get this node's children. Presented as an unmodifiable list: new children can not be added, but the child nodes
themselves can be manipulated.
@return list of children. If no children, returns an empty list.
*/
open func getChildNodes()->Array<Node> {
return childNodes
}
/**
* Returns a deep copy of this node's children. Changes made to these nodes will not be reflected in the original
* nodes
* @return a deep copy of this node's children
*/
open func childNodesCopy()->Array<Node> {
var children: Array<Node> = Array<Node>()
for node: Node in childNodes {
children.append(node.copy() as! Node)
}
return children
}
/**
* Get the number of child nodes that this node holds.
* @return the number of child nodes that this node holds.
*/
public func childNodeSize() -> Int {
return childNodes.count
}
final func childNodesAsArray() -> [Node] {
return childNodes as Array
}
/**
Gets this node's parent node.
@return parent node or null if no parent.
*/
open func parent() -> Node? {
return parentNode
}
/**
Gets this node's parent node. Node overridable by extending classes, so useful if you really just need the Node type.
@return parent node or null if no parent.
*/
final func getParentNode() -> Node? {
return parentNode
}
/**
* Gets the Document associated with this Node.
* @return the Document associated with this Node, or null if there is no such Document.
*/
open func ownerDocument() -> Document? {
if let this = self as? Document {
return this
} else if (parentNode == nil) {
return nil
} else {
return parentNode!.ownerDocument()
}
}
/**
* Remove (delete) this node from the DOM tree. If this node has children, they are also removed.
*/
open func remove()throws {
try parentNode?.removeChild(self)
}
/**
* Insert the specified HTML into the DOM before this node (i.e. as a preceding sibling).
* @param html HTML to add before this node
* @return this node, for chaining
* @see #after(String)
*/
@discardableResult
open func before(_ html: String)throws->Node {
try addSiblingHtml(siblingIndex, html)
return self
}
/**
* Insert the specified node into the DOM before this node (i.e. as a preceding sibling).
* @param node to add before this node
* @return this node, for chaining
* @see #after(Node)
*/
@discardableResult
open func before(_ node: Node)throws ->Node {
try Validate.notNull(obj: node)
try Validate.notNull(obj: parentNode)
try parentNode?.addChildren(siblingIndex, node)
return self
}
/**
* Insert the specified HTML into the DOM after this node (i.e. as a following sibling).
* @param html HTML to add after this node
* @return this node, for chaining
* @see #before(String)
*/
@discardableResult
open func after(_ html: String)throws ->Node {
try addSiblingHtml(siblingIndex + 1, html)
return self
}
/**
* Insert the specified node into the DOM after this node (i.e. as a following sibling).
* @param node to add after this node
* @return this node, for chaining
* @see #before(Node)
*/
@discardableResult
open func after(_ node: Node)throws->Node {
try Validate.notNull(obj: node)
try Validate.notNull(obj: parentNode)
try parentNode?.addChildren(siblingIndex+1, node)
return self
}
private func addSiblingHtml(_ index: Int, _ html: String)throws {
try Validate.notNull(obj: parentNode)
let context: Element? = parent() as? Element
let nodes: Array<Node> = try Parser.parseFragment(html, context, getBaseUri())
try parentNode?.addChildren(index, nodes)
}
/**
* Insert the specified HTML into the DOM after this node (i.e. as a following sibling).
* @param html HTML to add after this node
* @return this node, for chaining
* @see #before(String)
*/
@discardableResult
open func after(html: String)throws->Node {
try addSiblingHtml(siblingIndex + 1, html)
return self
}
/**
* Insert the specified node into the DOM after this node (i.e. as a following sibling).
* @param node to add after this node
* @return this node, for chaining
* @see #before(Node)
*/
@discardableResult
open func after(node: Node)throws->Node {
try Validate.notNull(obj: node)
try Validate.notNull(obj: parentNode)
try parentNode?.addChildren(siblingIndex + 1, node)
return self
}
open func addSiblingHtml(index: Int, _ html: String)throws {
try Validate.notNull(obj: html)
try Validate.notNull(obj: parentNode)
let context: Element? = parent() as? Element
let nodes: Array<Node> = try Parser.parseFragment(html, context, getBaseUri())
try parentNode?.addChildren(index, nodes)
}
/**
Wrap the supplied HTML around this node.
@param html HTML to wrap around this element, e.g. {@code <div class="head"></div>}. Can be arbitrarily deep.
@return this node, for chaining.
*/
@discardableResult
open func wrap(_ html: String)throws->Node? {
try Validate.notEmpty(string: html)
let context: Element? = parent() as? Element
var wrapChildren: Array<Node> = try Parser.parseFragment(html, context, getBaseUri())
let wrapNode: Node? = wrapChildren.count > 0 ? wrapChildren[0] : nil
if (wrapNode == nil || !(((wrapNode as? Element) != nil))) { // nothing to wrap with; noop
return nil
}
let wrap: Element = wrapNode as! Element
let deepest: Element = getDeepChild(el: wrap)
try parentNode?.replaceChild(self, wrap)
wrapChildren = wrapChildren.filter { $0 != wrap}
try deepest.addChildren(self)
// remainder (unbalanced wrap, like <div></div><p></p> -- The <p> is remainder
if (wrapChildren.count > 0) {
for i in 0..<wrapChildren.count {
let remainder: Node = wrapChildren[i]
try remainder.parentNode?.removeChild(remainder)
try wrap.appendChild(remainder)
}
}
return self
}
/**
* Removes this node from the DOM, and moves its children up into the node's parent. This has the effect of dropping
* the node but keeping its children.
* <p>
* For example, with the input html:
* </p>
* <p>{@code <div>One <span>Two <b>Three</b></span></div>}</p>
* Calling {@code element.unwrap()} on the {@code span} element will result in the html:
* <p>{@code <div>One Two <b>Three</b></div>}</p>
* and the {@code "Two "} {@link TextNode} being returned.
*
* @return the first child of this node, after the node has been unwrapped. Null if the node had no children.
* @see #remove()
* @see #wrap(String)
*/
@discardableResult
open func unwrap()throws ->Node? {
try Validate.notNull(obj: parentNode)
let firstChild: Node? = childNodes.count > 0 ? childNodes[0] : nil
try parentNode?.addChildren(siblingIndex, self.childNodesAsArray())
try self.remove()
return firstChild
}
private func getDeepChild(el: Element) -> Element {
let children = el.children()
if (children.size() > 0) {
return getDeepChild(el: children.get(0))
} else {
return el
}
}
/**
* Replace this node in the DOM with the supplied node.
* @param in the node that will will replace the existing node.
*/
public func replaceWith(_ input: Node)throws {
try Validate.notNull(obj: input)
try Validate.notNull(obj: parentNode)
try parentNode?.replaceChild(self, input)
}
public func setParentNode(_ parentNode: Node)throws {
if (self.parentNode != nil) {
try self.parentNode?.removeChild(self)
}
self.parentNode = parentNode
}
public func replaceChild(_ out: Node, _ input: Node)throws {
try Validate.isTrue(val: out.parentNode === self)
try Validate.notNull(obj: input)
if (input.parentNode != nil) {
try input.parentNode?.removeChild(input)
}
let index: Int = out.siblingIndex
childNodes[index] = input
input.parentNode = self
input.setSiblingIndex(index)
out.parentNode = nil
}
public func removeChild(_ out: Node)throws {
try Validate.isTrue(val: out.parentNode === self)
let index: Int = out.siblingIndex
childNodes.remove(at: index)
reindexChildren(index)
out.parentNode = nil
}
public func addChildren(_ children: Node...)throws {
//most used. short circuit addChildren(int), which hits reindex children and array copy
try addChildren(children)
}
public func addChildren(_ children: [Node])throws {
//most used. short circuit addChildren(int), which hits reindex children and array copy
for child in children {
try reparentChild(child)
ensureChildNodes()
childNodes.append(child)
child.setSiblingIndex(childNodes.count-1)
}
}
public func addChildren(_ index: Int, _ children: Node...)throws {
try addChildren(index, children)
}
public func addChildren(_ index: Int, _ children: [Node])throws {
ensureChildNodes()
for i in (0..<children.count).reversed() {
let input: Node = children[i]
try reparentChild(input)
childNodes.insert(input, at: index)
reindexChildren(index)
}
}
public func ensureChildNodes() {
// if (childNodes === Node.EMPTY_NODES) {
// childNodes = Array<Node>()
// }
}
public func reparentChild(_ child: Node)throws {
if (child.parentNode != nil) {
try child.parentNode?.removeChild(child)
}
try child.setParentNode(self)
}
private func reindexChildren(_ start: Int) {
for i in start..<childNodes.count {
childNodes[i].setSiblingIndex(i)
}
}
/**
Retrieves this node's sibling nodes. Similar to {@link #childNodes() node.parent.childNodes()}, but does not
include this node (a node is not a sibling of itself).
@return node siblings. If the node has no parent, returns an empty list.
*/
open func siblingNodes()->Array<Node> {
if (parentNode == nil) {
return Array<Node>()
}
let nodes: Array<Node> = parentNode!.childNodes
var siblings: Array<Node> = Array<Node>()
for node in nodes {
if (node !== self) {
siblings.append(node)
}
}
return siblings
}
/**
Get this node's next sibling.
@return next sibling, or null if this is the last sibling
*/
open func nextSibling() -> Node? {
guard let siblings: Array<Node> = parentNode?.childNodes else {
return nil
}
let index: Int = siblingIndex+1
if (siblings.count > index) {
return siblings[index]
} else {
return nil
}
}
/**
Get this node's previous sibling.
@return the previous sibling, or null if this is the first sibling
*/
open func previousSibling() -> Node? {
if (parentNode == nil) {
return nil // root
}
if (siblingIndex > 0) {
return parentNode?.childNodes[siblingIndex-1]
} else {
return nil
}
}
public func setSiblingIndex(_ siblingIndex: Int) {
self.siblingIndex = siblingIndex
}
/**
* Perform a depth-first traversal through this node and its descendants.
* @param nodeVisitor the visitor callbacks to perform on each node
* @return this node, for chaining
*/
@discardableResult
open func traverse(_ nodeVisitor: NodeVisitor)throws->Node {
let traversor: NodeTraversor = NodeTraversor(nodeVisitor)
try traversor.traverse(self)
return self
}
/**
Get the outer HTML of this node.
@return HTML
*/
open func outerHtml()throws->String {
let accum: StringBuilder = StringBuilder(128)
try outerHtml(accum)
return accum.toString()
}
public func outerHtml(_ accum: StringBuilder)throws {
try NodeTraversor(OuterHtmlVisitor(accum, getOutputSettings())).traverse(self)
}
// if this node has no document (or parent), retrieve the default output settings
func getOutputSettings() -> OutputSettings {
return ownerDocument() != nil ? ownerDocument()!.outputSettings() : (Document(Node.empty)).outputSettings()
}
/**
Get the outer HTML of this node.
@param accum accumulator to place HTML into
@throws IOException if appending to the given accumulator fails.
*/
func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) throws {
preconditionFailure("This method must be overridden")
}
func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) throws {
preconditionFailure("This method must be overridden")
}
/**
* Write this node and its children to the given {@link Appendable}.
*
* @param appendable the {@link Appendable} to write to.
* @return the supplied {@link Appendable}, for chaining.
*/
open func html(_ appendable: StringBuilder)throws -> StringBuilder {
try outerHtml(appendable)
return appendable
}
public func indent(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {
accum.append(UnicodeScalar.BackslashN).append(StringUtil.padding(depth * Int(out.indentAmount())))
}
/**
* Check if this node is the same instance of another (object identity test).
* @param o other object to compare to
* @return true if the content of this node is the same as the other
* @see Node#hasSameValue(Object) to compare nodes by their value
*/
open func equals(_ o: Node) -> Bool {
// implemented just so that javadoc is clear this is an identity test
return self === o
}
/**
* Check if this node is has the same content as another node. A node is considered the same if its name, attributes and content match the
* other node; particularly its position in the tree does not influence its similarity.
* @param o other object to compare to
* @return true if the content of this node is the same as the other
*/
open func hasSameValue(_ o: Node)throws->Bool {
if (self === o) {return true}
// if (type(of:self) != type(of: o))
// {
// return false
// }
return try self.outerHtml() == o.outerHtml()
}
/**
* Create a stand-alone, deep copy of this node, and all of its children. The cloned node will have no siblings or
* parent node. As a stand-alone object, any changes made to the clone or any of its children will not impact the
* original node.
* <p>
* The cloned node may be adopted into another Document or node structure using {@link Element#appendChild(Node)}.
* @return stand-alone cloned node
*/
public func copy(with zone: NSZone? = nil) -> Any {
return copy(clone: Node())
}
public func copy(parent: Node?) -> Node {
let clone = Node()
return copy(clone: clone, parent: parent)
}
public func copy(clone: Node) -> Node {
let thisClone: Node = copy(clone: clone, parent: nil) // splits for orphan
// Queue up nodes that need their children cloned (BFS).
var nodesToProcess: Array<Node> = Array<Node>()
nodesToProcess.append(thisClone)
while (!nodesToProcess.isEmpty) {
let currParent: Node = nodesToProcess.removeFirst()
for i in 0..<currParent.childNodes.count {
let childClone: Node = currParent.childNodes[i].copy(parent: currParent)
currParent.childNodes[i] = childClone
nodesToProcess.append(childClone)
}
}
return thisClone
}
/*
* Return a clone of the node using the given parent (which can be null).
* Not a deep copy of children.
*/
public func copy(clone: Node, parent: Node?) -> Node {
clone.parentNode = parent // can be null, to create an orphan split
clone.siblingIndex = parent == nil ? 0 : siblingIndex
clone.attributes = attributes != nil ? attributes?.clone() : nil
clone.baseUri = baseUri
clone.childNodes = Array<Node>()
for child in childNodes {
clone.childNodes.append(child)
}
return clone
}
private class OuterHtmlVisitor: NodeVisitor {
private var accum: StringBuilder
private var out: OutputSettings
static private let text = "#text"
init(_ accum: StringBuilder, _ out: OutputSettings) {
self.accum = accum
self.out = out
}
open func head(_ node: Node, _ depth: Int)throws {
try node.outerHtmlHead(accum, depth, out)
}
open func tail(_ node: Node, _ depth: Int)throws {
// When compiling a release optimized swift linux 4.2 version the "saves a void hit."
// causes a SIL error. Removing optimization on linux until a fix is found.
#if os(Linux)
try node.outerHtmlTail(accum, depth, out)
#else
if (!(node.nodeName() == OuterHtmlVisitor.text)) { // saves a void hit.
try node.outerHtmlTail(accum, depth, out)
}
#endif
}
}
/// Returns a Boolean value indicating whether two values are equal.
///
/// Equality is the inverse of inequality. For any values `a` and `b`,
/// `a == b` implies that `a != b` is `false`.
///
/// - Parameters:
/// - lhs: A value to compare.
/// - rhs: Another value to compare.
public static func ==(lhs: Node, rhs: Node) -> Bool {
return lhs === rhs
}
/// The hash value.
///
/// Hash values are not guaranteed to be equal across different executions of
/// your program. Do not save hash values to use during a future execution.
public func hash(into hasher: inout Hasher) {
hasher.combine(description)
hasher.combine(baseUri)
}
}
extension Node: CustomStringConvertible {
public var description: String {
do {
return try outerHtml()
} catch {
}
return Node.empty
}
}
extension Node: CustomDebugStringConvertible {
private static let space = " "
public var debugDescription: String {
do {
return try String(describing: type(of: self)) + Node.space + outerHtml()
} catch {
}
return String(describing: type(of: self))
}
}

50
Pods/SwiftSoup/Sources/NodeTraversor.swift

@ -1,50 +0,0 @@
//
// NodeTraversor.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 17/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
class NodeTraversor {
private let visitor: NodeVisitor
/**
* Create a new traversor.
* @param visitor a class implementing the {@link NodeVisitor} interface, to be called when visiting each node.
*/
public init(_ visitor: NodeVisitor) {
self.visitor = visitor
}
/**
* Start a depth-first traverse of the root and all of its descendants.
* @param root the root node point to traverse.
*/
open func traverse(_ root: Node?)throws {
var node: Node? = root
var depth: Int = 0
while (node != nil) {
try visitor.head(node!, depth)
if (node!.childNodeSize() > 0) {
node = node!.childNode(0)
depth+=1
} else {
while (node!.nextSibling() == nil && depth > 0) {
try visitor.tail(node!, depth)
node = node!.getParentNode()
depth-=1
}
try visitor.tail(node!, depth)
if (node === root) {
break
}
node = node!.nextSibling()
}
}
}
}

37
Pods/SwiftSoup/Sources/NodeVisitor.swift

@ -1,37 +0,0 @@
//
// NodeVisitor.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 16/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
* Node visitor interface. Provide an implementing class to {@link NodeTraversor} to iterate through nodes.
* <p>
* This interface provides two methods, {@code head} and {@code tail}. The head method is called when the node is first
* seen, and the tail method when all of the node's children have been visited. As an example, head can be used to
* create a start tag for a node, and tail to create the end tag.
* </p>
*/
public protocol NodeVisitor {
/**
* Callback for when a node is first visited.
*
* @param node the node being visited.
* @param depth the depth of the node, relative to the root node. E.g., the root node has depth 0, and a child node
* of that will have depth 1.
*/
func head(_ node: Node, _ depth: Int)throws
/**
* Callback for when a node is last visited, after all of its descendants have been visited.
*
* @param node the node being visited.
* @param depth the depth of the node, relative to the root node. E.g., the root node has depth 0, and a child node
* of that will have depth 1.
*/
func tail(_ node: Node, _ depth: Int)throws
}

429
Pods/SwiftSoup/Sources/OrderedDictionary.swift

@ -1,429 +0,0 @@
//
// LinkedHashMap.swift
// SwifSoup
//
// Created by Nabil Chatbi on 03/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollection, Hashable {
/// Returns the position immediately after the given index.
///
/// - Parameter i: A valid index of the collection. `i` must be less than
/// `endIndex`.
/// - Returns: The index value immediately after `i`.
public func index(after i: Int) -> Int {
return _orderedKeys.index(after: i)
}
// ======================================================= //
// MARK: - Type Aliases
// ======================================================= //
public typealias Element = (Key, Value)
public typealias Index = Int
// ======================================================= //
// MARK: - Initialization
// ======================================================= //
public init() {}
public init(count: Int) {}
public init(elements: [Element]) {
for (key, value) in elements {
self[key] = value
}
}
public func copy() -> Any {
return copy(with: nil)
}
public func mutableCopy(with zone: NSZone? = nil) -> Any {
return copy()
}
public func copy(with zone: NSZone? = nil) -> Any {
let copy = OrderedDictionary<Key, Value>()
//let copy = type(of:self).init()
for element in orderedKeys {
copy.put(value: valueForKey(key: element)!, forKey: element)
}
return copy
}
func clone() -> OrderedDictionary<Key, Value> {
return copy() as! OrderedDictionary<Key, Value>
}
// ======================================================= //
// MARK: - Accessing Keys & Values
// ======================================================= //
public var orderedKeys: [Key] {
return _orderedKeys
}
public func keySet() -> [Key] {
return _orderedKeys
}
public var orderedValues: [Value] {
#if !swift(>=4.1)
return _orderedKeys.flatMap { _keysToValues[$0] }
#else
return _orderedKeys.compactMap { _keysToValues[$0] }
#endif
}
// ======================================================= //
// MARK: - Managing Content Using Keys
// ======================================================= //
public subscript(key: Key) -> Value? {
get {
return valueForKey(key: key)
}
set(newValue) {
if let newValue = newValue {
updateValue(value: newValue, forKey: key)
} else {
removeValueForKey(key: key)
}
}
}
public func containsKey(key: Key) -> Bool {
return _orderedKeys.contains(key)
}
public func valueForKey(key: Key) -> Value? {
return _keysToValues[key]
}
public func get(key: Key) -> Value? {
return valueForKey(key: key)
}
public func hash(into hasher: inout Hasher) {
hasher.combine(_orderedKeys)
}
public func hashCode() -> Int {
return hashValue
}
@discardableResult
private func updateValue(value: Value, forKey key: Key) -> Value? {
guard let currentValue = _keysToValues[key] else {
_orderedKeys.append(key)
_keysToValues[key] = value
return nil
}
_keysToValues[key] = value
return currentValue
// if _orderedKeys.contains(key) {
// guard let currentValue = _keysToValues[key] else {
// fatalError("Inconsistency error occured in OrderedDictionary")
// }
//
// _keysToValues[key] = value
//
// return currentValue
// } else {
// _orderedKeys.append(key)
// _keysToValues[key] = value
//
// return nil
// }
}
public func put(value: Value, forKey key: Key) {
self[key] = value
}
public func putAll(all: OrderedDictionary<Key, Value>) {
for i in all.orderedKeys {
put(value: all[i]!, forKey: i)
}
}
@discardableResult
public func removeValueForKey(key: Key) -> Value? {
if let index = _orderedKeys.firstIndex(of: key) {
guard let currentValue = _keysToValues[key] else {
fatalError("Inconsistency error occured in OrderedDictionary")
}
_orderedKeys.remove(at: index)
_keysToValues[key] = nil
return currentValue
} else {
return nil
}
}
@discardableResult
public func remove(key: Key) -> Value? {
return removeValueForKey(key: key)
}
public func removeAll(keepCapacity: Bool = true) {
_orderedKeys.removeAll(keepingCapacity: keepCapacity)
_keysToValues.removeAll(keepingCapacity: keepCapacity)
}
// ======================================================= //
// MARK: - Managing Content Using Indexes
// ======================================================= //
public subscript(index: Index) -> Element {
get {
guard let element = elementAtIndex(index: index) else {
fatalError("OrderedDictionary index out of range")
}
return element
}
set(newValue) {
updateElement(element: newValue, atIndex: index)
}
}
public func indexForKey(key: Key) -> Index? {
return _orderedKeys.firstIndex(of: key)
}
public func elementAtIndex(index: Index) -> Element? {
guard _orderedKeys.indices.contains(index) else { return nil }
let key = _orderedKeys[index]
guard let value = self._keysToValues[key] else {
fatalError("Inconsistency error occured in OrderedDictionary")
}
return (key, value)
}
public func insertElementWithKey(key: Key, value: Value, atIndex index: Index) -> Value? {
return insertElement(newElement: (key, value), atIndex: index)
}
public func insertElement(newElement: Element, atIndex index: Index) -> Value? {
guard index >= 0 else {
fatalError("Negative OrderedDictionary index is out of range")
}
guard index <= count else {
fatalError("OrderedDictionary index out of range")
}
let (key, value) = newElement
let adjustedIndex: Int
let currentValue: Value?
if let currentIndex = _orderedKeys.firstIndex(of: key) {
currentValue = _keysToValues[key]
adjustedIndex = (currentIndex < index - 1) ? index - 1 : index
_orderedKeys.remove(at: currentIndex)
_keysToValues[key] = nil
} else {
currentValue = nil
adjustedIndex = index
}
_orderedKeys.insert(key, at: adjustedIndex)
_keysToValues[key] = value
return currentValue
}
@discardableResult
public func updateElement(element: Element, atIndex index: Index) -> Element? {
guard let currentElement = elementAtIndex(index: index) else {
fatalError("OrderedDictionary index out of range")
}
let (newKey, newValue) = element
_orderedKeys[index] = newKey
_keysToValues[newKey] = newValue
return currentElement
}
public func removeAtIndex(index: Index) -> Element? {
if let element = elementAtIndex(index: index) {
_orderedKeys.remove(at: index)
_keysToValues.removeValue(forKey: element.0)
return element
} else {
return nil
}
}
// ======================================================= //
// MARK: - CollectionType Conformance
// ======================================================= //
public var startIndex: Index {
return _orderedKeys.startIndex
}
public var endIndex: Index {
return _orderedKeys.endIndex
}
// ======================================================= //
// MARK: - Internal Backing Store
// ======================================================= //
/// The backing store for the ordered keys.
internal var _orderedKeys = [Key]()
/// The backing store for the mapping of keys to values.
internal var _keysToValues = [Key: Value]()
}
// ======================================================= //
// MARK: - Initializations from Literals
// ======================================================= //
//extension OrderedDictionary: ExpressibleByArrayLiteral {
//
// public convenience init(arrayLiteral elements: Element...) {
// self.init(elements: elements)
// }
//}
//
//extension OrderedDictionary: ExpressibleByDictionaryLiteral {
//
// public convenience init(dictionaryLiteral elements: Element...) {
// self.init(elements: elements)
// }
//
//}
extension OrderedDictionary: LazySequenceProtocol {
func generate() -> AnyIterator<Value> {
var i = 0
return AnyIterator {
if (i >= self.orderedValues.count) {
return nil
}
i += 1
return self.orderedValues[i-1]
}
}
}
// ======================================================= //
// MARK: - Description
// ======================================================= //
extension OrderedDictionary: CustomStringConvertible, CustomDebugStringConvertible {
public var description: String {
return constructDescription(debug: false)
}
public var debugDescription: String {
return constructDescription(debug: true)
}
private func constructDescription(debug: Bool) -> String {
// The implementation of the description is inspired by zwaldowski's implementation of the ordered dictionary.
// See http://bit.ly/1VL4JUR
if isEmpty { return "[:]" }
func descriptionForItem(item: Any) -> String {
var description = ""
if debug {
debugPrint(item, separator: "", terminator: "", to: &description)
} else {
print(item, separator: "", terminator: "", to: &description)
}
return description
}
let bodyComponents = map({ (key: Key, value: Value) -> String in
return descriptionForItem(item: key) + ": " + descriptionForItem(item: value)
})
let body = bodyComponents.joined(separator: ", ")
return "[\(body)]"
}
}
extension OrderedDictionary: Equatable {
/// Returns a Boolean value indicating whether two values are equal.
///
/// Equality is the inverse of inequality. For any values `a` and `b`,
/// `a == b` implies that `a != b` is `false`.
///
/// - Parameters:
/// - lhs: A value to compare.
/// - rhs: Another value to compare.
public static func ==(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
if(lhs.count != rhs.count) {return false}
return (lhs._orderedKeys == rhs._orderedKeys) && (lhs._keysToValues == rhs._keysToValues)
}
}
//public func == <Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
// return lhs._orderedKeys == rhs._orderedKeys && lhs._keysToValues == rhs._keysToValues
//}
/**
* Elements IteratorProtocol.
*/
public struct OrderedDictionaryIterator<Key: Hashable, Value: Equatable>: IteratorProtocol {
/// Elements reference
let orderedDictionary: OrderedDictionary<Key, Value>
//current element index
var index = 0
/// Initializer
init(_ od: OrderedDictionary<Key, Value>) {
self.orderedDictionary = od
}
/// Advances to the next element and returns it, or `nil` if no next element
mutating public func next() -> Value? {
let result = index < orderedDictionary.orderedKeys.count ? orderedDictionary[orderedDictionary.orderedKeys[index]] : nil
index += 1
return result
}
}
/**
* Elements Extension Sequence.
*/
extension OrderedDictionary: Sequence {
/// Returns an iterator over the elements of this sequence.
func generate()->OrderedDictionaryIterator<Key, Value> {
return OrderedDictionaryIterator(self)
}
}

453
Pods/SwiftSoup/Sources/OrderedSet.swift

@ -1,453 +0,0 @@
//
// OrderedSet.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 12/11/16.
// Copyright © 2016 Nabil Chatbi. All rights reserved.
//
import Foundation
/// An ordered, unique collection of objects.
public class OrderedSet<T: Hashable> {
public typealias Index = Int
fileprivate var contents = [T: Index]() // Needs to have a value of Index instead of Void for fast removals
fileprivate var sequencedContents = Array<UnsafeMutablePointer<T>>()
/**
Inititalizes an empty ordered set.
- returns: An empty ordered set.
*/
public init() { }
deinit {
removeAllObjects()
}
/**
Initializes a new ordered set with the order and contents
of sequence.
If an object appears more than once in the sequence it will only appear
once in the ordered set, at the position of its first occurance.
- parameter sequence: The sequence to initialize the ordered set with.
- returns: An initialized ordered set with the contents of sequence.
*/
public init<S: Sequence>(sequence: S) where S.Iterator.Element == T {
for object in sequence {
if contents[object] == nil {
contents[object] = contents.count
let pointer = UnsafeMutablePointer<T>.allocate(capacity: 1)
pointer.initialize(to: object)
sequencedContents.append(pointer)
}
}
}
public required init(arrayLiteral elements: T...) {
for object in elements {
if contents[object] == nil {
contents[object] = contents.count
let pointer = UnsafeMutablePointer<T>.allocate(capacity: 1)
pointer.initialize(to: object)
sequencedContents.append(pointer)
}
}
}
/**
Locate the index of an object in the ordered set.
It is preferable to use this method over the global find() for performance reasons.
- parameter object: The object to find the index for.
- returns: The index of the object, or nil if the object is not in the ordered set.
*/
public func index(of object: T) -> Index? {
if let index = contents[object] {
return index
}
return nil
}
/**
Appends an object to the end of the ordered set.
- parameter object: The object to be appended.
*/
public func append(_ object: T) {
if let lastIndex = index(of: object) {
remove(object)
insert(object, at: lastIndex)
} else {
contents[object] = contents.count
let pointer = UnsafeMutablePointer<T>.allocate(capacity: 1)
pointer.initialize(to: object)
sequencedContents.append(pointer)
}
}
/**
Appends a sequence of objects to the end of the ordered set.
- parameter sequence: The sequence of objects to be appended.
*/
public func append<S: Sequence>(contentsOf sequence: S) where S.Iterator.Element == T {
var gen = sequence.makeIterator()
while let object: T = gen.next() {
append(object)
}
}
/**
Removes an object from the ordered set.
If the object exists in the ordered set, it will be removed.
If it is not the last object in the ordered set, subsequent
objects will be shifted down one position.
- parameter object: The object to be removed.
*/
public func remove(_ object: T) {
if let index = contents[object] {
contents[object] = nil
#if !swift(>=4.1)
sequencedContents[index].deallocate(capacity: 1)
#else
sequencedContents[index].deallocate()
#endif
sequencedContents.remove(at: index)
for (object, i) in contents {
if i < index {
continue
}
contents[object] = i - 1
}
}
}
/**
Removes the given objects from the ordered set.
- parameter objects: The objects to be removed.
*/
public func remove<S: Sequence>(_ objects: S) where S.Iterator.Element == T {
var gen = objects.makeIterator()
while let object: T = gen.next() {
remove(object)
}
}
/**
Removes an object at a given index.
This method will cause a fatal error if you attempt to move an object to an index that is out of bounds.
- parameter index: The index of the object to be removed.
*/
public func removeObject(at index: Index) {
if index < 0 || index >= count {
fatalError("Attempting to remove an object at an index that does not exist")
}
remove(sequencedContents[index].pointee)
}
/**
Removes all objects in the ordered set.
*/
public func removeAllObjects() {
contents.removeAll()
for sequencedContent in sequencedContents {
#if !swift(>=4.1)
sequencedContent.deallocate(capacity: 1)
#else
sequencedContent.deallocate()
#endif
}
sequencedContents.removeAll()
}
/**
Swaps two objects contained within the ordered set.
Both objects must exist within the set, or the swap will not occur.
- parameter first: The first object to be swapped.
- parameter second: The second object to be swapped.
*/
public func swapObject(_ first: T, with second: T) {
if let firstPosition = contents[first] {
if let secondPosition = contents[second] {
contents[first] = secondPosition
contents[second] = firstPosition
sequencedContents[firstPosition].pointee = second
sequencedContents[secondPosition].pointee = first
}
}
}
/**
Tests if the ordered set contains any objects within a sequence.
- parameter other: The sequence to look for the intersection in.
- returns: Returns true if the sequence and set contain any equal objects, otherwise false.
*/
public func intersects<S: Sequence>(_ other: S) -> Bool where S.Iterator.Element == T {
var gen = other.makeIterator()
while let object: T = gen.next() {
if contains(object) {
return true
}
}
return false
}
/**
Tests if a the ordered set is a subset of another sequence.
- parameter sequence: The sequence to check.
- returns: true if the sequence contains all objects contained in the receiver, otherwise false.
*/
public func isSubset<S: Sequence>(of sequence: S) -> Bool where S.Iterator.Element == T {
for (object, _) in contents {
if !sequence.contains(object) {
return false
}
}
return true
}
/**
Moves an object to a different index, shifting all objects in between the movement.
This method is a no-op if the object doesn't exist in the set or the index is the
same that the object is currently at.
This method will cause a fatal error if you attempt to move an object to an index that is out of bounds.
- parameter object: The object to be moved
- parameter index: The index that the object should be moved to.
*/
public func moveObject(_ object: T, toIndex index: Index) {
if index < 0 || index >= count {
fatalError("Attempting to move an object at an index that does not exist")
}
if let position = contents[object] {
// Return if the client attempted to move to the current index
if position == index {
return
}
let adjustment = position > index ? -1 : 1
var currentIndex = position
while currentIndex != index {
let nextIndex = currentIndex + adjustment
let firstObject = sequencedContents[currentIndex].pointee
let secondObject = sequencedContents[nextIndex].pointee
sequencedContents[currentIndex].pointee = secondObject
sequencedContents[nextIndex].pointee = firstObject
contents[firstObject] = nextIndex
contents[secondObject] = currentIndex
currentIndex += adjustment
}
}
}
/**
Moves an object from one index to a different index, shifting all objects in between the movement.
This method is a no-op if the index is the same that the object is currently at.
This method will cause a fatal error if you attempt to move an object fro man index that is out of bounds
or to an index that is out of bounds.
- parameter index: The index of the object to be moved.
- parameter toIndex: The index that the object should be moved to.
*/
public func moveObject(at index: Index, to toIndex: Index) {
if ((index < 0 || index >= count) || (toIndex < 0 || toIndex >= count)) {
fatalError("Attempting to move an object at or to an index that does not exist")
}
moveObject(self[index], toIndex: toIndex)
}
/**
Inserts an object at a given index, shifting all objects above it up one.
This method will cause a fatal error if you attempt to insert the object out of bounds.
If the object already exists in the OrderedSet, this operation is a no-op.
- parameter object: The object to be inserted.
- parameter index: The index to be inserted at.
*/
public func insert(_ object: T, at index: Index) {
if index > count || index < 0 {
fatalError("Attempting to insert an object at an index that does not exist")
}
if contents[object] != nil {
return
}
// Append our object, then swap them until its at the end.
append(object)
for i in (index..<count-1).reversed() {
swapObject(self[i], with: self[i+1])
}
}
/**
Inserts objects at a given index, shifting all objects above it up one.
This method will cause a fatal error if you attempt to insert the objects out of bounds.
If an object in objects already exists in the OrderedSet it will not be added. Objects that occur twice
in the sequence will only be added once.
- parameter objects: The objects to be inserted.
- parameter index: The index to be inserted at.
*/
public func insert<S: Sequence>(_ objects: S, at index: Index) where S.Iterator.Element == T {
if index > count || index < 0 {
fatalError("Attempting to insert an object at an index that does not exist")
}
var addedObjectCount = 0
for object in objects {
if contents[object] == nil {
let seqIdx = index + addedObjectCount
let element = UnsafeMutablePointer<T>.allocate(capacity: 1)
element.initialize(to: object)
sequencedContents.insert(element, at: seqIdx)
contents[object] = seqIdx
addedObjectCount += 1
}
}
// Now we'll remove duplicates and update the shifted objects position in the contents
// dictionary.
for i in index + addedObjectCount..<count {
contents[sequencedContents[i].pointee] = i
}
}
/// Returns the last object in the set, or `nil` if the set is empty.
public var last: T? {
return sequencedContents.last?.pointee
}
}
extension OrderedSet: ExpressibleByArrayLiteral { }
extension OrderedSet where T: Comparable {}
extension OrderedSet {
public var count: Int {
return contents.count
}
public var isEmpty: Bool {
return count == 0
}
public var first: T? {
guard count > 0 else { return nil }
return sequencedContents[0].pointee
}
public func index(after i: Int) -> Int {
return sequencedContents.index(after: i)
}
public var startIndex: Int {
return 0
}
public var endIndex: Int {
return contents.count
}
public subscript(index: Index) -> T {
get {
return sequencedContents[index].pointee
}
set {
let previousCount = contents.count
contents[sequencedContents[index].pointee] = nil
contents[newValue] = index
// If the count is reduced we used an existing value, and need to sync up sequencedContents
if contents.count == previousCount {
sequencedContents[index].pointee = newValue
} else {
sequencedContents.remove(at: index)
}
}
}
}
extension OrderedSet: Sequence {
public typealias Iterator = OrderedSetGenerator<T>
public func makeIterator() -> Iterator {
return OrderedSetGenerator(set: self)
}
}
public struct OrderedSetGenerator<T: Hashable>: IteratorProtocol {
public typealias Element = T
private var generator: IndexingIterator<Array<UnsafeMutablePointer<T>>>
public init(set: OrderedSet<T>) {
generator = set.sequencedContents.makeIterator()
}
public mutating func next() -> Element? {
return generator.next()?.pointee
}
}
extension OrderedSetGenerator where T: Comparable {}
public func +<T, S: Sequence> (lhs: OrderedSet<T>, rhs: S) -> OrderedSet<T> where S.Iterator.Element == T {
let joinedSet = lhs
joinedSet.append(contentsOf: rhs)
return joinedSet
}
public func +=<T, S: Sequence> (lhs: inout OrderedSet<T>, rhs: S) where S.Iterator.Element == T {
lhs.append(contentsOf: rhs)
}
public func -<T, S: Sequence> (lhs: OrderedSet<T>, rhs: S) -> OrderedSet<T> where S.Iterator.Element == T {
let purgedSet = lhs
purgedSet.remove(rhs)
return purgedSet
}
public func -=<T, S: Sequence> (lhs: inout OrderedSet<T>, rhs: S) where S.Iterator.Element == T {
lhs.remove(rhs)
}
extension OrderedSet: Equatable { }
public func ==<T> (lhs: OrderedSet<T>, rhs: OrderedSet<T>) -> Bool {
if lhs.count != rhs.count {
return false
}
for object in lhs {
if lhs.contents[object] != rhs.contents[object] {
return false
}
}
return true
}
extension OrderedSet: CustomStringConvertible {
public var description: String {
let children = map({ "\($0)" }).joined(separator: ", ")
return "OrderedSet (\(count) object(s)): [\(children)]"
}
}

42
Pods/SwiftSoup/Sources/ParseError.swift

@ -1,42 +0,0 @@
//
// ParseError.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 19/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
* A Parse Error records an error in the input HTML that occurs in either the tokenisation or the tree building phase.
*/
open class ParseError {
private let pos: Int
private let errorMsg: String
init(_ pos: Int, _ errorMsg: String) {
self.pos = pos
self.errorMsg = errorMsg
}
/**
* Retrieve the error message.
* @return the error message.
*/
open func getErrorMessage() -> String {
return errorMsg
}
/**
* Retrieves the offset of the error.
* @return error offset within input
*/
open func getPosition() -> Int {
return pos
}
open func toString() -> String {
return "\(pos): " + errorMsg
}
}

52
Pods/SwiftSoup/Sources/ParseErrorList.swift

@ -1,52 +0,0 @@
//
// ParseErrorList.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 19/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
public class ParseErrorList {
private static let INITIAL_CAPACITY: Int = 16
private let maxSize: Int
private let initialCapacity: Int
private var array: Array<ParseError?> = Array<ParseError>()
init(_ initialCapacity: Int, _ maxSize: Int) {
self.maxSize = maxSize
self.initialCapacity = initialCapacity
array = Array(repeating: nil, count: maxSize)
}
func canAddError() -> Bool {
return array.count < maxSize
}
func getMaxSize() -> Int {
return maxSize
}
static func noTracking() -> ParseErrorList {
return ParseErrorList(0, 0)
}
static func tracking(_ maxSize: Int) -> ParseErrorList {
return ParseErrorList(INITIAL_CAPACITY, maxSize)
}
// // you need to provide the Equatable functionality
// static func ==(leftFoo: Foo, rightFoo: Foo) -> Bool {
// return ObjectIdentifier(leftFoo) == ObjectIdentifier(rightFoo)
// }
open func add(_ e: ParseError) {
array.append(e)
}
open func add(_ index: Int, _ element: ParseError) {
array.insert(element, at: index)
}
}

59
Pods/SwiftSoup/Sources/ParseSettings.swift

@ -1,59 +0,0 @@
//
// ParseSettings.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 14/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
open class ParseSettings {
/**
* HTML default settings: both tag and attribute names are lower-cased during parsing.
*/
public static let htmlDefault: ParseSettings = ParseSettings(false, false)
/**
* Preserve both tag and attribute case.
*/
public static let preserveCase: ParseSettings = ParseSettings(true, true)
private let preserveTagCase: Bool
private let preserveAttributeCase: Bool
/**
* Define parse settings.
* @param tag preserve tag case?
* @param attribute preserve attribute name case?
*/
public init(_ tag: Bool, _ attribute: Bool) {
preserveTagCase = tag
preserveAttributeCase = attribute
}
open func normalizeTag(_ name: String) -> String {
var name = name.trim()
if (!preserveTagCase) {
name = name.lowercased()
}
return name
}
open func normalizeAttribute(_ name: String) -> String {
var name = name.trim()
if (!preserveAttributeCase) {
name = name.lowercased()
}
return name
}
open func normalizeAttributes(_ attributes: Attributes)throws ->Attributes {
if (!preserveAttributeCase) {
for attr in attributes {
try attr.setKey(key: attr.getKey().lowercased())
}
}
return attributes
}
}

201
Pods/SwiftSoup/Sources/Parser.swift

@ -1,201 +0,0 @@
//
// Parser.swift
// SwifSoup
//
// Created by Nabil Chatbi on 29/09/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
* Parses HTML into a {@link org.jsoup.nodes.Document}. Generally best to use one of the more convenient parse methods
* in {@link org.jsoup.Jsoup}.
*/
public class Parser {
private static let DEFAULT_MAX_ERRORS: Int = 0 // by default, error tracking is disabled.
private var _treeBuilder: TreeBuilder
private var _maxErrors: Int = DEFAULT_MAX_ERRORS
private var _errors: ParseErrorList = ParseErrorList(16, 16)
private var _settings: ParseSettings
/**
* Create a new Parser, using the specified TreeBuilder
* @param treeBuilder TreeBuilder to use to parse input into Documents.
*/
init(_ treeBuilder: TreeBuilder) {
self._treeBuilder = treeBuilder
_settings = treeBuilder.defaultSettings()
}
public func parseInput(_ html: String, _ baseUri: String)throws->Document {
_errors = isTrackErrors() ? ParseErrorList.tracking(_maxErrors) : ParseErrorList.noTracking()
return try _treeBuilder.parse(html, baseUri, _errors, _settings)
}
// gets & sets
/**
* Get the TreeBuilder currently in use.
* @return current TreeBuilder.
*/
public func getTreeBuilder() -> TreeBuilder {
return _treeBuilder
}
/**
* Update the TreeBuilder used when parsing content.
* @param treeBuilder current TreeBuilder
* @return this, for chaining
*/
@discardableResult
public func setTreeBuilder(_ treeBuilder: TreeBuilder) -> Parser {
self._treeBuilder = treeBuilder
return self
}
/**
* Check if parse error tracking is enabled.
* @return current track error state.
*/
public func isTrackErrors() -> Bool {
return _maxErrors > 0
}
/**
* Enable or disable parse error tracking for the next parse.
* @param maxErrors the maximum number of errors to track. Set to 0 to disable.
* @return this, for chaining
*/
@discardableResult
public func setTrackErrors(_ maxErrors: Int) -> Parser {
self._maxErrors = maxErrors
return self
}
/**
* Retrieve the parse errors, if any, from the last parse.
* @return list of parse errors, up to the size of the maximum errors tracked.
*/
public func getErrors() -> ParseErrorList {
return _errors
}
@discardableResult
public func settings(_ settings: ParseSettings) -> Parser {
self._settings = settings
return self
}
public func settings() -> ParseSettings {
return _settings
}
// static parse functions below
/**
* Parse HTML into a Document.
*
* @param html HTML to parse
* @param baseUri base URI of document (i.e. original fetch location), for resolving relative URLs.
*
* @return parsed Document
*/
public static func parse(_ html: String, _ baseUri: String)throws->Document {
let treeBuilder: TreeBuilder = HtmlTreeBuilder()
return try treeBuilder.parse(html, baseUri, ParseErrorList.noTracking(), treeBuilder.defaultSettings())
}
/**
* Parse a fragment of HTML into a list of nodes. The context element, if supplied, supplies parsing context.
*
* @param fragmentHtml the fragment of HTML to parse
* @param context (optional) the element that this HTML fragment is being parsed for (i.e. for inner HTML). This
* provides stack context (for implicit element creation).
* @param baseUri base URI of document (i.e. original fetch location), for resolving relative URLs.
*
* @return list of nodes parsed from the input HTML. Note that the context element, if supplied, is not modified.
*/
public static func parseFragment(_ fragmentHtml: String, _ context: Element?, _ baseUri: String)throws->Array<Node> {
let treeBuilder = HtmlTreeBuilder()
return try treeBuilder.parseFragment(fragmentHtml, context, baseUri, ParseErrorList.noTracking(), treeBuilder.defaultSettings())
}
/**
* Parse a fragment of XML into a list of nodes.
*
* @param fragmentXml the fragment of XML to parse
* @param baseUri base URI of document (i.e. original fetch location), for resolving relative URLs.
* @return list of nodes parsed from the input XML.
*/
public static func parseXmlFragment(_ fragmentXml: String, _ baseUri: String)throws->Array<Node> {
let treeBuilder: XmlTreeBuilder = XmlTreeBuilder()
return try treeBuilder.parseFragment(fragmentXml, baseUri, ParseErrorList.noTracking(), treeBuilder.defaultSettings())
}
/**
* Parse a fragment of HTML into the {@code body} of a Document.
*
* @param bodyHtml fragment of HTML
* @param baseUri base URI of document (i.e. original fetch location), for resolving relative URLs.
*
* @return Document, with empty head, and HTML parsed into body
*/
public static func parseBodyFragment(_ bodyHtml: String, _ baseUri: String)throws->Document {
let doc: Document = Document.createShell(baseUri)
if let body: Element = doc.body() {
let nodeList: Array<Node> = try parseFragment(bodyHtml, body, baseUri)
//var nodes: [Node] = nodeList.toArray(Node[nodeList.size()]) // the node list gets modified when re-parented
if nodeList.count > 0 {
for i in 1..<nodeList.count {
try nodeList[i].remove()
}
}
for node: Node in nodeList {
try body.appendChild(node)
}
}
return doc
}
/**
* Utility method to unescape HTML entities from a string
* @param string HTML escaped string
* @param inAttribute if the string is to be escaped in strict mode (as attributes are)
* @return an unescaped string
*/
public static func unescapeEntities(_ string: String, _ inAttribute: Bool)throws->String {
let tokeniser: Tokeniser = Tokeniser(CharacterReader(string), ParseErrorList.noTracking())
return try tokeniser.unescapeEntities(inAttribute)
}
/**
* @param bodyHtml HTML to parse
* @param baseUri baseUri base URI of document (i.e. original fetch location), for resolving relative URLs.
*
* @return parsed Document
* @deprecated Use {@link #parseBodyFragment} or {@link #parseFragment} instead.
*/
public static func parseBodyFragmentRelaxed(_ bodyHtml: String, _ baseUri: String)throws->Document {
return try parse(bodyHtml, baseUri)
}
// builders
/**
* Create a new HTML parser. This parser treats input as HTML5, and enforces the creation of a normalised document,
* based on a knowledge of the semantics of the incoming tags.
* @return a new HTML parser.
*/
public static func htmlParser() -> Parser {
return Parser(HtmlTreeBuilder())
}
/**
* Create a new XML parser. This parser assumes no knowledge of the incoming tags and does not treat it as HTML,
* rather creates a simple tree directly from the input.
* @return a new simple XML parser.
*/
public static func xmlParser() -> Parser {
return Parser(XmlTreeBuilder())
}
}

84
Pods/SwiftSoup/Sources/Pattern.swift

@ -1,84 +0,0 @@
//
// Regex.swift
// SwifSoup
//
// Created by Nabil Chatbi on 08/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
public struct Pattern {
public static let CASE_INSENSITIVE: Int = 0x02
let pattern: String
init(_ pattern: String) {
self.pattern = pattern
}
static public func compile(_ s: String) -> Pattern {
return Pattern(s)
}
static public func compile(_ s: String, _ op: Int) -> Pattern {
return Pattern(s)
}
func validate()throws {
_ = try NSRegularExpression(pattern: self.pattern, options: [])
}
public func matcher(in text: String) -> Matcher {
do {
let regex = try NSRegularExpression(pattern: self.pattern, options: [])
let nsString = NSString(string: text)
let results = regex.matches(in: text, options: [], range: NSRange(location: 0, length: nsString.length))
return Matcher(results, text)
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return Matcher([], text)
}
}
public func toString() -> String {
return pattern
}
}
public class Matcher {
let matches: [NSTextCheckingResult]
let string: String
var index: Int = -1
public var count: Int { return matches.count}
init(_ m: [NSTextCheckingResult], _ s: String) {
matches = m
string = s
}
@discardableResult
public func find() -> Bool {
index += 1
if(index < matches.count) {
return true
}
return false
}
public func group(_ i: Int) -> String? {
let b = matches[index]
#if !os(Linux) && !swift(>=4)
let c = b.rangeAt(i)
#else
let c = b.range(at: i)
#endif
if(c.location == NSNotFound) {return nil}
let result = string.substring(c.location, c.length)
return result
}
public func group() -> String? {
return group(0)
}
}

322
Pods/SwiftSoup/Sources/QueryParser.swift

@ -1,322 +0,0 @@
//
// QueryParser.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 23/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
* Parses a CSS selector into an Evaluator tree.
*/
public class QueryParser {
private static let combinators: [String] = [",", ">", "+", "~", " "]
private static let AttributeEvals: [String] = ["=", "!=", "^=", "$=", "*=", "~="]
private var tq: TokenQueue
private var query: String
private var evals: Array<Evaluator> = Array<Evaluator>()
/**
* Create a new QueryParser.
* @param query CSS query
*/
private init(_ query: String) {
self.query = query
self.tq = TokenQueue(query)
}
/**
* Parse a CSS query into an Evaluator.
* @param query CSS query
* @return Evaluator
*/
public static func parse(_ query: String)throws->Evaluator {
let p = QueryParser(query)
return try p.parse()
}
/**
* Parse the query
* @return Evaluator
*/
public func parse()throws->Evaluator {
tq.consumeWhitespace()
if (tq.matchesAny(QueryParser.combinators)) { // if starts with a combinator, use root as elements
evals.append( StructuralEvaluator.Root())
try combinator(tq.consume())
} else {
try findElements()
}
while (!tq.isEmpty()) {
// hierarchy and extras
let seenWhite: Bool = tq.consumeWhitespace()
if (tq.matchesAny(QueryParser.combinators)) {
try combinator(tq.consume())
} else if (seenWhite) {
try combinator(" " as Character)
} else { // E.class, E#id, E[attr] etc. AND
try findElements() // take next el, #. etc off queue
}
}
if (evals.count == 1) {
return evals[0]
}
return CombiningEvaluator.And(evals)
}
private func combinator(_ combinator: Character)throws {
tq.consumeWhitespace()
let subQuery: String = consumeSubQuery() // support multi > childs
var rootEval: Evaluator? // the new topmost evaluator
var currentEval: Evaluator? // the evaluator the new eval will be combined to. could be root, or rightmost or.
let newEval: Evaluator = try QueryParser.parse(subQuery) // the evaluator to add into target evaluator
var replaceRightMost: Bool = false
if (evals.count == 1) {
currentEval = evals[0]
rootEval = currentEval
// make sure OR (,) has precedence:
if (((rootEval as? CombiningEvaluator.Or) != nil) && combinator != ",") {
currentEval = (currentEval as! CombiningEvaluator.Or).rightMostEvaluator()
replaceRightMost = true
}
} else {
currentEval = CombiningEvaluator.And(evals)
rootEval = currentEval
}
evals.removeAll()
// for most combinators: change the current eval into an AND of the current eval and the new eval
if (combinator == ">") {currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.ImmediateParent(currentEval!))} else if (combinator == " ") {currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.Parent(currentEval!))} else if (combinator == "+") {currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.ImmediatePreviousSibling(currentEval!))} else if (combinator == "~") {currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.PreviousSibling(currentEval!))} else if (combinator == ",") { // group or.
let or: CombiningEvaluator.Or
if ((currentEval as? CombiningEvaluator.Or) != nil) {
or = currentEval as! CombiningEvaluator.Or
or.add(newEval)
} else {
or = CombiningEvaluator.Or()
or.add(currentEval!)
or.add(newEval)
}
currentEval = or
} else {
throw Exception.Error(type: ExceptionType.SelectorParseException, Message: "Unknown combinator: \(String(combinator))")
}
if (replaceRightMost) {
(rootEval as! CombiningEvaluator.Or).replaceRightMostEvaluator(currentEval!)
} else {
rootEval = currentEval
}
evals.append(rootEval!)
}
private func consumeSubQuery() -> String {
var sq = ""
while (!tq.isEmpty()) {
if (tq.matches("(")) {
sq.append("(")
sq.append(tq.chompBalanced("(", ")"))
sq.append(")")
} else if (tq.matches("[")) {
sq.append("[")
sq.append(tq.chompBalanced("[", "]"))
sq.append("]")
} else if (tq.matchesAny(QueryParser.combinators)) {
break
} else {
sq.append(tq.consume())
}
}
return sq
}
private func findElements()throws {
if (tq.matchChomp("#")) {
try byId()
} else if (tq.matchChomp(".")) {
try byClass()} else if (tq.matchesWord() || tq.matches("*|")) {try byTag()} else if (tq.matches("[")) {try byAttribute()} else if (tq.matchChomp("*")) { allElements()} else if (tq.matchChomp(":lt(")) {try indexLessThan()} else if (tq.matchChomp(":gt(")) {try indexGreaterThan()} else if (tq.matchChomp(":eq(")) {try indexEquals()} else if (tq.matches(":has(")) {try has()} else if (tq.matches(":contains(")) {try contains(false)} else if (tq.matches(":containsOwn(")) {try contains(true)} else if (tq.matches(":matches(")) {try matches(false)} else if (tq.matches(":matchesOwn(")) {try matches(true)} else if (tq.matches(":not(")) {try not()} else if (tq.matchChomp(":nth-child(")) {try cssNthChild(false, false)} else if (tq.matchChomp(":nth-last-child(")) {try cssNthChild(true, false)} else if (tq.matchChomp(":nth-of-type(")) {try cssNthChild(false, true)} else if (tq.matchChomp(":nth-last-of-type(")) {try cssNthChild(true, true)} else if (tq.matchChomp(":first-child")) {evals.append(Evaluator.IsFirstChild())} else if (tq.matchChomp(":last-child")) {evals.append(Evaluator.IsLastChild())} else if (tq.matchChomp(":first-of-type")) {evals.append(Evaluator.IsFirstOfType())} else if (tq.matchChomp(":last-of-type")) {evals.append(Evaluator.IsLastOfType())} else if (tq.matchChomp(":only-child")) {evals.append(Evaluator.IsOnlyChild())} else if (tq.matchChomp(":only-of-type")) {evals.append(Evaluator.IsOnlyOfType())} else if (tq.matchChomp(":empty")) {evals.append(Evaluator.IsEmpty())} else if (tq.matchChomp(":root")) {evals.append(Evaluator.IsRoot())} else // unhandled
{
throw Exception.Error(type: ExceptionType.SelectorParseException, Message: "Could not parse query \(query): unexpected token at \(tq.remainder())")
}
}
private func byId()throws {
let id: String = tq.consumeCssIdentifier()
try Validate.notEmpty(string: id)
evals.append(Evaluator.Id(id))
}
private func byClass()throws {
let className: String = tq.consumeCssIdentifier()
try Validate.notEmpty(string: className)
evals.append(Evaluator.Class(className.trim()))
}
private func byTag()throws {
var tagName = tq.consumeElementSelector()
try Validate.notEmpty(string: tagName)
// namespaces: wildcard match equals(tagName) or ending in ":"+tagName
if (tagName.startsWith("*|")) {
evals.append(
CombiningEvaluator.Or(
Evaluator.Tag(tagName.trim().lowercased()),
Evaluator.TagEndsWith(tagName.replacingOccurrences(of: "*|", with: ":").trim().lowercased())))
} else {
// namespaces: if element name is "abc:def", selector must be "abc|def", so flip:
if (tagName.contains("|")) {
tagName = tagName.replacingOccurrences(of: "|", with: ":")
}
evals.append(Evaluator.Tag(tagName.trim()))
}
}
private func byAttribute()throws {
let cq: TokenQueue = TokenQueue(tq.chompBalanced("[", "]")) // content queue
let key: String = cq.consumeToAny(QueryParser.AttributeEvals) // eq, not, start, end, contain, match, (no val)
try Validate.notEmpty(string: key)
cq.consumeWhitespace()
if (cq.isEmpty()) {
if (key.startsWith("^")) {
evals.append(try Evaluator.AttributeStarting(key.substring(1)))
} else {
evals.append(Evaluator.Attribute(key))
}
} else {
if (cq.matchChomp("=")) {
evals.append(try Evaluator.AttributeWithValue(key, cq.remainder()))
} else if (cq.matchChomp("!=")) {
evals.append(try Evaluator.AttributeWithValueNot(key, cq.remainder()))
} else if (cq.matchChomp("^=")) {
evals.append(try Evaluator.AttributeWithValueStarting(key, cq.remainder()))
} else if (cq.matchChomp("$=")) {
evals.append(try Evaluator.AttributeWithValueEnding(key, cq.remainder()))
} else if (cq.matchChomp("*=")) {
evals.append(try Evaluator.AttributeWithValueContaining(key, cq.remainder()))
} else if (cq.matchChomp("~=")) {
evals.append( Evaluator.AttributeWithValueMatching(key, Pattern.compile(cq.remainder())))
} else {
throw Exception.Error(type: ExceptionType.SelectorParseException, Message: "Could not parse attribute query '\(query)': unexpected token at '\(cq.remainder())'")
}
}
}
private func allElements() {
evals.append(Evaluator.AllElements())
}
// pseudo selectors :lt, :gt, :eq
private func indexLessThan()throws {
evals.append(Evaluator.IndexLessThan(try consumeIndex()))
}
private func indexGreaterThan()throws {
evals.append(Evaluator.IndexGreaterThan(try consumeIndex()))
}
private func indexEquals()throws {
evals.append(Evaluator.IndexEquals(try consumeIndex()))
}
//pseudo selectors :first-child, :last-child, :nth-child, ...
private static let NTH_AB: Pattern = Pattern.compile("((\\+|-)?(\\d+)?)n(\\s*(\\+|-)?\\s*\\d+)?", Pattern.CASE_INSENSITIVE)
private static let NTH_B: Pattern = Pattern.compile("(\\+|-)?(\\d+)")
private func cssNthChild(_ backwards: Bool, _ ofType: Bool)throws {
let argS: String = tq.chompTo(")").trim().lowercased()
let mAB: Matcher = QueryParser.NTH_AB.matcher(in: argS)
let mB: Matcher = QueryParser.NTH_B.matcher(in: argS)
var a: Int
var b: Int
if ("odd"==argS) {
a = 2
b = 1
} else if ("even"==argS) {
a = 2
b = 0
} else if (mAB.matches.count > 0) {
mAB.find()
a = mAB.group(3) != nil ? Int(mAB.group(1)!.replaceFirst(of: "^\\+", with: ""))! : 1
b = mAB.group(4) != nil ? Int(mAB.group(4)!.replaceFirst(of: "^\\+", with: ""))! : 0
} else if (mB.matches.count > 0) {
a = 0
mB.find()
b = Int(mB.group()!.replaceFirst(of: "^\\+", with: ""))!
} else {
throw Exception.Error(type: ExceptionType.SelectorParseException, Message: "Could not parse nth-index '\(argS)': unexpected format")
}
if (ofType) {
if (backwards) {
evals.append(Evaluator.IsNthLastOfType(a, b))
} else {
evals.append(Evaluator.IsNthOfType(a, b))
}
} else {
if (backwards) {
evals.append(Evaluator.IsNthLastChild(a, b))
} else {
evals.append(Evaluator.IsNthChild(a, b))
}
}
}
private func consumeIndex()throws->Int {
let indexS: String = tq.chompTo(")").trim()
try Validate.isTrue(val: StringUtil.isNumeric(indexS), msg: "Index must be numeric")
return Int(indexS)!
}
// pseudo selector :has(el)
private func has()throws {
try tq.consume(":has")
let subQuery: String = tq.chompBalanced("(", ")")
try Validate.notEmpty(string: subQuery, msg: ":has(el) subselect must not be empty")
evals.append(StructuralEvaluator.Has(try QueryParser.parse(subQuery)))
}
// pseudo selector :contains(text), containsOwn(text)
private func contains(_ own: Bool)throws {
try tq.consume(own ? ":containsOwn" : ":contains")
let searchText: String = TokenQueue.unescape(tq.chompBalanced("(", ")"))
try Validate.notEmpty(string: searchText, msg: ":contains(text) query must not be empty")
if (own) {
evals.append(Evaluator.ContainsOwnText(searchText))
} else {
evals.append(Evaluator.ContainsText(searchText))
}
}
// :matches(regex), matchesOwn(regex)
private func matches(_ own: Bool)throws {
try tq.consume(own ? ":matchesOwn" : ":matches")
let regex: String = tq.chompBalanced("(", ")") // don't unescape, as regex bits will be escaped
try Validate.notEmpty(string: regex, msg: ":matches(regex) query must not be empty")
if (own) {
evals.append(Evaluator.MatchesOwn(Pattern.compile(regex)))
} else {
evals.append(Evaluator.Matches(Pattern.compile(regex)))
}
}
// :not(selector)
private func not()throws {
try tq.consume(":not")
let subQuery: String = tq.chompBalanced("(", ")")
try Validate.notEmpty(string: subQuery, msg: ":not(selector) subselect must not be empty")
evals.append(StructuralEvaluator.Not(try QueryParser.parse(subQuery)))
}
}

163
Pods/SwiftSoup/Sources/Selector.swift

@ -1,163 +0,0 @@
//
// Selector.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 21/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
* CSS-like element selector, that finds elements matching a query.
*
* <h2>Selector syntax</h2>
* <p>
* A selector is a chain of simple selectors, separated by combinators. Selectors are <b>case insensitive</b> (including against
* elements, attributes, and attribute values).
* </p>
* <p>
* The universal selector (*) is implicit when no element selector is supplied (i.e. {@code *.header} and {@code .header}
* is equivalent).
* </p>
* <table summary="">
* <tr><th align="left">Pattern</th><th align="left">Matches</th><th align="left">Example</th></tr>
* <tr><td><code>*</code></td><td>any element</td><td><code>*</code></td></tr>
* <tr><td><code>tag</code></td><td>elements with the given tag name</td><td><code>div</code></td></tr>
* <tr><td><code>*|E</code></td><td>elements of type E in any namespace <i>ns</i></td><td><code>*|name</code> finds <code>&lt;fb:name&gt;</code> elements</td></tr>
* <tr><td><code>ns|E</code></td><td>elements of type E in the namespace <i>ns</i></td><td><code>fb|name</code> finds <code>&lt;fb:name&gt;</code> elements</td></tr>
* <tr><td><code>#id</code></td><td>elements with attribute ID of "id"</td><td><code>div#wrap</code>, <code>#logo</code></td></tr>
* <tr><td><code>.class</code></td><td>elements with a class name of "class"</td><td><code>div.left</code>, <code>.result</code></td></tr>
* <tr><td><code>[attr]</code></td><td>elements with an attribute named "attr" (with any value)</td><td><code>a[href]</code>, <code>[title]</code></td></tr>
* <tr><td><code>[^attrPrefix]</code></td><td>elements with an attribute name starting with "attrPrefix". Use to find elements with HTML5 datasets</td><td><code>[^data-]</code>, <code>div[^data-]</code></td></tr>
* <tr><td><code>[attr=val]</code></td><td>elements with an attribute named "attr", and value equal to "val"</td><td><code>img[width=500]</code>, <code>a[rel=nofollow]</code></td></tr>
* <tr><td><code>[attr=&quot;val&quot;]</code></td><td>elements with an attribute named "attr", and value equal to "val"</td><td><code>span[hello="Cleveland"][goodbye="Columbus"]</code>, <code>a[rel=&quot;nofollow&quot;]</code></td></tr>
* <tr><td><code>[attr^=valPrefix]</code></td><td>elements with an attribute named "attr", and value starting with "valPrefix"</td><td><code>a[href^=http:]</code></td></tr>
* <tr><td><code>[attr$=valSuffix]</code></td><td>elements with an attribute named "attr", and value ending with "valSuffix"</td><td><code>img[src$=.png]</code></td></tr>
* <tr><td><code>[attr*=valContaining]</code></td><td>elements with an attribute named "attr", and value containing "valContaining"</td><td><code>a[href*=/search/]</code></td></tr>
* <tr><td><code>[attr~=<em>regex</em>]</code></td><td>elements with an attribute named "attr", and value matching the regular expression</td><td><code>img[src~=(?i)\\.(png|jpe?g)]</code></td></tr>
* <tr><td></td><td>The above may be combined in any order</td><td><code>div.header[title]</code></td></tr>
* <tr><td><td colspan="3"><h3>Combinators</h3></td></tr>
* <tr><td><code>E F</code></td><td>an F element descended from an E element</td><td><code>div a</code>, <code>.logo h1</code></td></tr>
* <tr><td><code>E {@literal >} F</code></td><td>an F direct child of E</td><td><code>ol {@literal >} li</code></td></tr>
* <tr><td><code>E + F</code></td><td>an F element immediately preceded by sibling E</td><td><code>li + li</code>, <code>div.head + div</code></td></tr>
* <tr><td><code>E ~ F</code></td><td>an F element preceded by sibling E</td><td><code>h1 ~ p</code></td></tr>
* <tr><td><code>E, F, G</code></td><td>all matching elements E, F, or G</td><td><code>a[href], div, h3</code></td></tr>
* <tr><td><td colspan="3"><h3>Pseudo selectors</h3></td></tr>
* <tr><td><code>:lt(<em>n</em>)</code></td><td>elements whose sibling index is less than <em>n</em></td><td><code>td:lt(3)</code> finds the first 3 cells of each row</td></tr>
* <tr><td><code>:gt(<em>n</em>)</code></td><td>elements whose sibling index is greater than <em>n</em></td><td><code>td:gt(1)</code> finds cells after skipping the first two</td></tr>
* <tr><td><code>:eq(<em>n</em>)</code></td><td>elements whose sibling index is equal to <em>n</em></td><td><code>td:eq(0)</code> finds the first cell of each row</td></tr>
* <tr><td><code>:has(<em>selector</em>)</code></td><td>elements that contains at least one element matching the <em>selector</em></td><td><code>div:has(p)</code> finds divs that contain p elements </td></tr>
* <tr><td><code>:not(<em>selector</em>)</code></td><td>elements that do not match the <em>selector</em>. See also {@link Elements#not(String)}</td><td><code>div:not(.logo)</code> finds all divs that do not have the "logo" class.<p><code>div:not(:has(div))</code> finds divs that do not contain divs.</p></td></tr>
* <tr><td><code>:contains(<em>text</em>)</code></td><td>elements that contains the specified text. The search is case insensitive. The text may appear in the found element, or any of its descendants.</td><td><code>p:contains(jsoup)</code> finds p elements containing the text "jsoup".</td></tr>
* <tr><td><code>:matches(<em>regex</em>)</code></td><td>elements whose text matches the specified regular expression. The text may appear in the found element, or any of its descendants.</td><td><code>td:matches(\\d+)</code> finds table cells containing digits. <code>div:matches((?i)login)</code> finds divs containing the text, case insensitively.</td></tr>
* <tr><td><code>:containsOwn(<em>text</em>)</code></td><td>elements that directly contain the specified text. The search is case insensitive. The text must appear in the found element, not any of its descendants.</td><td><code>p:containsOwn(jsoup)</code> finds p elements with own text "jsoup".</td></tr>
* <tr><td><code>:matchesOwn(<em>regex</em>)</code></td><td>elements whose own text matches the specified regular expression. The text must appear in the found element, not any of its descendants.</td><td><code>td:matchesOwn(\\d+)</code> finds table cells directly containing digits. <code>div:matchesOwn((?i)login)</code> finds divs containing the text, case insensitively.</td></tr>
* <tr><td></td><td>The above may be combined in any order and with other selectors</td><td><code>.light:contains(name):eq(0)</code></td></tr>
* <tr><td colspan="3"><h3>Structural pseudo selectors</h3></td></tr>
* <tr><td><code>:root</code></td><td>The element that is the root of the document. In HTML, this is the <code>html</code> element</td><td><code>:root</code></td></tr>
* <tr><td><code>:nth-child(<em>a</em>n+<em>b</em>)</code></td><td><p>elements that have <code><em>a</em>n+<em>b</em>-1</code> siblings <b>before</b> it in the document tree, for any positive integer or zero value of <code>n</code>, and has a parent element. For values of <code>a</code> and <code>b</code> greater than zero, this effectively divides the element's children into groups of a elements (the last group taking the remainder), and selecting the <em>b</em>th element of each group. For example, this allows the selectors to address every other row in a table, and could be used to alternate the color of paragraph text in a cycle of four. The <code>a</code> and <code>b</code> values must be integers (positive, negative, or zero). The index of the first child of an element is 1.</p>
* In addition to this, <code>:nth-child()</code> can take <code>odd</code> and <code>even</code> as arguments instead. <code>odd</code> has the same signification as <code>2n+1</code>, and <code>even</code> has the same signification as <code>2n</code>.</td><td><code>tr:nth-child(2n+1)</code> finds every odd row of a table. <code>:nth-child(10n-1)</code> the 9th, 19th, 29th, etc, element. <code>li:nth-child(5)</code> the 5h li</td></tr>
* <tr><td><code>:nth-last-child(<em>a</em>n+<em>b</em>)</code></td><td>elements that have <code><em>a</em>n+<em>b</em>-1</code> siblings <b>after</b> it in the document tree. Otherwise like <code>:nth-child()</code></td><td><code>tr:nth-last-child(-n+2)</code> the last two rows of a table</td></tr>
* <tr><td><code>:nth-of-type(<em>a</em>n+<em>b</em>)</code></td><td>pseudo-class notation represents an element that has <code><em>a</em>n+<em>b</em>-1</code> siblings with the same expanded element name <em>before</em> it in the document tree, for any zero or positive integer value of n, and has a parent element</td><td><code>img:nth-of-type(2n+1)</code></td></tr>
* <tr><td><code>:nth-last-of-type(<em>a</em>n+<em>b</em>)</code></td><td>pseudo-class notation represents an element that has <code><em>a</em>n+<em>b</em>-1</code> siblings with the same expanded element name <em>after</em> it in the document tree, for any zero or positive integer value of n, and has a parent element</td><td><code>img:nth-last-of-type(2n+1)</code></td></tr>
* <tr><td><code>:first-child</code></td><td>elements that are the first child of some other element.</td><td><code>div {@literal >} p:first-child</code></td></tr>
* <tr><td><code>:last-child</code></td><td>elements that are the last child of some other element.</td><td><code>ol {@literal >} li:last-child</code></td></tr>
* <tr><td><code>:first-of-type</code></td><td>elements that are the first sibling of its type in the list of children of its parent element</td><td><code>dl dt:first-of-type</code></td></tr>
* <tr><td><code>:last-of-type</code></td><td>elements that are the last sibling of its type in the list of children of its parent element</td><td><code>tr {@literal >} td:last-of-type</code></td></tr>
* <tr><td><code>:only-child</code></td><td>elements that have a parent element and whose parent element hasve no other element children</td><td></td></tr>
* <tr><td><code>:only-of-type</code></td><td> an element that has a parent element and whose parent element has no other element children with the same expanded element name</td><td></td></tr>
* <tr><td><code>:empty</code></td><td>elements that have no children at all</td><td></td></tr>
* </table>
*
* @see Element#select(String)
*/
open class Selector {
private let evaluator: Evaluator
private let root: Element
private init(_ query: String, _ root: Element)throws {
let query = query.trim()
try Validate.notEmpty(string: query)
self.evaluator = try QueryParser.parse(query)
self.root = root
}
private init(_ evaluator: Evaluator, _ root: Element) {
self.evaluator = evaluator
self.root = root
}
/**
* Find elements matching selector.
*
* @param query CSS selector
* @param root root element to descend into
* @return matching elements, empty if none
* @throws Selector.SelectorParseException (unchecked) on an invalid CSS query.
*/
public static func select(_ query: String, _ root: Element)throws->Elements {
return try Selector(query, root).select()
}
/**
* Find elements matching selector.
*
* @param evaluator CSS selector
* @param root root element to descend into
* @return matching elements, empty if none
*/
public static func select(_ evaluator: Evaluator, _ root: Element)throws->Elements {
return try Selector(evaluator, root).select()
}
/**
* Find elements matching selector.
*
* @param query CSS selector
* @param roots root elements to descend into
* @return matching elements, empty if none
*/
public static func select(_ query: String, _ roots: Array<Element>)throws->Elements {
try Validate.notEmpty(string: query)
let evaluator: Evaluator = try QueryParser.parse(query)
var elements: Array<Element> = Array<Element>()
var seenElements: Array<Element> = Array<Element>()
// dedupe elements by identity, not equality
for root: Element in roots {
let found: Elements = try select(evaluator, root)
for el: Element in found.array() {
if (!seenElements.contains(el)) {
elements.append(el)
seenElements.append(el)
}
}
}
return Elements(elements)
}
private func select()throws->Elements {
return try Collector.collect(evaluator, root)
}
// exclude set. package open so that Elements can implement .not() selector.
static func filterOut(_ elements: Array<Element>, _ outs: Array<Element>) -> Elements {
let output: Elements = Elements()
for el: Element in elements {
var found: Bool = false
for out: Element in outs {
if (el.equals(out)) {
found = true
break
}
}
if (!found) {
output.add(el)
}
}
return output
}
}

10
Pods/SwiftSoup/Sources/SerializationException.swift

@ -1,10 +0,0 @@
//
// SerializationException.swift
// SwifSoup
//
// Created by Nabil Chatbi on 29/09/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
//TODO:

39
Pods/SwiftSoup/Sources/SimpleDictionary.swift

@ -1,39 +0,0 @@
//
// SimpleDictionary.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 30/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
public class SimpleDictionary<KeyType: Hashable, ValueType> {
public typealias DictionaryType = [KeyType: ValueType]
public private(set) var values = DictionaryType()
public init() {
}
public var count: Int {
return values.count
}
public func remove(_ key: KeyType) {
values.removeValue(forKey: key)
}
public func contains(_ key: KeyType) -> Bool {
return self.values[key] != nil
}
public func put(_ value: ValueType, forKey key: KeyType) {
self.values[key] = value
}
public func get(_ key: KeyType) -> ValueType? {
return self.values[key]
}
}

89
Pods/SwiftSoup/Sources/StreamReader.swift

@ -1,89 +0,0 @@
//
// StreamReader.swift
// SwifSoup
//
// Created by Nabil Chatbi on 08/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
class StreamReader {
let encoding: String.Encoding
let chunkSize: Int
var fileHandle: FileHandle!
let delimData: Data
var buffer: Data
var atEof: Bool
init?(path: String, delimiter: String = "\n", encoding: String.Encoding = .utf8,
chunkSize: Int = 4096) {
guard let fileHandle = FileHandle(forReadingAtPath: path),
let delimData = delimiter.data(using: encoding) else {
return nil
}
self.encoding = encoding
self.chunkSize = chunkSize
self.fileHandle = fileHandle
self.delimData = delimData
self.buffer = Data(capacity: chunkSize)
self.atEof = false
}
deinit {
self.close()
}
/// Return next line, or nil on EOF.
func nextLine() -> String? {
precondition(fileHandle != nil, "Attempt to read from closed file")
// Read data chunks from file until a line delimiter is found:
while !atEof {
if let range = buffer.range(of: delimData) {
// Convert complete line (excluding the delimiter) to a string:
let line = String(data: buffer.subdata(in: 0..<range.lowerBound), encoding: encoding)
// Remove line (and the delimiter) from the buffer:
buffer.removeSubrange(0..<range.upperBound)
return line
}
let tmpData = fileHandle.readData(ofLength: chunkSize)
if tmpData.count > 0 {
buffer.append(tmpData)
} else {
// EOF or read error.
atEof = true
if buffer.count > 0 {
// Buffer contains last line in file (not terminated by delimiter).
let line = String(data: buffer as Data, encoding: encoding)
buffer.count = 0
return line
}
}
}
return nil
}
/// Start reading from the beginning of file.
func rewind() {
fileHandle.seek(toFileOffset: 0)
buffer.count = 0
atEof = false
}
/// Close the underlying file. No reading must be done after calling this method.
func close() {
fileHandle?.closeFile()
fileHandle = nil
}
}
extension StreamReader: Sequence {
func makeIterator() -> AnyIterator<String> {
return AnyIterator {
return self.nextLine()
}
}
}

206
Pods/SwiftSoup/Sources/String.swift

@ -1,206 +0,0 @@
//
// String.swift
// SwifSoup
//
// Created by Nabil Chatbi on 21/04/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
extension String {
subscript (i: Int) -> Character {
return self[self.index(self.startIndex, offsetBy: i)]
}
subscript (i: Int) -> String {
return String(self[i] as Character)
}
init<S: Sequence>(_ ucs: S)where S.Iterator.Element == UnicodeScalar {
var s = ""
s.unicodeScalars.append(contentsOf: ucs)
self = s
}
func unicodeScalar(_ i: Int) -> UnicodeScalar {
return self.unicodeScalars.prefix(i+1).last!
}
func string(_ offset: Int, _ count: Int) -> String {
let truncStart = self.unicodeScalars.count-offset
return String(self.unicodeScalars.suffix(truncStart).prefix(count))
}
static func split(_ value: String, _ offset: Int, _ count: Int) -> String {
let start = value.index(value.startIndex, offsetBy: offset)
let end = value.index(value.startIndex, offsetBy: count+offset)
#if swift(>=4)
return String(value[start..<end])
#else
let range = start..<end
return value.substring(with: range)
#endif
}
func isEmptyOrWhitespace() -> Bool {
if(self.isEmpty) {
return true
}
return (self.trimmingCharacters(in: CharacterSet.whitespaces) == "")
}
func startsWith(_ string: String) -> Bool {
return self.hasPrefix(string)
}
func indexOf(_ substring: String, _ offset: Int ) -> Int {
if(offset > count) {return -1}
let maxIndex = self.count - substring.count
if(maxIndex >= 0) {
for index in offset...maxIndex {
let rangeSubstring = self.index(self.startIndex, offsetBy: index)..<self.index(self.startIndex, offsetBy: index + substring.count)
#if swift(>=4)
let selfSubstring = self[rangeSubstring]
#else
let selfSubstring = self.substring(with: rangeSubstring)
#endif
if selfSubstring == substring {
return index
}
}
}
return -1
}
func indexOf(_ substring: String) -> Int {
return self.indexOf(substring, 0)
}
func trim() -> String {
return trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines)
}
func equalsIgnoreCase(string: String?) -> Bool {
if(string == nil) {return false}
return string!.lowercased() == lowercased()
}
static func toHexString(n: Int) -> String {
return String(format: "%2x", n)
}
func insert(string: String, ind: Int) -> String {
return String(self.prefix(ind)) + string + String(self.suffix(self.count-ind))
}
func charAt(_ i: Int) -> Character {
return self[i] as Character
}
func substring(_ beginIndex: Int) -> String {
return String.split(self, beginIndex, self.count-beginIndex)
}
func substring(_ beginIndex: Int, _ count: Int) -> String {
return String.split(self, beginIndex, count)
}
func regionMatches(_ ignoreCase: Bool, _ selfOffset: Int, _ other: String, _ otherOffset: Int, _ length: Int ) -> Bool {
if ((otherOffset < 0) || (selfOffset < 0)
|| (selfOffset > self.count - length)
|| (otherOffset > other.count - length)) {
return false
}
for i in 0..<length {
let charSelf: Character = self[i+selfOffset]
let charOther: Character = other[i+otherOffset]
if(ignoreCase) {
if(charSelf.lowercase != charOther.lowercase) {
return false
}
} else {
if(charSelf != charOther) {
return false
}
}
}
return true
}
func startsWith(_ input: String, _ offset: Int) -> Bool {
if ((offset < 0) || (offset > count - input.count)) {
return false
}
for i in 0..<input.count {
let charSelf: Character = self[i+offset]
let charOther: Character = input[i]
if(charSelf != charOther) {return false}
}
return true
}
func replaceFirst(of pattern: String, with replacement: String) -> String {
if let range = self.range(of: pattern) {
return self.replacingCharacters(in: range, with: replacement)
} else {
return self
}
}
func replaceAll(of pattern: String, with replacement: String, options: NSRegularExpression.Options = []) -> String {
do {
let regex = try NSRegularExpression(pattern: pattern, options: [])
let range = NSRange(0..<self.utf16.count)
return regex.stringByReplacingMatches(in: self, options: [],
range: range, withTemplate: replacement)
} catch {
return self
}
}
func equals(_ s: String?) -> Bool {
if(s == nil) {return false}
return self == s!
}
}
extension String.Encoding {
func canEncode(_ string: String) -> Bool {
return string.cString(using: self) != nil
}
public func displayName() -> String {
switch self {
case String.Encoding.ascii: return "US-ASCII"
case String.Encoding.nextstep: return "nextstep"
case String.Encoding.japaneseEUC: return "EUC-JP"
case String.Encoding.utf8: return "UTF-8"
case String.Encoding.isoLatin1: return "csISOLatin1"
case String.Encoding.symbol: return "MacSymbol"
case String.Encoding.nonLossyASCII: return "nonLossyASCII"
case String.Encoding.shiftJIS: return "shiftJIS"
case String.Encoding.isoLatin2: return "csISOLatin2"
case String.Encoding.unicode: return "unicode"
case String.Encoding.windowsCP1251: return "windows-1251"
case String.Encoding.windowsCP1252: return "windows-1252"
case String.Encoding.windowsCP1253: return "windows-1253"
case String.Encoding.windowsCP1254: return "windows-1254"
case String.Encoding.windowsCP1250: return "windows-1250"
case String.Encoding.iso2022JP: return "iso2022jp"
case String.Encoding.macOSRoman: return "macOSRoman"
case String.Encoding.utf16: return "UTF-16"
case String.Encoding.utf16BigEndian: return "UTF-16BE"
case String.Encoding.utf16LittleEndian: return "UTF-16LE"
case String.Encoding.utf32: return "UTF-32"
case String.Encoding.utf32BigEndian: return "UTF-32BE"
case String.Encoding.utf32LittleEndian: return "UTF-32LE"
default:
return self.description
}
}
}

165
Pods/SwiftSoup/Sources/StringBuilder.swift

@ -1,165 +0,0 @@
/**
Supports creation of a String from pieces
https://gist.github.com/kristopherjohnson/1fc55e811d944a430289
*/
open class StringBuilder {
fileprivate var stringValue: Array<Character>
/**
Construct with initial String contents
:param: string Initial value; defaults to empty string
*/
public init(string: String = "") {
self.stringValue = Array(string)
}
public init(_ size: Int) {
self.stringValue = Array()
}
/**
Return the String object
:return: String
*/
open func toString() -> String {
return String(stringValue)
}
/**
Return the current length of the String object
*/
open var length: Int {
return self.stringValue.count
//return countElements(stringValue)
}
/**
Append a String to the object
:param: string String
:return: reference to this StringBuilder instance
*/
open func append(_ string: String) {
stringValue.append(contentsOf: string)
}
open func appendCodePoint(_ chr: Character) {
stringValue.append(chr)
}
open func appendCodePoints(_ chr: [Character]) {
stringValue.append(contentsOf: chr)
}
open func appendCodePoint(_ ch: Int) {
stringValue.append(Character(UnicodeScalar(ch)!))
}
open func appendCodePoint(_ ch: UnicodeScalar) {
stringValue.append(Character(ch))
}
open func appendCodePoints(_ chr: [UnicodeScalar]) {
for c in chr {
appendCodePoint(c)
}
}
/**
Append a Printable to the object
:param: value a value supporting the Printable protocol
:return: reference to this StringBuilder instance
*/
@discardableResult
open func append<T: CustomStringConvertible>(_ value: T) -> StringBuilder {
stringValue.append(contentsOf: value.description)
return self
}
@discardableResult
open func append(_ value: UnicodeScalar) -> StringBuilder {
stringValue.append(contentsOf: value.description)
return self
}
@discardableResult
open func insert<T: CustomStringConvertible>(_ offset: Int, _ value: T) -> StringBuilder {
stringValue.insert(contentsOf: value.description, at: offset)
return self
}
/**
Append a String and a newline to the object
:param: string String
:return: reference to this StringBuilder instance
*/
@discardableResult
open func appendLine(_ string: String) -> StringBuilder {
stringValue.append(contentsOf: "\n")
return self
}
/**
Append a Printable and a newline to the object
:param: value a value supporting the Printable protocol
:return: reference to this StringBuilder instance
*/
@discardableResult
open func appendLine<T: CustomStringConvertible>(_ value: T) -> StringBuilder {
stringValue.append(contentsOf: value.description)
stringValue.append(contentsOf: "\n")
return self
}
/**
Reset the object to an empty string
:return: reference to this StringBuilder instance
*/
@discardableResult
open func clear() -> StringBuilder {
stringValue = Array()
return self
}
}
/**
Append a String to a StringBuilder using operator syntax
:param: lhs StringBuilder
:param: rhs String
*/
public func += (lhs: StringBuilder, rhs: String) {
lhs.append(rhs)
}
/**
Append a Printable to a StringBuilder using operator syntax
:param: lhs Printable
:param: rhs String
*/
public func += <T: CustomStringConvertible>(lhs: StringBuilder, rhs: T) {
lhs.append(rhs.description)
}
/**
Create a StringBuilder by concatenating the values of two StringBuilders
:param: lhs first StringBuilder
:param: rhs second StringBuilder
:result StringBuilder
*/
public func +(lhs: StringBuilder, rhs: StringBuilder) -> StringBuilder {
return StringBuilder(string: lhs.toString() + rhs.toString())
}

262
Pods/SwiftSoup/Sources/StringUtil.swift

@ -1,262 +0,0 @@
//
// StringUtil.swift
// SwifSoup
//
// Created by Nabil Chatbi on 20/04/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
* A minimal String utility class. Designed for internal jsoup use only.
*/
open class StringUtil {
enum StringError: Error {
case empty
case short
case error(String)
}
// memoised padding up to 10
fileprivate static let padding: [String] = ["", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
private static let empty = ""
private static let space = " "
/**
* Join a collection of strings by a seperator
* @param strings collection of string objects
* @param sep string to place between strings
* @return joined string
*/
public static func join(_ strings: [String], sep: String) -> String {
return strings.joined(separator: sep)
}
public static func join(_ strings: Set<String>, sep: String) -> String {
return strings.joined(separator: sep)
}
public static func join(_ strings: OrderedSet<String>, sep: String) -> String {
return strings.joined(separator: sep)
}
// /**
// * Join a collection of strings by a seperator
// * @param strings iterator of string objects
// * @param sep string to place between strings
// * @return joined string
// */
// public static String join(Iterator strings, String sep) {
// if (!strings.hasNext())
// return ""
//
// String start = strings.next().toString()
// if (!strings.hasNext()) // only one, avoid builder
// return start
//
// StringBuilder sb = new StringBuilder(64).append(start)
// while (strings.hasNext()) {
// sb.append(sep)
// sb.append(strings.next())
// }
// return sb.toString()
// }
/**
* Returns space padding
* @param width amount of padding desired
* @return string of spaces * width
*/
public static func padding(_ width: Int) -> String {
if width <= 0 {
return empty
}
if width < padding.count {
return padding[width]
}
return String.init(repeating: space, count: width)
}
/**
* Tests if a string is blank: null, emtpy, or only whitespace (" ", \r\n, \t, etc)
* @param string string to test
* @return if string is blank
*/
public static func isBlank(_ string: String) -> Bool {
if (string.count == 0) {
return true
}
for chr in string {
if (!StringUtil.isWhitespace(chr)) {
return false
}
}
return true
}
/**
* Tests if a string is numeric, i.e. contains only digit characters
* @param string string to test
* @return true if only digit chars, false if empty or null or contains non-digit chrs
*/
public static func isNumeric(_ string: String) -> Bool {
if (string.count == 0) {
return false
}
for chr in string {
if !("0"..."9" ~= chr) {
return false
}
}
return true
}
/**
* Tests if a code point is "whitespace" as defined in the HTML spec.
* @param c code point to test
* @return true if code point is whitespace, false otherwise
*/
public static func isWhitespace(_ c: Character) -> Bool {
//(c == " " || c == UnicodeScalar.BackslashT || c == "\n" || (c == "\f" ) || c == "\r")
return c.isWhitespace
}
/**
* Normalise the whitespace within this string; multiple spaces collapse to a single, and all whitespace characters
* (e.g. newline, tab) convert to a simple space
* @param string content to normalise
* @return normalised string
*/
public static func normaliseWhitespace(_ string: String) -> String {
let sb: StringBuilder = StringBuilder.init()
appendNormalisedWhitespace(sb, string: string, stripLeading: false)
return sb.toString()
}
/**
* After normalizing the whitespace within a string, appends it to a string builder.
* @param accum builder to append to
* @param string string to normalize whitespace within
* @param stripLeading set to true if you wish to remove any leading whitespace
*/
public static func appendNormalisedWhitespace(_ accum: StringBuilder, string: String, stripLeading: Bool ) {
var lastWasWhite: Bool = false
var reachedNonWhite: Bool = false
for c in string {
if (isWhitespace(c)) {
if ((stripLeading && !reachedNonWhite) || lastWasWhite) {
continue
}
accum.append(" ")
lastWasWhite = true
} else {
accum.appendCodePoint(c)
lastWasWhite = false
reachedNonWhite = true
}
}
}
public static func inString(_ needle: String?, haystack: String...) -> Bool {
return inString(needle, haystack)
}
public static func inString(_ needle: String?, _ haystack: [String?]) -> Bool {
if(needle == nil) {return false}
for hay in haystack {
if(hay != nil && hay! == needle!) {
return true
}
}
return false
}
// open static func inSorted(_ needle: String, haystack: [String]) -> Bool {
// return binarySearch(haystack, searchItem: needle) >= 0
// }
//
// open static func binarySearch<T: Comparable>(_ inputArr: Array<T>, searchItem: T) -> Int {
// var lowerIndex = 0
// var upperIndex = inputArr.count - 1
//
// while (true) {
// let currentIndex = (lowerIndex + upperIndex)/2
// if(inputArr[currentIndex] == searchItem) {
// return currentIndex
// } else if (lowerIndex > upperIndex) {
// return -1
// } else {
// if (inputArr[currentIndex] > searchItem) {
// upperIndex = currentIndex - 1
// } else {
// lowerIndex = currentIndex + 1
// }
// }
// }
// }
/**
* Create a new absolute URL, from a provided existing absolute URL and a relative URL component.
* @param base the existing absolulte base URL
* @param relUrl the relative URL to resolve. (If it's already absolute, it will be returned)
* @return the resolved absolute URL
* @throws MalformedURLException if an error occurred generating the URL
*/
//NOTE: Not sure it work
public static func resolve(_ base: URL, relUrl: String ) -> URL? {
var base = base
if(base.pathComponents.count == 0 && base.absoluteString.last != "/" && !base.isFileURL) {
base = base.appendingPathComponent("/", isDirectory: false)
}
let u = URL(string: relUrl, relativeTo: base)
return u
}
/**
* Create a new absolute URL, from a provided existing absolute URL and a relative URL component.
* @param baseUrl the existing absolute base URL
* @param relUrl the relative URL to resolve. (If it's already absolute, it will be returned)
* @return an absolute URL if one was able to be generated, or the empty string if not
*/
public static func resolve(_ baseUrl: String, relUrl: String ) -> String {
let base = URL(string: baseUrl)
if(base == nil || base?.scheme == nil) {
let abs = URL(string: relUrl)
return abs != nil && abs?.scheme != nil ? abs!.absoluteURL.absoluteString : empty
} else {
let url = resolve(base!, relUrl: relUrl)
if(url != nil) {
let ext = url!.absoluteURL.absoluteString
return ext
}
if(base != nil && base?.scheme != nil) {
let ext = base!.absoluteString
return ext
}
return empty
}
// try {
// try {
// base = new URL(baseUrl)
// } catch (MalformedURLException e) {
// // the base is unsuitable, but the attribute/rel may be abs on its own, so try that
// URL abs = new URL(relUrl)
// return abs.toExternalForm()
// }
// return resolve(base, relUrl).toExternalForm()
// } catch (MalformedURLException e) {
// return ""
// }
}
}

176
Pods/SwiftSoup/Sources/StructuralEvaluator.swift

@ -1,176 +0,0 @@
//
// StructuralEvaluator.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 23/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
* Base structural evaluator.
*/
public class StructuralEvaluator: Evaluator {
let evaluator: Evaluator
public init(_ evaluator: Evaluator) {
self.evaluator = evaluator
}
public class Root: Evaluator {
public override func matches(_ root: Element, _ element: Element) -> Bool {
return root === element
}
}
public class Has: StructuralEvaluator {
public override init(_ evaluator: Evaluator) {
super.init(evaluator)
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
for e in try element.getAllElements().array() {
do {
if(e != element) {
if ((try evaluator.matches(root, e))) {
return true
}
}
} catch {}
}
return false
}
public override func toString() -> String {
return ":has(\(evaluator.toString()))"
}
}
public class Not: StructuralEvaluator {
public override init(_ evaluator: Evaluator) {
super.init(evaluator)
}
public override func matches(_ root: Element, _ node: Element) -> Bool {
do {
return try !evaluator.matches(root, node)
} catch {}
return false
}
public override func toString() -> String {
return ":not\(evaluator.toString())"
}
}
public class Parent: StructuralEvaluator {
public override init(_ evaluator: Evaluator) {
super.init(evaluator)
}
public override func matches(_ root: Element, _ element: Element) -> Bool {
if (root == element) {
return false
}
var parent = element.parent()
while (true) {
do {
if parent != nil {
if (try evaluator.matches(root, parent!)) {
return true
}
}
} catch {}
if (parent == root) {
break
}
parent = parent?.parent()
}
return false
}
public override func toString() -> String {
return ":parent\(evaluator.toString())"
}
}
public class ImmediateParent: StructuralEvaluator {
public override init(_ evaluator: Evaluator) {
super.init(evaluator)
}
public override func matches(_ root: Element, _ element: Element) -> Bool {
if (root == element) {
return false
}
if let parent = element.parent() {
do {
return try evaluator.matches(root, parent)
} catch {}
}
return false
}
public override func toString() -> String {
return ":ImmediateParent\(evaluator.toString())"
}
}
public class PreviousSibling: StructuralEvaluator {
public override init(_ evaluator: Evaluator) {
super.init(evaluator)
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
if (root == element) {
return false
}
var prev = try element.previousElementSibling()
while (prev != nil) {
do {
if (try evaluator.matches(root, prev!)) {
return true
}
} catch {}
prev = try prev!.previousElementSibling()
}
return false
}
public override func toString() -> String {
return ":prev*\(evaluator.toString())"
}
}
class ImmediatePreviousSibling: StructuralEvaluator {
public override init(_ evaluator: Evaluator) {
super.init(evaluator)
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
if (root == element) {
return false
}
if let prev = try element.previousElementSibling() {
do {
return try evaluator.matches(root, prev)
} catch {}
}
return false
}
public override func toString() -> String {
return ":prev\(evaluator.toString())"
}
}
}

242
Pods/SwiftSoup/Sources/SwiftSoup.swift

@ -1,242 +0,0 @@
//
// SwiftSoup.swift
// Jsoup
//
// Created by Nabil Chatbi on 29/09/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
Parse HTML into a Document. The parser will make a sensible, balanced document tree out of any HTML.
@param html HTML to parse
@param baseUri The URL where the HTML was retrieved from. Used to resolve relative URLs to absolute URLs, that occur
before the HTML declares a {@code <base href>} tag.
@return sane HTML
*/
public func parse(_ html: String, _ baseUri: String)throws->Document {
return try Parser.parse(html, baseUri)
}
/**
Parse HTML into a Document, using the provided Parser. You can provide an alternate parser, such as a simple XML
(non-HTML) parser.
@param html HTML to parse
@param baseUri The URL where the HTML was retrieved from. Used to resolve relative URLs to absolute URLs, that occur
before the HTML declares a {@code <base href>} tag.
@param parser alternate {@link Parser#xmlParser() parser} to use.
@return sane HTML
*/
public func parse(_ html: String, _ baseUri: String, _ parser: Parser)throws->Document {
return try parser.parseInput(html, baseUri)
}
/**
Parse HTML into a Document. As no base URI is specified, absolute URL detection relies on the HTML including a
{@code <base href>} tag.
@param html HTML to parse
@return sane HTML
@see #parse(String, String)
*/
public func parse(_ html: String)throws->Document {
return try Parser.parse(html, "")
}
//todo:
// /**
// * Creates a new {@link Connection} to a URL. Use to fetch and parse a HTML page.
// * <p>
// * Use examples:
// * <ul>
// * <li><code>Document doc = Jsoup.connect("http://example.com").userAgent("Mozilla").data("name", "jsoup").get();</code></li>
// * <li><code>Document doc = Jsoup.connect("http://example.com").cookie("auth", "token").post();</code></li>
// * </ul>
// * @param url URL to connect to. The protocol must be {@code http} or {@code https}.
// * @return the connection. You can add data, cookies, and headers; set the user-agent, referrer, method; and then execute.
// */
// public static Connection connect(String url) {
// return HttpConnection.connect(url);
// }
//todo:
// /**
// Parse the contents of a file as HTML.
//
// @param in file to load HTML from
// @param charsetName (optional) character set of file contents. Set to {@code null} to determine from {@code http-equiv} meta tag, if
// present, or fall back to {@code UTF-8} (which is often safe to do).
// @param baseUri The URL where the HTML was retrieved from, to resolve relative links against.
// @return sane HTML
//
// @throws IOException if the file could not be found, or read, or if the charsetName is invalid.
// */
// public static Document parse(File in, String charsetName, String baseUri) throws IOException {
// return DataUtil.load(in, charsetName, baseUri);
// }
//todo:
// /**
// Parse the contents of a file as HTML. The location of the file is used as the base URI to qualify relative URLs.
//
// @param in file to load HTML from
// @param charsetName (optional) character set of file contents. Set to {@code null} to determine from {@code http-equiv} meta tag, if
// present, or fall back to {@code UTF-8} (which is often safe to do).
// @return sane HTML
//
// @throws IOException if the file could not be found, or read, or if the charsetName is invalid.
// @see #parse(File, String, String)
// */
// public static Document parse(File in, String charsetName) throws IOException {
// return DataUtil.load(in, charsetName, in.getAbsolutePath());
// }
// /**
// Read an input stream, and parse it to a Document.
//
// @param in input stream to read. Make sure to close it after parsing.
// @param charsetName (optional) character set of file contents. Set to {@code null} to determine from {@code http-equiv} meta tag, if
// present, or fall back to {@code UTF-8} (which is often safe to do).
// @param baseUri The URL where the HTML was retrieved from, to resolve relative links against.
// @return sane HTML
//
// @throws IOException if the file could not be found, or read, or if the charsetName is invalid.
// */
// public static Document parse(InputStream in, String charsetName, String baseUri) throws IOException {
// return DataUtil.load(in, charsetName, baseUri);
// }
// /**
// Read an input stream, and parse it to a Document. You can provide an alternate parser, such as a simple XML
// (non-HTML) parser.
//
// @param in input stream to read. Make sure to close it after parsing.
// @param charsetName (optional) character set of file contents. Set to {@code null} to determine from {@code http-equiv} meta tag, if
// present, or fall back to {@code UTF-8} (which is often safe to do).
// @param baseUri The URL where the HTML was retrieved from, to resolve relative links against.
// @param parser alternate {@link Parser#xmlParser() parser} to use.
// @return sane HTML
//
// @throws IOException if the file could not be found, or read, or if the charsetName is invalid.
// */
// public static Document parse(InputStream in, String charsetName, String baseUri, Parser parser) throws IOException {
// return DataUtil.load(in, charsetName, baseUri, parser);
// }
/**
Parse a fragment of HTML, with the assumption that it forms the {@code body} of the HTML.
@param bodyHtml body HTML fragment
@param baseUri URL to resolve relative URLs against.
@return sane HTML document
@see Document#body()
*/
public func parseBodyFragment(_ bodyHtml: String, _ baseUri: String)throws->Document {
return try Parser.parseBodyFragment(bodyHtml, baseUri)
}
/**
Parse a fragment of HTML, with the assumption that it forms the {@code body} of the HTML.
@param bodyHtml body HTML fragment
@return sane HTML document
@see Document#body()
*/
public func parseBodyFragment(_ bodyHtml: String)throws->Document {
return try Parser.parseBodyFragment(bodyHtml, "")
}
// /**
// Fetch a URL, and parse it as HTML. Provided for compatibility; in most cases use {@link #connect(String)} instead.
// <p>
// The encoding character set is determined by the content-type header or http-equiv meta tag, or falls back to {@code UTF-8}.
//
// @param url URL to fetch (with a GET). The protocol must be {@code http} or {@code https}.
// @param timeoutMillis Connection and read timeout, in milliseconds. If exceeded, IOException is thrown.
// @return The parsed HTML.
//
// @throws java.net.MalformedURLException if the request URL is not a HTTP or HTTPS URL, or is otherwise malformed
// @throws HttpStatusException if the response is not OK and HTTP response errors are not ignored
// @throws UnsupportedMimeTypeException if the response mime type is not supported and those errors are not ignored
// @throws java.net.SocketTimeoutException if the connection times out
// @throws IOException if a connection or read error occurs
//
// @see #connect(String)
// */
// public static func parse(_ url: URL, _ timeoutMillis: Int)throws->Document {
// Connection con = HttpConnection.connect(url);
// con.timeout(timeoutMillis);
// return con.get();
// }
/**
Get safe HTML from untrusted input HTML, by parsing input HTML and filtering it through a white-list of permitted
tags and attributes.
@param bodyHtml input untrusted HTML (body fragment)
@param baseUri URL to resolve relative URLs against
@param whitelist white-list of permitted HTML elements
@return safe HTML (body fragment)
@see Cleaner#clean(Document)
*/
public func clean(_ bodyHtml: String, _ baseUri: String, _ whitelist: Whitelist)throws->String? {
let dirty: Document = try parseBodyFragment(bodyHtml, baseUri)
let cleaner: Cleaner = Cleaner(whitelist)
let clean: Document = try cleaner.clean(dirty)
return try clean.body()?.html()
}
/**
Get safe HTML from untrusted input HTML, by parsing input HTML and filtering it through a white-list of permitted
tags and attributes.
@param bodyHtml input untrusted HTML (body fragment)
@param whitelist white-list of permitted HTML elements
@return safe HTML (body fragment)
@see Cleaner#clean(Document)
*/
public func clean(_ bodyHtml: String, _ whitelist: Whitelist)throws->String? {
return try SwiftSoup.clean(bodyHtml, "", whitelist)
}
/**
* Get safe HTML from untrusted input HTML, by parsing input HTML and filtering it through a white-list of
* permitted
* tags and attributes.
*
* @param bodyHtml input untrusted HTML (body fragment)
* @param baseUri URL to resolve relative URLs against
* @param whitelist white-list of permitted HTML elements
* @param outputSettings document output settings; use to control pretty-printing and entity escape modes
* @return safe HTML (body fragment)
* @see Cleaner#clean(Document)
*/
public func clean(_ bodyHtml: String, _ baseUri: String, _ whitelist: Whitelist, _ outputSettings: OutputSettings)throws->String? {
let dirty: Document = try SwiftSoup.parseBodyFragment(bodyHtml, baseUri)
let cleaner: Cleaner = Cleaner(whitelist)
let clean: Document = try cleaner.clean(dirty)
clean.outputSettings(outputSettings)
return try clean.body()?.html()
}
/**
Test if the input HTML has only tags and attributes allowed by the Whitelist. Useful for form validation. The input HTML should
still be run through the cleaner to set up enforced attributes, and to tidy the output.
@param bodyHtml HTML to test
@param whitelist whitelist to test against
@return true if no tags or attributes were removed; false otherwise
@see #clean(String, org.jsoup.safety.Whitelist)
*/
public func isValid(_ bodyHtml: String, _ whitelist: Whitelist)throws->Bool {
let dirty = try parseBodyFragment(bodyHtml, "")
let cleaner = Cleaner(whitelist)
return try cleaner.isValid(dirty)
}

347
Pods/SwiftSoup/Sources/Tag.swift

@ -1,347 +0,0 @@
//
// Tag.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 15/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
open class Tag: Hashable {
// map of known tags
static var tags: Dictionary<String, Tag> = {
do {
return try Tag.initializeMaps()
} catch {
preconditionFailure("This method must be overridden")
}
return Dictionary<String, Tag>()
}()
fileprivate var _tagName: String
fileprivate var _tagNameNormal: String
fileprivate var _isBlock: Bool = true // block or inline
fileprivate var _formatAsBlock: Bool = true // should be formatted as a block
fileprivate var _canContainBlock: Bool = true // Can this tag hold block level tags?
fileprivate var _canContainInline: Bool = true // only pcdata if not
fileprivate var _empty: Bool = false // can hold nothing e.g. img
fileprivate var _selfClosing: Bool = false // can self close (<foo />). used for unknown tags that self close, without forcing them as empty.
fileprivate var _preserveWhitespace: Bool = false // for pre, textarea, script etc
fileprivate var _formList: Bool = false // a control that appears in forms: input, textarea, output etc
fileprivate var _formSubmit: Bool = false // a control that can be submitted in a form: input etc
public init(_ tagName: String) {
self._tagName = tagName
self._tagNameNormal = tagName.lowercased()
}
/**
* Get this tag's name.
*
* @return the tag's name
*/
open func getName() -> String {
return self._tagName
}
open func getNameNormal() -> String {
return self._tagNameNormal
}
/**
* Get a Tag by name. If not previously defined (unknown), returns a new generic tag, that can do anything.
* <p>
* Pre-defined tags (P, DIV etc) will be ==, but unknown tags are not registered and will only .equals().
* </p>
*
* @param tagName Name of tag, e.g. "p". Case insensitive.
* @param settings used to control tag name sensitivity
* @return The tag, either defined or new generic.
*/
public static func valueOf(_ tagName: String, _ settings: ParseSettings)throws->Tag {
var tagName = tagName
var tag: Tag? = Tag.tags[tagName]
if (tag == nil) {
tagName = settings.normalizeTag(tagName)
try Validate.notEmpty(string: tagName)
tag = Tag.tags[tagName]
if (tag == nil) {
// not defined: create default; go anywhere, do anything! (incl be inside a <p>)
tag = Tag(tagName)
tag!._isBlock = false
tag!._canContainBlock = true
}
}
return tag!
}
/**
* Get a Tag by name. If not previously defined (unknown), returns a new generic tag, that can do anything.
* <p>
* Pre-defined tags (P, DIV etc) will be ==, but unknown tags are not registered and will only .equals().
* </p>
*
* @param tagName Name of tag, e.g. "p". <b>Case sensitive</b>.
* @return The tag, either defined or new generic.
*/
public static func valueOf(_ tagName: String)throws->Tag {
return try valueOf(tagName, ParseSettings.preserveCase)
}
/**
* Gets if this is a block tag.
*
* @return if block tag
*/
open func isBlock() -> Bool {
return _isBlock
}
/**
* Gets if this tag should be formatted as a block (or as inline)
*
* @return if should be formatted as block or inline
*/
open func formatAsBlock() -> Bool {
return _formatAsBlock
}
/**
* Gets if this tag can contain block tags.
*
* @return if tag can contain block tags
*/
open func canContainBlock() -> Bool {
return _canContainBlock
}
/**
* Gets if this tag is an inline tag.
*
* @return if this tag is an inline tag.
*/
open func isInline() -> Bool {
return !_isBlock
}
/**
* Gets if this tag is a data only tag.
*
* @return if this tag is a data only tag
*/
open func isData() -> Bool {
return !_canContainInline && !isEmpty()
}
/**
* Get if this is an empty tag
*
* @return if this is an empty tag
*/
open func isEmpty() -> Bool {
return _empty
}
/**
* Get if this tag is self closing.
*
* @return if this tag should be output as self closing.
*/
open func isSelfClosing() -> Bool {
return _empty || _selfClosing
}
/**
* Get if this is a pre-defined tag, or was auto created on parsing.
*
* @return if a known tag
*/
open func isKnownTag() -> Bool {
return Tag.tags[_tagName] != nil
}
/**
* Check if this tagname is a known tag.
*
* @param tagName name of tag
* @return if known HTML tag
*/
public static func isKnownTag(_ tagName: String) -> Bool {
return Tag.tags[tagName] != nil
}
/**
* Get if this tag should preserve whitespace within child text nodes.
*
* @return if preserve whitepace
*/
public func preserveWhitespace() -> Bool {
return _preserveWhitespace
}
/**
* Get if this tag represents a control associated with a form. E.g. input, textarea, output
* @return if associated with a form
*/
public func isFormListed() -> Bool {
return _formList
}
/**
* Get if this tag represents an element that should be submitted with a form. E.g. input, option
* @return if submittable with a form
*/
public func isFormSubmittable() -> Bool {
return _formSubmit
}
@discardableResult
func setSelfClosing() -> Tag {
_selfClosing = true
return self
}
/// Returns a Boolean value indicating whether two values are equal.
///
/// Equality is the inverse of inequality. For any values `a` and `b`,
/// `a == b` implies that `a != b` is `false`.
///
/// - Parameters:
/// - lhs: A value to compare.
/// - rhs: Another value to compare.
static public func ==(lhs: Tag, rhs: Tag) -> Bool {
let this = lhs
let o = rhs
if (this === o) {return true}
if (type(of: this) != type(of: o)) {return false}
let tag: Tag = o
if (lhs._tagName != tag._tagName) {return false}
if (lhs._canContainBlock != tag._canContainBlock) {return false}
if (lhs._canContainInline != tag._canContainInline) {return false}
if (lhs._empty != tag._empty) {return false}
if (lhs._formatAsBlock != tag._formatAsBlock) {return false}
if (lhs._isBlock != tag._isBlock) {return false}
if (lhs._preserveWhitespace != tag._preserveWhitespace) {return false}
if (lhs._selfClosing != tag._selfClosing) {return false}
if (lhs._formList != tag._formList) {return false}
return lhs._formSubmit == tag._formSubmit
}
public func equals(_ tag: Tag) -> Bool {
return self == tag
}
/// The hash value.
///
/// Hash values are not guaranteed to be equal across different executions of
/// your program. Do not save hash values to use during a future execution.
public func hash(into hasher: inout Hasher) {
hasher.combine(_tagName)
hasher.combine(_isBlock)
hasher.combine(_formatAsBlock)
hasher.combine(_canContainBlock)
hasher.combine(_canContainInline)
hasher.combine(_empty)
hasher.combine(_selfClosing)
hasher.combine(_preserveWhitespace)
hasher.combine(_formList)
hasher.combine(_formSubmit)
}
open func toString() -> String {
return _tagName
}
// internal static initialisers:
// prepped from http://www.w3.org/TR/REC-html40/sgml/dtd.html and other sources
private static let blockTags: [String] = [
"html", "head", "body", "frameset", "script", "noscript", "style", "meta", "link", "title", "frame",
"noframes", "section", "nav", "aside", "hgroup", "header", "footer", "p", "h1", "h2", "h3", "h4", "h5", "h6",
"ul", "ol", "pre", "div", "blockquote", "hr", "address", "figure", "figcaption", "form", "fieldset", "ins",
"del", "s", "dl", "dt", "dd", "li", "table", "caption", "thead", "tfoot", "tbody", "colgroup", "col", "tr", "th",
"td", "video", "audio", "canvas", "details", "menu", "plaintext", "template", "article", "main",
"svg", "math"
]
private static let inlineTags: [String] = [
"object", "base", "font", "tt", "i", "b", "u", "big", "small", "em", "strong", "dfn", "code", "samp", "kbd",
"var", "cite", "abbr", "time", "acronym", "mark", "ruby", "rt", "rp", "a", "img", "br", "wbr", "map", "q",
"sub", "sup", "bdo", "iframe", "embed", "span", "input", "select", "textarea", "label", "button", "optgroup",
"option", "legend", "datalist", "keygen", "output", "progress", "meter", "area", "param", "source", "track",
"summary", "command", "device", "area", "basefont", "bgsound", "menuitem", "param", "source", "track",
"data", "bdi"
]
private static let emptyTags: [String] = [
"meta", "link", "base", "frame", "img", "br", "wbr", "embed", "hr", "input", "keygen", "col", "command",
"device", "area", "basefont", "bgsound", "menuitem", "param", "source", "track"
]
private static let formatAsInlineTags: [String] = [
"title", "a", "p", "h1", "h2", "h3", "h4", "h5", "h6", "pre", "address", "li", "th", "td", "script", "style",
"ins", "del", "s"
]
private static let preserveWhitespaceTags: [String] = [
"pre", "plaintext", "title", "textarea"
// script is not here as it is a data node, which always preserve whitespace
]
// todo: I think we just need submit tags, and can scrub listed
private static let formListedTags: [String] = [
"button", "fieldset", "input", "keygen", "object", "output", "select", "textarea"
]
private static let formSubmitTags: [String] = [
"input", "keygen", "object", "select", "textarea"
]
static private func initializeMaps()throws->Dictionary<String, Tag> {
var dict = Dictionary<String, Tag>()
// creates
for tagName in blockTags {
let tag = Tag(tagName)
dict[tag._tagName] = tag
}
for tagName in inlineTags {
let tag = Tag(tagName)
tag._isBlock = false
tag._canContainBlock = false
tag._formatAsBlock = false
dict[tag._tagName] = tag
}
// mods:
for tagName in emptyTags {
let tag = dict[tagName]
try Validate.notNull(obj: tag)
tag?._canContainBlock = false
tag?._canContainInline = false
tag?._empty = true
}
for tagName in formatAsInlineTags {
let tag = dict[tagName]
try Validate.notNull(obj: tag)
tag?._formatAsBlock = false
}
for tagName in preserveWhitespaceTags {
let tag = dict[tagName]
try Validate.notNull(obj: tag)
tag?._preserveWhitespace = true
}
for tagName in formListedTags {
let tag = dict[tagName]
try Validate.notNull(obj: tag)
tag?._formList = true
}
for tagName in formSubmitTags {
let tag = dict[tagName]
try Validate.notNull(obj: tag)
tag?._formSubmit = true
}
return dict
}
}

199
Pods/SwiftSoup/Sources/TextNode.swift

@ -1,199 +0,0 @@
//
// TextNode.swift
// SwifSoup
//
// Created by Nabil Chatbi on 29/09/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
A text node.
*/
open class TextNode: Node {
/*
TextNode is a node, and so by default comes with attributes and children. The attributes are seldom used, but use
memory, and the child nodes are never used. So we don't have them, and override accessors to attributes to create
them as needed on the fly.
*/
private static let TEXT_KEY: String = "text"
var _text: String
/**
Create a new TextNode representing the supplied (unencoded) text).
@param text raw text
@param baseUri base uri
@see #createFromEncoded(String, String)
*/
public init(_ text: String, _ baseUri: String?) {
self._text = text
super.init()
self.baseUri = baseUri
}
open override func nodeName() -> String {
return "#text"
}
/**
* Get the text content of this text node.
* @return Unencoded, normalised text.
* @see TextNode#getWholeText()
*/
open func text() -> String {
return TextNode.normaliseWhitespace(getWholeText())
}
/**
* Set the text content of this text node.
* @param text unencoded text
* @return this, for chaining
*/
@discardableResult
public func text(_ text: String) -> TextNode {
self._text = text
guard let attributes = attributes else {
return self
}
do {
try attributes.put(TextNode.TEXT_KEY, text)
} catch {
}
return self
}
/**
Get the (unencoded) text of this text node, including any newlines and spaces present in the original.
@return text
*/
open func getWholeText() -> String {
return attributes == nil ? _text : attributes!.get(key: TextNode.TEXT_KEY)
}
/**
Test if this text node is blank -- that is, empty or only whitespace (including newlines).
@return true if this document is empty or only whitespace, false if it contains any text content.
*/
open func isBlank() -> Bool {
return StringUtil.isBlank(getWholeText())
}
/**
* Split this text node into two nodes at the specified string offset. After splitting, this node will contain the
* original text up to the offset, and will have a new text node sibling containing the text after the offset.
* @param offset string offset point to split node at.
* @return the newly created text node containing the text after the offset.
*/
open func splitText(_ offset: Int)throws->TextNode {
try Validate.isTrue(val: offset >= 0, msg: "Split offset must be not be negative")
try Validate.isTrue(val: offset < _text.count, msg: "Split offset must not be greater than current text length")
let head: String = getWholeText().substring(0, offset)
let tail: String = getWholeText().substring(offset)
text(head)
let tailNode: TextNode = TextNode(tail, self.getBaseUri())
if (parent() != nil) {
try parent()?.addChildren(siblingIndex+1, tailNode)
}
return tailNode
}
override func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings)throws {
if (out.prettyPrint() &&
((siblingIndex == 0 && (parentNode as? Element) != nil && (parentNode as! Element).tag().formatAsBlock() && !isBlank()) ||
(out.outline() && siblingNodes().count > 0 && !isBlank()) )) {
indent(accum, depth, out)
}
let par: Element? = parent() as? Element
let normaliseWhite = out.prettyPrint() && par != nil && !Element.preserveWhitespace(par!)
Entities.escape(accum, getWholeText(), out, false, normaliseWhite, false)
}
override func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {
}
/**
* Create a new TextNode from HTML encoded (aka escaped) data.
* @param encodedText Text containing encoded HTML (e.g. &amp;lt;)
* @param baseUri Base uri
* @return TextNode containing unencoded data (e.g. &lt;)
*/
public static func createFromEncoded(_ encodedText: String, _ baseUri: String)throws->TextNode {
let text: String = try Entities.unescape(encodedText)
return TextNode(text, baseUri)
}
static public func normaliseWhitespace(_ text: String) -> String {
let _text = StringUtil.normaliseWhitespace(text)
return _text
}
static public func stripLeadingWhitespace(_ text: String) -> String {
return text.replaceFirst(of: "^\\s+", with: "")
//return text.replaceFirst("^\\s+", "")
}
static public func lastCharIsWhitespace(_ sb: StringBuilder) -> Bool {
return sb.toString().last == " "
}
// attribute fiddling. create on first access.
private func ensureAttributes() {
if (attributes == nil) {
attributes = Attributes()
do {
try attributes?.put(TextNode.TEXT_KEY, _text)
} catch {}
}
}
open override func attr(_ attributeKey: String)throws->String {
ensureAttributes()
return try super.attr(attributeKey)
}
open override func getAttributes() -> Attributes {
ensureAttributes()
return super.getAttributes()!
}
open override func attr(_ attributeKey: String, _ attributeValue: String)throws->Node {
ensureAttributes()
return try super.attr(attributeKey, attributeValue)
}
open override func hasAttr(_ attributeKey: String) -> Bool {
ensureAttributes()
return super.hasAttr(attributeKey)
}
open override func removeAttr(_ attributeKey: String)throws->Node {
ensureAttributes()
return try super.removeAttr(attributeKey)
}
open override func absUrl(_ attributeKey: String)throws->String {
ensureAttributes()
return try super.absUrl(attributeKey)
}
public override func copy(with zone: NSZone? = nil) -> Any {
let clone = TextNode(_text, baseUri)
return super.copy(clone: clone)
}
public override func copy(parent: Node?) -> Node {
let clone = TextNode(_text, baseUri)
return super.copy(clone: clone, parent: parent)
}
public override func copy(clone: Node, parent: Node?) -> Node {
return super.copy(clone: clone, parent: parent)
}
}

396
Pods/SwiftSoup/Sources/Token.swift

@ -1,396 +0,0 @@
//
// Token.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 18/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
open class Token {
var type: TokenType = TokenType.Doctype
private init() {
}
func tokenType() -> String {
return String(describing: Swift.type(of: self))
}
/**
* Reset the data represent by this token, for reuse. Prevents the need to create transfer objects for every
* piece of data, which immediately get GCed.
*/
@discardableResult
public func reset() -> Token {
preconditionFailure("This method must be overridden")
}
static func reset(_ sb: StringBuilder) {
sb.clear()
}
open func toString()throws->String {
return String(describing: Swift.type(of: self))
}
final class Doctype: Token {
let name: StringBuilder = StringBuilder()
var pubSysKey: String?
let publicIdentifier: StringBuilder = StringBuilder()
let systemIdentifier: StringBuilder = StringBuilder()
var forceQuirks: Bool = false
override init() {
super.init()
type = TokenType.Doctype
}
@discardableResult
override func reset() -> Token {
Token.reset(name)
pubSysKey = nil
Token.reset(publicIdentifier)
Token.reset(systemIdentifier)
forceQuirks = false
return self
}
func getName() -> String {
return name.toString()
}
func getPubSysKey() -> String? {
return pubSysKey
}
func getPublicIdentifier() -> String {
return publicIdentifier.toString()
}
public func getSystemIdentifier() -> String {
return systemIdentifier.toString()
}
public func isForceQuirks() -> Bool {
return forceQuirks
}
}
class Tag: Token {
public var _tagName: String?
public var _normalName: String? // lc version of tag name, for case insensitive tree build
private var _pendingAttributeName: String? // attribute names are generally caught in one hop, not accumulated
private let _pendingAttributeValue: StringBuilder = StringBuilder() // but values are accumulated, from e.g. & in hrefs
private var _pendingAttributeValueS: String? // try to get attr vals in one shot, vs Builder
private var _hasEmptyAttributeValue: Bool = false // distinguish boolean attribute from empty string value
private var _hasPendingAttributeValue: Bool = false
public var _selfClosing: Bool = false
// start tags get attributes on construction. End tags get attributes on first new attribute (but only for parser convenience, not used).
public var _attributes: Attributes = Attributes()
override init() {
super.init()
}
@discardableResult
override func reset() -> Tag {
_tagName = nil
_normalName = nil
_pendingAttributeName = nil
Token.reset(_pendingAttributeValue)
_pendingAttributeValueS = nil
_hasEmptyAttributeValue = false
_hasPendingAttributeValue = false
_selfClosing = false
_attributes = Attributes()
return self
}
func newAttribute()throws {
// if (_attributes == nil){
// _attributes = Attributes()
// }
if (_pendingAttributeName != nil) {
var attribute: Attribute
if (_hasPendingAttributeValue) {
attribute = try Attribute(key: _pendingAttributeName!, value: _pendingAttributeValue.length > 0 ? _pendingAttributeValue.toString() : _pendingAttributeValueS!)
} else if (_hasEmptyAttributeValue) {
attribute = try Attribute(key: _pendingAttributeName!, value: "")
} else {
attribute = try BooleanAttribute(key: _pendingAttributeName!)
}
_attributes.put(attribute: attribute)
}
_pendingAttributeName = nil
_hasEmptyAttributeValue = false
_hasPendingAttributeValue = false
Token.reset(_pendingAttributeValue)
_pendingAttributeValueS = nil
}
func finaliseTag()throws {
// finalises for emit
if (_pendingAttributeName != nil) {
// todo: check if attribute name exists; if so, drop and error
try newAttribute()
}
}
func name()throws->String { // preserves case, for input into Tag.valueOf (which may drop case)
try Validate.isFalse(val: _tagName == nil || _tagName!.unicodeScalars.count == 0)
return _tagName!
}
func normalName() -> String? { // loses case, used in tree building for working out where in tree it should go
return _normalName
}
@discardableResult
func name(_ name: String) -> Tag {
_tagName = name
_normalName = name.lowercased()
return self
}
func isSelfClosing() -> Bool {
return _selfClosing
}
func getAttributes() -> Attributes {
return _attributes
}
// these appenders are rarely hit in not null state-- caused by null chars.
func appendTagName(_ append: String) {
_tagName = _tagName == nil ? append : _tagName!.appending(append)
_normalName = _tagName?.lowercased()
}
func appendTagName(_ append: UnicodeScalar) {
appendTagName("\(append)")
}
func appendAttributeName(_ append: String) {
_pendingAttributeName = _pendingAttributeName == nil ? append : _pendingAttributeName?.appending(append)
}
func appendAttributeName(_ append: UnicodeScalar) {
appendAttributeName("\(append)")
}
func appendAttributeValue(_ append: String) {
ensureAttributeValue()
if (_pendingAttributeValue.length == 0) {
_pendingAttributeValueS = append
} else {
_pendingAttributeValue.append(append)
}
}
func appendAttributeValue(_ append: UnicodeScalar) {
ensureAttributeValue()
_pendingAttributeValue.appendCodePoint(append)
}
func appendAttributeValue(_ append: [UnicodeScalar]) {
ensureAttributeValue()
_pendingAttributeValue.appendCodePoints(append)
}
func appendAttributeValue(_ appendCodepoints: [Int]) {
ensureAttributeValue()
for codepoint in appendCodepoints {
_pendingAttributeValue.appendCodePoint(UnicodeScalar(codepoint)!)
}
}
func setEmptyAttributeValue() {
_hasEmptyAttributeValue = true
}
private func ensureAttributeValue() {
_hasPendingAttributeValue = true
// if on second hit, we'll need to move to the builder
if (_pendingAttributeValueS != nil) {
_pendingAttributeValue.append(_pendingAttributeValueS!)
_pendingAttributeValueS = nil
}
}
}
final class StartTag: Tag {
override init() {
super.init()
_attributes = Attributes()
type = TokenType.StartTag
}
@discardableResult
override func reset() -> Tag {
super.reset()
_attributes = Attributes()
// todo - would prefer these to be null, but need to check Element assertions
return self
}
@discardableResult
func nameAttr(_ name: String, _ attributes: Attributes) -> StartTag {
self._tagName = name
self._attributes = attributes
_normalName = _tagName?.lowercased()
return self
}
public override func toString()throws->String {
if (_attributes.size() > 0) {
return try "<" + (name()) + " " + (_attributes.toString()) + ">"
} else {
return try "<" + name() + ">"
}
}
}
final class EndTag: Tag {
override init() {
super.init()
type = TokenType.EndTag
}
public override func toString()throws->String {
return "</" + (try name()) + ">"
}
}
final class Comment: Token {
let data: StringBuilder = StringBuilder()
var bogus: Bool = false
@discardableResult
override func reset() -> Token {
Token.reset(data)
bogus = false
return self
}
override init() {
super.init()
type = TokenType.Comment
}
func getData() -> String {
return data.toString()
}
public override func toString()throws->String {
return "<!--" + getData() + "-->"
}
}
final class Char: Token {
public var data: String?
override init() {
super.init()
type = TokenType.Char
}
@discardableResult
override func reset() -> Token {
data = nil
return self
}
@discardableResult
func data(_ data: String) -> Char {
self.data = data
return self
}
func getData() -> String? {
return data
}
public override func toString()throws->String {
try Validate.notNull(obj: data)
return getData()!
}
}
final class EOF: Token {
override init() {
super.init()
type = Token.TokenType.EOF
}
@discardableResult
override func reset() -> Token {
return self
}
}
func isDoctype() -> Bool {
return type == TokenType.Doctype
}
func asDoctype() -> Doctype {
return self as! Doctype
}
func isStartTag() -> Bool {
return type == TokenType.StartTag
}
func asStartTag() -> StartTag {
return self as! StartTag
}
func isEndTag() -> Bool {
return type == TokenType.EndTag
}
func asEndTag() -> EndTag {
return self as! EndTag
}
func isComment() -> Bool {
return type == TokenType.Comment
}
func asComment() -> Comment {
return self as! Comment
}
func isCharacter() -> Bool {
return type == TokenType.Char
}
func asCharacter() -> Char {
return self as! Char
}
func isEOF() -> Bool {
return type == TokenType.EOF
}
public enum TokenType {
case Doctype
case StartTag
case EndTag
case Comment
case Char
case EOF
}
}
extension Token: CustomDebugStringConvertible {
public var debugDescription: String {
do {
return try self.toString()
} catch {
return "Error while get string debug"
}
}
}

428
Pods/SwiftSoup/Sources/TokenQueue.swift

@ -1,428 +0,0 @@
//
// TokenQueue.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 13/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
open class TokenQueue {
private var queue: String
private var pos: Int = 0
private static let empty: Character = Character(UnicodeScalar(0))
private static let ESC: Character = "\\" // escape char for chomp balanced.
/**
Create a new TokenQueue.
@param data string of data to back queue.
*/
public init (_ data: String) {
queue = data
}
/**
* Is the queue empty?
* @return true if no data left in queue.
*/
open func isEmpty() -> Bool {
return remainingLength() == 0
}
private func remainingLength() -> Int {
return queue.count - pos
}
/**
* Retrieves but does not remove the first character from the queue.
* @return First character, or 0 if empty.
*/
open func peek() -> Character {
return isEmpty() ? Character(UnicodeScalar(0)) : queue[pos]
}
/**
Add a character to the start of the queue (will be the next character retrieved).
@param c character to add
*/
open func addFirst(_ c: Character) {
addFirst(String(c))
}
/**
Add a string to the start of the queue.
@param seq string to add.
*/
open func addFirst(_ seq: String) {
// not very performant, but an edge case
queue = seq + queue.substring(pos)
pos = 0
}
/**
* Tests if the next characters on the queue match the sequence. Case insensitive.
* @param seq String to check queue for.
* @return true if the next characters match.
*/
open func matches(_ seq: String) -> Bool {
return queue.regionMatches(true, pos, seq, 0, seq.count)
}
/**
* Case sensitive match test.
* @param seq string to case sensitively check for
* @return true if matched, false if not
*/
open func matchesCS(_ seq: String) -> Bool {
return queue.startsWith(seq, pos)
}
/**
Tests if the next characters match any of the sequences. Case insensitive.
@param seq list of strings to case insensitively check for
@return true of any matched, false if none did
*/
open func matchesAny(_ seq: [String]) -> Bool {
for s in seq {
if (matches(s)) {
return true
}
}
return false
}
open func matchesAny(_ seq: String...) -> Bool {
return matchesAny(seq)
}
open func matchesAny(_ seq: Character...) -> Bool {
if (isEmpty()) {
return false
}
for c in seq {
if (queue[pos] as Character == c) {
return true
}
}
return false
}
open func matchesStartTag() -> Bool {
// micro opt for matching "<x"
return (remainingLength() >= 2 && queue[pos] as Character == "<" && Character.isLetter(queue.charAt(pos+1)))
}
/**
* Tests if the queue matches the sequence (as with match), and if they do, removes the matched string from the
* queue.
* @param seq String to search for, and if found, remove from queue.
* @return true if found and removed, false if not found.
*/
@discardableResult
open func matchChomp(_ seq: String) -> Bool {
if (matches(seq)) {
pos += seq.count
return true
} else {
return false
}
}
/**
Tests if queue starts with a whitespace character.
@return if starts with whitespace
*/
open func matchesWhitespace() -> Bool {
return !isEmpty() && StringUtil.isWhitespace(queue.charAt(pos))
}
/**
Test if the queue matches a word character (letter or digit).
@return if matches a word character
*/
open func matchesWord() -> Bool {
return !isEmpty() && (Character.isLetterOrDigit(queue.charAt(pos)))
}
/**
* Drops the next character off the queue.
*/
open func advance() {
if (!isEmpty()) {pos+=1}
}
/**
* Consume one character off queue.
* @return first character on queue.
*/
open func consume() -> Character {
let i = pos
pos+=1
return queue.charAt(i)
}
/**
* Consumes the supplied sequence of the queue. If the queue does not start with the supplied sequence, will
* throw an illegal state exception -- but you should be running match() against that condition.
<p>
Case insensitive.
* @param seq sequence to remove from head of queue.
*/
open func consume(_ seq: String)throws {
if (!matches(seq)) {
//throw new IllegalStateException("Queue did not match expected sequence")
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Queue did not match expected sequence")
}
let len = seq.count
if (len > remainingLength()) {
//throw new IllegalStateException("Queue not long enough to consume sequence")
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Queue not long enough to consume sequence")
}
pos += len
}
/**
* Pulls a string off the queue, up to but exclusive of the match sequence, or to the queue running out.
* @param seq String to end on (and not include in return, but leave on queue). <b>Case sensitive.</b>
* @return The matched data consumed from queue.
*/
@discardableResult
open func consumeTo(_ seq: String) -> String {
let offset = queue.indexOf(seq, pos)
if (offset != -1) {
let consumed = queue.substring(pos, offset-pos)
pos += consumed.count
return consumed
} else {
//return remainder()
}
return ""
}
open func consumeToIgnoreCase(_ seq: String) -> String {
let start = pos
let first = seq.substring(0, 1)
let canScan = first.lowercased() == first.uppercased() // if first is not cased, use index of
while (!isEmpty()) {
if (matches(seq)) {
break
}
if (canScan) {
let skip = queue.indexOf(first, pos) - pos
if (skip == 0) { // this char is the skip char, but not match, so force advance of pos
pos+=1
} else if (skip < 0) { // no chance of finding, grab to end
pos = queue.count
} else {
pos += skip
}
} else {
pos+=1
}
}
return queue.substring(start, pos-start)
}
/**
Consumes to the first sequence provided, or to the end of the queue. Leaves the terminator on the queue.
@param seq any number of terminators to consume to. <b>Case insensitive.</b>
@return consumed string
*/
// todo: method name. not good that consumeTo cares for case, and consume to any doesn't. And the only use for this
// is is a case sensitive time...
open func consumeToAny(_ seq: String...) -> String {
return consumeToAny(seq)
}
open func consumeToAny(_ seq: [String]) -> String {
let start = pos
while (!isEmpty() && !matchesAny(seq)) {
pos+=1
}
return queue.substring(start, pos-start)
}
/**
* Pulls a string off the queue (like consumeTo), and then pulls off the matched string (but does not return it).
* <p>
* If the queue runs out of characters before finding the seq, will return as much as it can (and queue will go
* isEmpty() == true).
* @param seq String to match up to, and not include in return, and to pull off queue. <b>Case sensitive.</b>
* @return Data matched from queue.
*/
open func chompTo(_ seq: String) -> String {
let data = consumeTo(seq)
matchChomp(seq)
return data
}
open func chompToIgnoreCase(_ seq: String) -> String {
let data = consumeToIgnoreCase(seq) // case insensitive scan
matchChomp(seq)
return data
}
/**
* Pulls a balanced string off the queue. E.g. if queue is "(one (two) three) four", (,) will return "one (two) three",
* and leave " four" on the queue. Unbalanced openers and closers can quoted (with ' or ") or escaped (with \). Those escapes will be left
* in the returned string, which is suitable for regexes (where we need to preserve the escape), but unsuitable for
* contains text strings; use unescape for that.
* @param open opener
* @param close closer
* @return data matched from the queue
*/
open func chompBalanced(_ open: Character, _ close: Character) -> String {
var start = -1
var end = -1
var depth = 0
var last: Character = TokenQueue.empty
var inQuote = false
repeat {
if (isEmpty()) {break}
let c = consume()
if (last == TokenQueue.empty || last != TokenQueue.ESC) {
if ((c=="'" || c=="\"") && c != open) {
inQuote = !inQuote
}
if (inQuote) {
continue
}
if (c==open) {
depth+=1
if (start == -1) {
start = pos
}
} else if (c==close) {
depth-=1
}
}
if (depth > 0 && last != TokenQueue.empty) {
end = pos // don't include the outer match pair in the return
}
last = c
} while (depth > 0)
return (end >= 0) ? queue.substring(start, end-start) : ""
}
/**
* Unescaped a \ escaped string.
* @param in backslash escaped string
* @return unescaped string
*/
public static func unescape(_ input: String) -> String {
let out = StringBuilder()
var last = empty
for c in input {
if (c == ESC) {
if (last != empty && last == TokenQueue.ESC) {
out.append(c)
}
} else {
out.append(c)
}
last = c
}
return out.toString()
}
/**
* Pulls the next run of whitespace characters of the queue.
* @return Whether consuming whitespace or not
*/
@discardableResult
open func consumeWhitespace() -> Bool {
var seen = false
while (matchesWhitespace()) {
pos+=1
seen = true
}
return seen
}
/**
* Retrieves the next run of word type (letter or digit) off the queue.
* @return String of word characters from queue, or empty string if none.
*/
@discardableResult
open func consumeWord() -> String {
let start = pos
while (matchesWord()) {
pos+=1
}
return queue.substring(start, pos-start)
}
/**
* Consume an tag name off the queue (word or :, _, -)
*
* @return tag name
*/
open func consumeTagName() -> String {
let start = pos
while (!isEmpty() && (matchesWord() || matchesAny(":", "_", "-"))) {
pos+=1
}
return queue.substring(start, pos-start)
}
/**
* Consume a CSS element selector (tag name, but | instead of : for namespaces (or *| for wildcard namespace), to not conflict with :pseudo selects).
*
* @return tag name
*/
open func consumeElementSelector() -> String {
let start = pos
while (!isEmpty() && (matchesWord() || matchesAny("*|", "|", "_", "-"))) {
pos+=1
}
return queue.substring(start, pos-start)
}
/**
Consume a CSS identifier (ID or class) off the queue (letter, digit, -, _)
http://www.w3.org/TR/CSS2/syndata.html#value-def-identifier
@return identifier
*/
open func consumeCssIdentifier() -> String {
let start = pos
while (!isEmpty() && (matchesWord() || matchesAny("-", "_"))) {
pos+=1
}
return queue.substring(start, pos-start)
}
/**
Consume an attribute key off the queue (letter, digit, -, _, :")
@return attribute key
*/
open func consumeAttributeKey() -> String {
let start = pos
while (!isEmpty() && (matchesWord() || matchesAny("-", "_", ":"))) {
pos+=1
}
return queue.substring(start, pos-start)
}
/**
Consume and return whatever is left on the queue.
@return remained of queue.
*/
open func remainder() -> String {
let remainder = queue.substring(pos, queue.count-pos)
pos = queue.count
return remainder
}
open func toString() -> String {
return queue.substring(pos)
}
}

311
Pods/SwiftSoup/Sources/Tokeniser.swift

@ -1,311 +0,0 @@
//
// Tokeniser.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 19/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
final class Tokeniser {
static let replacementChar: UnicodeScalar = "\u{FFFD}" // replaces null character
private static let notCharRefCharsSorted: [UnicodeScalar] = [UnicodeScalar.BackslashT, "\n", "\r", UnicodeScalar.BackslashF, " ", "<", UnicodeScalar.Ampersand].sorted()
private let reader: CharacterReader // html input
private let errors: ParseErrorList? // errors found while tokenising
private var state: TokeniserState = TokeniserState.Data // current tokenisation state
private var emitPending: Token? // the token we are about to emit on next read
private var isEmitPending: Bool = false
private var charsString: String? // characters pending an emit. Will fall to charsBuilder if more than one
private let charsBuilder: StringBuilder = StringBuilder(1024) // buffers characters to output as one token, if more than one emit per read
let dataBuffer: StringBuilder = StringBuilder(1024) // buffers data looking for </script>
var tagPending: Token.Tag = Token.Tag() // tag we are building up
let startPending: Token.StartTag = Token.StartTag()
let endPending: Token.EndTag = Token.EndTag()
let charPending: Token.Char = Token.Char()
let doctypePending: Token.Doctype = Token.Doctype() // doctype building up
let commentPending: Token.Comment = Token.Comment() // comment building up
private var lastStartTag: String? // the last start tag emitted, to test appropriate end tag
private var selfClosingFlagAcknowledged: Bool = true
init(_ reader: CharacterReader, _ errors: ParseErrorList?) {
self.reader = reader
self.errors = errors
}
func read()throws->Token {
if (!selfClosingFlagAcknowledged) {
error("Self closing flag not acknowledged")
selfClosingFlagAcknowledged = true
}
while (!isEmitPending) {
try state.read(self, reader)
}
// if emit is pending, a non-character token was found: return any chars in buffer, and leave token for next read:
if (charsBuilder.length > 0) {
let str: String = charsBuilder.toString()
charsBuilder.clear()
charsString = nil
return charPending.data(str)
} else if (charsString != nil) {
let token: Token = charPending.data(charsString!)
charsString = nil
return token
} else {
isEmitPending = false
return emitPending!
}
}
func emit(_ token: Token)throws {
try Validate.isFalse(val: isEmitPending, msg: "There is an unread token pending!")
emitPending = token
isEmitPending = true
if (token.type == Token.TokenType.StartTag) {
let startTag: Token.StartTag = token as! Token.StartTag
lastStartTag = startTag._tagName!
if (startTag._selfClosing) {
selfClosingFlagAcknowledged = false
}
} else if (token.type == Token.TokenType.EndTag) {
let endTag: Token.EndTag = token as! Token.EndTag
if (endTag._attributes.size() != 0) {
error("Attributes incorrectly present on end tag")
}
}
}
func emit(_ str: String ) {
// buffer strings up until last string token found, to emit only one token for a run of character refs etc.
// does not set isEmitPending; read checks that
if (charsString == nil) {
charsString = str
} else {
if (charsBuilder.length == 0) { // switching to string builder as more than one emit before read
charsBuilder.append(charsString!)
}
charsBuilder.append(str)
}
}
func emit(_ chars: [UnicodeScalar]) {
emit(String(chars.map {Character($0)}))
}
// func emit(_ codepoints: [Int]) {
// emit(String(codepoints, 0, codepoints.length));
// }
func emit(_ c: UnicodeScalar) {
emit(String(c))
}
func getState() -> TokeniserState {
return state
}
func transition(_ state: TokeniserState) {
self.state = state
}
func advanceTransition(_ state: TokeniserState) {
reader.advance()
self.state = state
}
func acknowledgeSelfClosingFlag() {
selfClosingFlagAcknowledged = true
}
private var codepointHolder: [UnicodeScalar] = [UnicodeScalar(0)!] // holder to not have to keep creating arrays
private var multipointHolder: [UnicodeScalar] = [UnicodeScalar(0)!, UnicodeScalar(0)!]
func consumeCharacterReference(_ additionalAllowedCharacter: UnicodeScalar?, _ inAttribute: Bool)throws->[UnicodeScalar]? {
if (reader.isEmpty()) {
return nil
}
if (additionalAllowedCharacter != nil && additionalAllowedCharacter == reader.current()) {
return nil
}
if (reader.matchesAnySorted(Tokeniser.notCharRefCharsSorted)) {
return nil
}
var codeRef: [UnicodeScalar] = codepointHolder
reader.markPos()
if (reader.matchConsume("#")) { // numbered
let isHexMode: Bool = reader.matchConsumeIgnoreCase("X")
let numRef: String = isHexMode ? reader.consumeHexSequence() : reader.consumeDigitSequence()
if (numRef.unicodeScalars.count == 0) { // didn't match anything
characterReferenceError("numeric reference with no numerals")
reader.rewindToMark()
return nil
}
if (!reader.matchConsume(";")) {
characterReferenceError("missing semicolon") // missing semi
}
var charval: Int = -1
let base: Int = isHexMode ? 16 : 10
if let num = Int(numRef, radix: base) {
charval = num
}
if (charval == -1 || (charval >= 0xD800 && charval <= 0xDFFF) || charval > 0x10FFFF) {
characterReferenceError("character outside of valid range")
codeRef[0] = Tokeniser.replacementChar
return codeRef
} else {
// todo: implement number replacement table
// todo: check for extra illegal unicode points as parse errors
codeRef[0] = UnicodeScalar(charval)!
return codeRef
}
} else { // named
// get as many letters as possible, and look for matching entities.
let nameRef: String = reader.consumeLetterThenDigitSequence()
let looksLegit: Bool = reader.matches(";")
// found if a base named entity without a ;, or an extended entity with the ;.
let found: Bool = (Entities.isBaseNamedEntity(nameRef) || (Entities.isNamedEntity(nameRef) && looksLegit))
if (!found) {
reader.rewindToMark()
if (looksLegit) { // named with semicolon
characterReferenceError("invalid named referenece '\(nameRef)'")
}
return nil
}
if (inAttribute && (reader.matchesLetter() || reader.matchesDigit() || reader.matchesAny("=", "-", "_"))) {
// don't want that to match
reader.rewindToMark()
return nil
}
if (!reader.matchConsume(";")) {
characterReferenceError("missing semicolon") // missing semi
}
let numChars: Int = Entities.codepointsForName(nameRef, codepoints: &multipointHolder)
if (numChars == 1) {
codeRef[0] = multipointHolder[0]
return codeRef
} else if (numChars == 2) {
return multipointHolder
} else {
try Validate.fail(msg: "Unexpected characters returned for \(nameRef) num: \(numChars)")
return multipointHolder
}
}
}
@discardableResult
func createTagPending(_ start: Bool)->Token.Tag {
tagPending = start ? startPending.reset() : endPending.reset()
return tagPending
}
func emitTagPending()throws {
try tagPending.finaliseTag()
try emit(tagPending)
}
func createCommentPending() {
commentPending.reset()
}
func emitCommentPending()throws {
try emit(commentPending)
}
func createDoctypePending() {
doctypePending.reset()
}
func emitDoctypePending()throws {
try emit(doctypePending)
}
func createTempBuffer() {
Token.reset(dataBuffer)
}
func isAppropriateEndTagToken()throws->Bool {
if(lastStartTag != nil) {
let s = try tagPending.name()
return s.equalsIgnoreCase(string: lastStartTag!)
}
return false
}
func appropriateEndTagName() -> String? {
if (lastStartTag == nil) {
return nil
}
return lastStartTag
}
func error(_ state: TokeniserState) {
if (errors != nil && errors!.canAddError()) {
errors?.add(ParseError(reader.getPos(), "Unexpected character '\(String(reader.current()))' in input state [\(state.description)]"))
}
}
func eofError(_ state: TokeniserState) {
if (errors != nil && errors!.canAddError()) {
errors?.add(ParseError(reader.getPos(), "Unexpectedly reached end of file (EOF) in input state [\(state.description)]"))
}
}
private func characterReferenceError(_ message: String) {
if (errors != nil && errors!.canAddError()) {
errors?.add(ParseError(reader.getPos(), "Invalid character reference: \(message)"))
}
}
private func error(_ errorMsg: String) {
if (errors != nil && errors!.canAddError()) {
errors?.add(ParseError(reader.getPos(), errorMsg))
}
}
func currentNodeInHtmlNS() -> Bool {
// todo: implement namespaces correctly
return true
// Element currentNode = currentNode()
// return currentNode != null && currentNode.namespace().equals("HTML")
}
/**
* Utility method to consume reader and unescape entities found within.
* @param inAttribute
* @return unescaped string from reader
*/
func unescapeEntities(_ inAttribute: Bool)throws->String {
let builder: StringBuilder = StringBuilder()
while (!reader.isEmpty()) {
builder.append(reader.consumeTo(UnicodeScalar.Ampersand))
if (reader.matches(UnicodeScalar.Ampersand)) {
reader.consume()
if let c = try consumeCharacterReference(nil, inAttribute) {
if (c.count==0) {
builder.append(UnicodeScalar.Ampersand)
} else {
builder.appendCodePoint(c[0])
if (c.count == 2) {
builder.appendCodePoint(c[1])
}
}
} else {
builder.append(UnicodeScalar.Ampersand)
}
}
}
return builder.toString()
}
}

1644
Pods/SwiftSoup/Sources/TokeniserState.swift

File diff suppressed because it is too large

98
Pods/SwiftSoup/Sources/TreeBuilder.swift

@ -1,98 +0,0 @@
//
// TreeBuilder.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 24/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
public class TreeBuilder {
public var reader: CharacterReader
var tokeniser: Tokeniser
public var doc: Document // current doc we are building into
public var stack: Array<Element> // the stack of open elements
public var baseUri: String // current base uri, for creating new elements
public var currentToken: Token? // currentToken is used only for error tracking.
public var errors: ParseErrorList // null when not tracking errors
public var settings: ParseSettings
private let start: Token.StartTag = Token.StartTag() // start tag to process
private let end: Token.EndTag = Token.EndTag()
public func defaultSettings() -> ParseSettings {preconditionFailure("This method must be overridden")}
public init() {
doc = Document("")
reader = CharacterReader("")
tokeniser = Tokeniser(reader, nil)
stack = Array<Element>()
baseUri = ""
errors = ParseErrorList(0, 0)
settings = ParseSettings(false, false)
}
public func initialiseParse(_ input: String, _ baseUri: String, _ errors: ParseErrorList, _ settings: ParseSettings) {
doc = Document(baseUri)
self.settings = settings
reader = CharacterReader(input)
self.errors = errors
tokeniser = Tokeniser(reader, errors)
stack = Array<Element>()
self.baseUri = baseUri
}
func parse(_ input: String, _ baseUri: String, _ errors: ParseErrorList, _ settings: ParseSettings)throws->Document {
initialiseParse(input, baseUri, errors, settings)
try runParser()
return doc
}
public func runParser()throws {
while (true) {
let token: Token = try tokeniser.read()
try process(token)
token.reset()
if (token.type == Token.TokenType.EOF) {
break
}
}
}
@discardableResult
public func process(_ token: Token)throws->Bool {preconditionFailure("This method must be overridden")}
@discardableResult
public func processStartTag(_ name: String)throws->Bool {
if (currentToken === start) { // don't recycle an in-use token
return try process(Token.StartTag().name(name))
}
return try process(start.reset().name(name))
}
@discardableResult
public func processStartTag(_ name: String, _ attrs: Attributes)throws->Bool {
if (currentToken === start) { // don't recycle an in-use token
return try process(Token.StartTag().nameAttr(name, attrs))
}
start.reset()
start.nameAttr(name, attrs)
return try process(start)
}
@discardableResult
public func processEndTag(_ name: String)throws->Bool {
if (currentToken === end) { // don't recycle an in-use token
return try process(Token.EndTag().name(name))
}
return try process(end.reset().name(name))
}
public func currentElement() -> Element? {
let size: Int = stack.count
return size > 0 ? stack[size-1] : nil
}
}

67
Pods/SwiftSoup/Sources/UnicodeScalar.swift

@ -1,67 +0,0 @@
//
// UnicodeScalar.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 14/11/16.
// Copyright © 2016 Nabil Chatbi. All rights reserved.
//
import Foundation
private let uppercaseSet = CharacterSet.uppercaseLetters
private let lowercaseSet = CharacterSet.lowercaseLetters
private let alphaSet = CharacterSet.letters
private let alphaNumericSet = CharacterSet.alphanumerics
private let symbolSet = CharacterSet.symbols
private let digitSet = CharacterSet.decimalDigits
extension UnicodeScalar {
public static let Ampersand: UnicodeScalar = "&"
public static let LessThan: UnicodeScalar = "<"
public static let GreaterThan: UnicodeScalar = ">"
public static let Space: UnicodeScalar = " "
public static let BackslashF: UnicodeScalar = UnicodeScalar(12)
public static let BackslashT: UnicodeScalar = "\t"
public static let BackslashN: UnicodeScalar = "\n"
public static let BackslashR: UnicodeScalar = "\r"
public static let Slash: UnicodeScalar = "/"
public static let FormFeed: UnicodeScalar = "\u{000B}"// Form Feed
public static let VerticalTab: UnicodeScalar = "\u{000C}"// vertical tab
func isMemberOfCharacterSet(_ set: CharacterSet) -> Bool {
return set.contains(self)
}
/// True for any space character, and the control characters \t, \n, \r, \f, \v.
var isWhitespace: Bool {
switch self {
case UnicodeScalar.Space, UnicodeScalar.BackslashT, UnicodeScalar.BackslashN, UnicodeScalar.BackslashR, UnicodeScalar.BackslashF: return true
case UnicodeScalar.FormFeed, UnicodeScalar.VerticalTab: return true // Form Feed, vertical tab
default: return false
}
}
/// `true` if `self` normalized contains a single code unit that is in the categories of Uppercase and Titlecase Letters.
var isUppercase: Bool {
return isMemberOfCharacterSet(uppercaseSet)
}
/// `true` if `self` normalized contains a single code unit that is in the category of Lowercase Letters.
var isLowercase: Bool {
return isMemberOfCharacterSet(lowercaseSet)
}
var uppercase: UnicodeScalar {
let str = String(self).uppercased()
return str.unicodeScalar(0)
}
}

133
Pods/SwiftSoup/Sources/Validate.swift

@ -1,133 +0,0 @@
//
// Validate.swift
// SwifSoup
//
// Created by Nabil Chatbi on 02/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
struct Validate {
/**
* Validates that the object is not null
* @param obj object to test
*/
public static func notNull(obj: Any?) throws {
if (obj == nil) {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Object must not be null")
}
}
/**
* Validates that the object is not null
* @param obj object to test
* @param msg message to output if validation fails
*/
public static func notNull(obj: AnyObject?, msg: String) throws {
if (obj == nil) {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: msg)
}
}
/**
* Validates that the value is true
* @param val object to test
*/
public static func isTrue(val: Bool) throws {
if (!val) {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Must be true")
}
}
/**
* Validates that the value is true
* @param val object to test
* @param msg message to output if validation fails
*/
public static func isTrue(val: Bool, msg: String) throws {
if (!val) {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: msg)
}
}
/**
* Validates that the value is false
* @param val object to test
*/
public static func isFalse(val: Bool) throws {
if (val) {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Must be false")
}
}
/**
* Validates that the value is false
* @param val object to test
* @param msg message to output if validation fails
*/
public static func isFalse(val: Bool, msg: String) throws {
if (val) {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: msg)
}
}
/**
* Validates that the array contains no null elements
* @param objects the array to test
*/
public static func noNullElements(objects: [AnyObject?]) throws {
try noNullElements(objects: objects, msg: "Array must not contain any null objects")
}
/**
* Validates that the array contains no null elements
* @param objects the array to test
* @param msg message to output if validation fails
*/
public static func noNullElements(objects: [AnyObject?], msg: String) throws {
for obj in objects {
if (obj == nil) {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: msg)
}
}
}
/**
* Validates that the string is not empty
* @param string the string to test
*/
public static func notEmpty(string: String?) throws {
if (string == nil || string?.count == 0) {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "String must not be empty")
}
}
/**
* Validates that the string is not empty
* @param string the string to test
* @param msg message to output if validation fails
*/
public static func notEmpty(string: String?, msg: String ) throws {
if (string == nil || string?.count == 0) {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: msg)
}
}
/**
Cause a failure.
@param msg message to output.
*/
public static func fail(msg: String) throws {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: msg)
}
/**
Helper
*/
public static func exception(msg: String) throws {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: msg)
}
}

650
Pods/SwiftSoup/Sources/Whitelist.swift

@ -1,650 +0,0 @@
//
// Whitelist.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 14/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
/*
Thank you to Ryan Grove (wonko.com) for the Ruby HTML cleaner http://github.com/rgrove/sanitize/, which inspired
this whitelist configuration, and the initial defaults.
*/
/**
Whitelists define what HTML (elements and attributes) to allow through the cleaner. Everything else is removed.
<p>
Start with one of the defaults:
</p>
<ul>
<li>{@link #none}
<li>{@link #simpleText}
<li>{@link #basic}
<li>{@link #basicWithImages}
<li>{@link #relaxed}
</ul>
<p>
If you need to allow more through (please be careful!), tweak a base whitelist with:
</p>
<ul>
<li>{@link #addTags}
<li>{@link #addAttributes}
<li>{@link #addEnforcedAttribute}
<li>{@link #addProtocols}
</ul>
<p>
You can remove any setting from an existing whitelist with:
</p>
<ul>
<li>{@link #removeTags}
<li>{@link #removeAttributes}
<li>{@link #removeEnforcedAttribute}
<li>{@link #removeProtocols}
</ul>
<p>
The cleaner and these whitelists assume that you want to clean a <code>body</code> fragment of HTML (to add user
supplied HTML into a templated page), and not to clean a full HTML document. If the latter is the case, either wrap the
document HTML around the cleaned body HTML, or create a whitelist that allows <code>html</code> and <code>head</code>
elements as appropriate.
</p>
<p>
If you are going to extend a whitelist, please be very careful. Make sure you understand what attributes may lead to
XSS attack vectors. URL attributes are particularly vulnerable and require careful validation. See
http://ha.ckers.org/xss.html for some XSS attack examples.
</p>
*/
import Foundation
public class Whitelist {
private var tagNames: Set<TagName> // tags allowed, lower case. e.g. [p, br, span]
private var attributes: Dictionary<TagName, Set<AttributeKey>> // tag -> attribute[]. allowed attributes [href] for a tag.
private var enforcedAttributes: Dictionary<TagName, Dictionary<AttributeKey, AttributeValue>> // always set these attribute values
private var protocols: Dictionary<TagName, Dictionary<AttributeKey, Set<Protocol>>> // allowed URL protocols for attributes
private var preserveRelativeLinks: Bool // option to preserve relative links
/**
This whitelist allows only text nodes: all HTML will be stripped.
@return whitelist
*/
public static func none() -> Whitelist {
return Whitelist()
}
/**
This whitelist allows only simple text formatting: <code>b, em, i, strong, u</code>. All other HTML (tags and
attributes) will be removed.
@return whitelist
*/
public static func simpleText()throws ->Whitelist {
return try Whitelist().addTags("b", "em", "i", "strong", "u")
}
/**
<p>
This whitelist allows a fuller range of text nodes: <code>a, b, blockquote, br, cite, code, dd, dl, dt, em, i, li,
ol, p, pre, q, small, span, strike, strong, sub, sup, u, ul</code>, and appropriate attributes.
</p>
<p>
Links (<code>a</code> elements) can point to <code>http, https, ftp, mailto</code>, and have an enforced
<code>rel=nofollow</code> attribute.
</p>
<p>
Does not allow images.
</p>
@return whitelist
*/
public static func basic()throws->Whitelist {
return try Whitelist()
.addTags(
"a", "b", "blockquote", "br", "cite", "code", "dd", "dl", "dt", "em",
"i", "li", "ol", "p", "pre", "q", "small", "span", "strike", "strong", "sub",
"sup", "u", "ul")
.addAttributes("a", "href")
.addAttributes("blockquote", "cite")
.addAttributes("q", "cite")
.addProtocols("a", "href", "ftp", "http", "https", "mailto")
.addProtocols("blockquote", "cite", "http", "https")
.addProtocols("cite", "cite", "http", "https")
.addEnforcedAttribute("a", "rel", "nofollow")
}
/**
This whitelist allows the same text tags as {@link #basic}, and also allows <code>img</code> tags, with appropriate
attributes, with <code>src</code> pointing to <code>http</code> or <code>https</code>.
@return whitelist
*/
public static func basicWithImages()throws->Whitelist {
return try basic()
.addTags("img")
.addAttributes("img", "align", "alt", "height", "src", "title", "width")
.addProtocols("img", "src", "http", "https")
}
/**
This whitelist allows a full range of text and structural body HTML: <code>a, b, blockquote, br, caption, cite,
code, col, colgroup, dd, div, dl, dt, em, h1, h2, h3, h4, h5, h6, i, img, li, ol, p, pre, q, small, span, strike, strong, sub,
sup, table, tbody, td, tfoot, th, thead, tr, u, ul</code>
<p>
Links do not have an enforced <code>rel=nofollow</code> attribute, but you can add that if desired.
</p>
@return whitelist
*/
public static func relaxed()throws->Whitelist {
return try Whitelist()
.addTags(
"a", "b", "blockquote", "br", "caption", "cite", "code", "col",
"colgroup", "dd", "div", "dl", "dt", "em", "h1", "h2", "h3", "h4", "h5", "h6",
"i", "img", "li", "ol", "p", "pre", "q", "small", "span", "strike", "strong",
"sub", "sup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "u",
"ul")
.addAttributes("a", "href", "title")
.addAttributes("blockquote", "cite")
.addAttributes("col", "span", "width")
.addAttributes("colgroup", "span", "width")
.addAttributes("img", "align", "alt", "height", "src", "title", "width")
.addAttributes("ol", "start", "type")
.addAttributes("q", "cite")
.addAttributes("table", "summary", "width")
.addAttributes("td", "abbr", "axis", "colspan", "rowspan", "width")
.addAttributes(
"th", "abbr", "axis", "colspan", "rowspan", "scope",
"width")
.addAttributes("ul", "type")
.addProtocols("a", "href", "ftp", "http", "https", "mailto")
.addProtocols("blockquote", "cite", "http", "https")
.addProtocols("cite", "cite", "http", "https")
.addProtocols("img", "src", "http", "https")
.addProtocols("q", "cite", "http", "https")
}
/**
Create a new, empty whitelist. Generally it will be better to start with a default prepared whitelist instead.
@see #basic()
@see #basicWithImages()
@see #simpleText()
@see #relaxed()
*/
init() {
tagNames = Set<TagName>()
attributes = Dictionary<TagName, Set<AttributeKey>>()
enforcedAttributes = Dictionary<TagName, Dictionary<AttributeKey, AttributeValue>>()
protocols = Dictionary<TagName, Dictionary<AttributeKey, Set<Protocol>>>()
preserveRelativeLinks = false
}
/**
Add a list of allowed elements to a whitelist. (If a tag is not allowed, it will be removed from the HTML.)
@param tags tag names to allow
@return this (for chaining)
*/
@discardableResult
open func addTags(_ tags: String...)throws ->Whitelist {
for tagName in tags {
try Validate.notEmpty(string: tagName)
tagNames.insert(TagName.valueOf(tagName))
}
return self
}
/**
Remove a list of allowed elements from a whitelist. (If a tag is not allowed, it will be removed from the HTML.)
@param tags tag names to disallow
@return this (for chaining)
*/
@discardableResult
open func removeTags(_ tags: String...)throws ->Whitelist {
try Validate.notNull(obj: tags)
for tag in tags {
try Validate.notEmpty(string: tag)
let tagName: TagName = TagName.valueOf(tag)
if(tagNames.contains(tagName)) { // Only look in sub-maps if tag was allowed
tagNames.remove(tagName)
attributes.removeValue(forKey: tagName)
enforcedAttributes.removeValue(forKey: tagName)
protocols.removeValue(forKey: tagName)
}
}
return self
}
/**
Add a list of allowed attributes to a tag. (If an attribute is not allowed on an element, it will be removed.)
<p>
E.g.: <code>addAttributes("a", "href", "class")</code> allows <code>href</code> and <code>class</code> attributes
on <code>a</code> tags.
</p>
<p>
To make an attribute valid for <b>all tags</b>, use the pseudo tag <code>:all</code>, e.g.
<code>addAttributes(":all", "class")</code>.
</p>
@param tag The tag the attributes are for. The tag will be added to the allowed tag list if necessary.
@param keys List of valid attributes for the tag
@return this (for chaining)
*/
@discardableResult
open func addAttributes(_ tag: String, _ keys: String...)throws->Whitelist {
try Validate.notEmpty(string: tag)
try Validate.isTrue(val: keys.count > 0, msg: "No attributes supplied.")
let tagName = TagName.valueOf(tag)
if (!tagNames.contains(tagName)) {
tagNames.insert(tagName)
}
var attributeSet = Set<AttributeKey>()
for key in keys {
try Validate.notEmpty(string: key)
attributeSet.insert(AttributeKey.valueOf(key))
}
if var currentSet = attributes[tagName] {
for at in attributeSet {
currentSet.insert(at)
}
attributes[tagName] = currentSet
} else {
attributes[tagName] = attributeSet
}
return self
}
/**
Remove a list of allowed attributes from a tag. (If an attribute is not allowed on an element, it will be removed.)
<p>
E.g.: <code>removeAttributes("a", "href", "class")</code> disallows <code>href</code> and <code>class</code>
attributes on <code>a</code> tags.
</p>
<p>
To make an attribute invalid for <b>all tags</b>, use the pseudo tag <code>:all</code>, e.g.
<code>removeAttributes(":all", "class")</code>.
</p>
@param tag The tag the attributes are for.
@param keys List of invalid attributes for the tag
@return this (for chaining)
*/
@discardableResult
open func removeAttributes(_ tag: String, _ keys: String...)throws->Whitelist {
try Validate.notEmpty(string: tag)
try Validate.isTrue(val: keys.count > 0, msg: "No attributes supplied.")
let tagName: TagName = TagName.valueOf(tag)
var attributeSet = Set<AttributeKey>()
for key in keys {
try Validate.notEmpty(string: key)
attributeSet.insert(AttributeKey.valueOf(key))
}
if(tagNames.contains(tagName)) { // Only look in sub-maps if tag was allowed
if var currentSet = attributes[tagName] {
for l in attributeSet {
currentSet.remove(l)
}
attributes[tagName] = currentSet
if(currentSet.isEmpty) { // Remove tag from attribute map if no attributes are allowed for tag
attributes.removeValue(forKey: tagName)
}
}
}
if(tag == ":all") { // Attribute needs to be removed from all individually set tags
for name in attributes.keys {
var currentSet: Set<AttributeKey> = attributes[name]!
for l in attributeSet {
currentSet.remove(l)
}
attributes[name] = currentSet
if(currentSet.isEmpty) { // Remove tag from attribute map if no attributes are allowed for tag
attributes.removeValue(forKey: name)
}
}
}
return self
}
/**
Add an enforced attribute to a tag. An enforced attribute will always be added to the element. If the element
already has the attribute set, it will be overridden.
<p>
E.g.: <code>addEnforcedAttribute("a", "rel", "nofollow")</code> will make all <code>a</code> tags output as
<code>&lt;a href="..." rel="nofollow"&gt;</code>
</p>
@param tag The tag the enforced attribute is for. The tag will be added to the allowed tag list if necessary.
@param key The attribute key
@param value The enforced attribute value
@return this (for chaining)
*/
@discardableResult
open func addEnforcedAttribute(_ tag: String, _ key: String, _ value: String)throws->Whitelist {
try Validate.notEmpty(string: tag)
try Validate.notEmpty(string: key)
try Validate.notEmpty(string: value)
let tagName: TagName = TagName.valueOf(tag)
if (!tagNames.contains(tagName)) {
tagNames.insert(tagName)
}
let attrKey: AttributeKey = AttributeKey.valueOf(key)
let attrVal: AttributeValue = AttributeValue.valueOf(value)
if (enforcedAttributes[tagName] != nil) {
enforcedAttributes[tagName]?[attrKey] = attrVal
} else {
var attrMap: Dictionary<AttributeKey, AttributeValue> = Dictionary<AttributeKey, AttributeValue>()
attrMap[attrKey] = attrVal
enforcedAttributes[tagName] = attrMap
}
return self
}
/**
Remove a previously configured enforced attribute from a tag.
@param tag The tag the enforced attribute is for.
@param key The attribute key
@return this (for chaining)
*/
@discardableResult
open func removeEnforcedAttribute(_ tag: String, _ key: String)throws->Whitelist {
try Validate.notEmpty(string: tag)
try Validate.notEmpty(string: key)
let tagName: TagName = TagName.valueOf(tag)
if(tagNames.contains(tagName) && (enforcedAttributes[tagName] != nil)) {
let attrKey: AttributeKey = AttributeKey.valueOf(key)
var attrMap: Dictionary<AttributeKey, AttributeValue> = enforcedAttributes[tagName]!
attrMap.removeValue(forKey: attrKey)
enforcedAttributes[tagName] = attrMap
if(attrMap.isEmpty) { // Remove tag from enforced attribute map if no enforced attributes are present
enforcedAttributes.removeValue(forKey: tagName)
}
}
return self
}
/**
* Configure this Whitelist to preserve relative links in an element's URL attribute, or convert them to absolute
* links. By default, this is <b>false</b>: URLs will be made absolute (e.g. start with an allowed protocol, like
* e.g. {@code http://}.
* <p>
* Note that when handling relative links, the input document must have an appropriate {@code base URI} set when
* parsing, so that the link's protocol can be confirmed. Regardless of the setting of the {@code preserve relative
* links} option, the link must be resolvable against the base URI to an allowed protocol; otherwise the attribute
* will be removed.
* </p>
*
* @param preserve {@code true} to allow relative links, {@code false} (default) to deny
* @return this Whitelist, for chaining.
* @see #addProtocols
*/
@discardableResult
open func preserveRelativeLinks(_ preserve: Bool) -> Whitelist {
preserveRelativeLinks = preserve
return self
}
/**
Add allowed URL protocols for an element's URL attribute. This restricts the possible values of the attribute to
URLs with the defined protocol.
<p>
E.g.: <code>addProtocols("a", "href", "ftp", "http", "https")</code>
</p>
<p>
To allow a link to an in-page URL anchor (i.e. <code>&lt;a href="#anchor"&gt;</code>, add a <code>#</code>:<br>
E.g.: <code>addProtocols("a", "href", "#")</code>
</p>
@param tag Tag the URL protocol is for
@param key Attribute key
@param protocols List of valid protocols
@return this, for chaining
*/
@discardableResult
open func addProtocols(_ tag: String, _ key: String, _ protocols: String...)throws->Whitelist {
try Validate.notEmpty(string: tag)
try Validate.notEmpty(string: key)
let tagName: TagName = TagName.valueOf(tag)
let attrKey: AttributeKey = AttributeKey.valueOf(key)
var attrMap: Dictionary<AttributeKey, Set<Protocol>>
var protSet: Set<Protocol>
if (self.protocols[tagName] != nil) {
attrMap = self.protocols[tagName]!
} else {
attrMap = Dictionary<AttributeKey, Set<Protocol>>()
self.protocols[tagName] = attrMap
}
if (attrMap[attrKey] != nil) {
protSet = attrMap[attrKey]!
} else {
protSet = Set<Protocol>()
attrMap[attrKey] = protSet
self.protocols[tagName] = attrMap
}
for ptl in protocols {
try Validate.notEmpty(string: ptl)
let prot: Protocol = Protocol.valueOf(ptl)
protSet.insert(prot)
}
attrMap[attrKey] = protSet
self.protocols[tagName] = attrMap
return self
}
/**
Remove allowed URL protocols for an element's URL attribute.
<p>
E.g.: <code>removeProtocols("a", "href", "ftp")</code>
</p>
@param tag Tag the URL protocol is for
@param key Attribute key
@param protocols List of invalid protocols
@return this, for chaining
*/
@discardableResult
open func removeProtocols(_ tag: String, _ key: String, _ protocols: String...)throws->Whitelist {
try Validate.notEmpty(string: tag)
try Validate.notEmpty(string: key)
let tagName: TagName = TagName.valueOf(tag)
let attrKey: AttributeKey = AttributeKey.valueOf(key)
if(self.protocols[tagName] != nil) {
var attrMap: Dictionary<AttributeKey, Set<Protocol>> = self.protocols[tagName]!
if(attrMap[attrKey] != nil) {
var protSet: Set<Protocol> = attrMap[attrKey]!
for ptl in protocols {
try Validate.notEmpty(string: ptl)
let prot: Protocol = Protocol.valueOf(ptl)
protSet.remove(prot)
}
attrMap[attrKey] = protSet
if(protSet.isEmpty) { // Remove protocol set if empty
attrMap.removeValue(forKey: attrKey)
if(attrMap.isEmpty) { // Remove entry for tag if empty
self.protocols.removeValue(forKey: tagName)
}
}
}
self.protocols[tagName] = attrMap
}
return self
}
/**
* Test if the supplied tag is allowed by this whitelist
* @param tag test tag
* @return true if allowed
*/
public func isSafeTag(_ tag: String) -> Bool {
return tagNames.contains(TagName.valueOf(tag))
}
/**
* Test if the supplied attribute is allowed by this whitelist for this tag
* @param tagName tag to consider allowing the attribute in
* @param el element under test, to confirm protocol
* @param attr attribute under test
* @return true if allowed
*/
public func isSafeAttribute(_ tagName: String, _ el: Element, _ attr: Attribute)throws -> Bool {
let tag: TagName = TagName.valueOf(tagName)
let key: AttributeKey = AttributeKey.valueOf(attr.getKey())
if (attributes[tag] != nil) {
if (attributes[tag]?.contains(key))! {
if (protocols[tag] != nil) {
let attrProts: Dictionary<AttributeKey, Set<Protocol>> = protocols[tag]!
// ok if not defined protocol; otherwise test
return try (attrProts[key] == nil) || testValidProtocol(el, attr, attrProts[key]!)
} else { // attribute found, no protocols defined, so OK
return true
}
}
}
// no attributes defined for tag, try :all tag
return try !(tagName == ":all") && isSafeAttribute(":all", el, attr)
}
private func testValidProtocol(_ el: Element, _ attr: Attribute, _ protocols: Set<Protocol>)throws->Bool {
// try to resolve relative urls to abs, and optionally update the attribute so output html has abs.
// rels without a baseuri get removed
var value: String = try el.absUrl(attr.getKey())
if (value.count == 0) {
value = attr.getValue()
}// if it could not be made abs, run as-is to allow custom unknown protocols
if (!preserveRelativeLinks) {
attr.setValue(value: value)
}
for ptl in protocols {
var prot: String = ptl.toString()
if (prot=="#") { // allows anchor links
if (isValidAnchor(value)) {
return true
} else {
continue
}
}
prot += ":"
if (value.lowercased().hasPrefix(prot)) {
return true
}
}
return false
}
private func isValidAnchor(_ value: String) -> Bool {
return value.startsWith("#") && !(Pattern(".*\\s.*").matcher(in: value).count > 0)
}
public func getEnforcedAttributes(_ tagName: String)throws->Attributes {
let attrs: Attributes = Attributes()
let tag: TagName = TagName.valueOf(tagName)
if let keyVals: Dictionary<AttributeKey, AttributeValue> = enforcedAttributes[tag] {
for entry in keyVals {
try attrs.put(entry.key.toString(), entry.value.toString())
}
}
return attrs
}
}
// named types for config. All just hold strings, but here for my sanity.
open class TagName: TypedValue {
override init(_ value: String) {
super.init(value)
}
static func valueOf(_ value: String) -> TagName {
return TagName(value)
}
}
open class AttributeKey: TypedValue {
override init(_ value: String) {
super.init(value)
}
static func valueOf(_ value: String) -> AttributeKey {
return AttributeKey(value)
}
}
open class AttributeValue: TypedValue {
override init(_ value: String) {
super.init(value)
}
static func valueOf(_ value: String) -> AttributeValue {
return AttributeValue(value)
}
}
open class Protocol: TypedValue {
override init(_ value: String) {
super.init(value)
}
static func valueOf(_ value: String) -> Protocol {
return Protocol(value)
}
}
open class TypedValue {
fileprivate let value: String
init(_ value: String) {
self.value = value
}
public func toString() -> String {
return value
}
}
extension TypedValue: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(value)
}
}
public func == (lhs: TypedValue, rhs: TypedValue) -> Bool {
if(lhs === rhs) {return true}
return lhs.value == rhs.value
}

77
Pods/SwiftSoup/Sources/XmlDeclaration.swift

@ -1,77 +0,0 @@
//
// XmlDeclaration.swift
// SwifSoup
//
// Created by Nabil Chatbi on 29/09/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
An XML Declaration.
*/
public class XmlDeclaration: Node {
private let _name: String
private let isProcessingInstruction: Bool // <! if true, <? if false, declaration (and last data char should be ?)
/**
Create a new XML declaration
@param name of declaration
@param baseUri base uri
@param isProcessingInstruction is processing instruction
*/
public init(_ name: String, _ baseUri: String, _ isProcessingInstruction: Bool) {
self._name = name
self.isProcessingInstruction = isProcessingInstruction
super.init(baseUri)
}
public override func nodeName() -> String {
return "#declaration"
}
/**
* Get the name of this declaration.
* @return name of this declaration.
*/
public func name() -> String {
return _name
}
/**
Get the unencoded XML declaration.
@return XML declaration
*/
public func getWholeDeclaration()throws->String {
return try attributes!.html().trim() // attr html starts with a " "
}
override func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {
accum
.append("<")
.append(isProcessingInstruction ? "!" : "?")
.append(_name)
do {
try attributes?.html(accum: accum, out: out)
} catch {}
accum
.append(isProcessingInstruction ? "!" : "?")
.append(">")
}
override func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {}
public override func copy(with zone: NSZone? = nil) -> Any {
let clone = XmlDeclaration(_name, baseUri!, isProcessingInstruction)
return copy(clone: clone)
}
public override func copy(parent: Node?) -> Node {
let clone = XmlDeclaration(_name, baseUri!, isProcessingInstruction)
return copy(clone: clone, parent: parent)
}
public override func copy(clone: Node, parent: Node?) -> Node {
return super.copy(clone: clone, parent: parent)
}
}

146
Pods/SwiftSoup/Sources/XmlTreeBuilder.swift

@ -1,146 +0,0 @@
//
// XmlTreeBuilder.swift
// SwiftSoup
//
// Created by Nabil Chatbi on 14/10/16.
// Copyright © 2016 Nabil Chatbi.. All rights reserved.
//
import Foundation
/**
* Use the {@code XmlTreeBuilder} when you want to parse XML without any of the HTML DOM rules being applied to the
* document.
* <p>Usage example: {@code Document xmlDoc = Jsoup.parse(html, baseUrl, Parser.xmlParser())}</p>
*
*/
public class XmlTreeBuilder: TreeBuilder {
public override init() {
super.init()
}
public override func defaultSettings() -> ParseSettings {
return ParseSettings.preserveCase
}
public func parse(_ input: String, _ baseUri: String)throws->Document {
return try parse(input, baseUri, ParseErrorList.noTracking(), ParseSettings.preserveCase)
}
override public func initialiseParse(_ input: String, _ baseUri: String, _ errors: ParseErrorList, _ settings: ParseSettings) {
super.initialiseParse(input, baseUri, errors, settings)
stack.append(doc) // place the document onto the stack. differs from HtmlTreeBuilder (not on stack)
doc.outputSettings().syntax(syntax: OutputSettings.Syntax.xml)
}
override public func process(_ token: Token)throws->Bool {
// start tag, end tag, doctype, comment, character, eof
switch (token.type) {
case .StartTag:
try insert(token.asStartTag())
break
case .EndTag:
try popStackToClose(token.asEndTag())
break
case .Comment:
try insert(token.asComment())
break
case .Char:
try insert(token.asCharacter())
break
case .Doctype:
try insert(token.asDoctype())
break
case .EOF: // could put some normalisation here if desired
break
// default:
// try Validate.fail(msg: "Unexpected token type: " + token.tokenType())
}
return true
}
private func insertNode(_ node: Node)throws {
try currentElement()?.appendChild(node)
}
@discardableResult
func insert(_ startTag: Token.StartTag)throws->Element {
let tag: Tag = try Tag.valueOf(startTag.name(), settings)
// todo: wonder if for xml parsing, should treat all tags as unknown? because it's not html.
let el: Element = try Element(tag, baseUri, settings.normalizeAttributes(startTag._attributes))
try insertNode(el)
if (startTag.isSelfClosing()) {
tokeniser.acknowledgeSelfClosingFlag()
if (!tag.isKnownTag()) // unknown tag, remember this is self closing for output. see above.
{
tag.setSelfClosing()
}
} else {
stack.append(el)
}
return el
}
func insert(_ commentToken: Token.Comment)throws {
let comment: Comment = Comment(commentToken.getData(), baseUri)
var insert: Node = comment
if (commentToken.bogus) { // xml declarations are emitted as bogus comments (which is right for html, but not xml)
// so we do a bit of a hack and parse the data as an element to pull the attributes out
let data: String = comment.getData()
if (data.count > 1 && (data.startsWith("!") || data.startsWith("?"))) {
let doc: Document = try SwiftSoup.parse("<" + data.substring(1, data.count - 2) + ">", baseUri, Parser.xmlParser())
let el: Element = doc.child(0)
insert = XmlDeclaration(settings.normalizeTag(el.tagName()), comment.getBaseUri(), data.startsWith("!"))
insert.getAttributes()?.addAll(incoming: el.getAttributes())
}
}
try insertNode(insert)
}
func insert(_ characterToken: Token.Char)throws {
let node: Node = TextNode(characterToken.getData()!, baseUri)
try insertNode(node)
}
func insert(_ d: Token.Doctype)throws {
let doctypeNode = DocumentType(settings.normalizeTag(d.getName()), d.getPubSysKey(), d.getPublicIdentifier(), d.getSystemIdentifier(), baseUri)
try insertNode(doctypeNode)
}
/**
* If the stack contains an element with this tag's name, pop up the stack to remove the first occurrence. If not
* found, skips.
*
* @param endTag
*/
private func popStackToClose(_ endTag: Token.EndTag)throws {
let elName: String = try endTag.name()
var firstFound: Element? = nil
for pos in (0..<stack.count).reversed() {
let next: Element = stack[pos]
if (next.nodeName().equals(elName)) {
firstFound = next
break
}
}
if (firstFound == nil) {
return // not found, skip
}
for pos in (0..<stack.count).reversed() {
let next: Element = stack[pos]
stack.remove(at: pos)
if (next == firstFound!) {
break
}
}
}
func parseFragment(_ inputFragment: String, _ baseUri: String, _ errors: ParseErrorList, _ settings: ParseSettings)throws->Array<Node> {
initialiseParse(inputFragment, baseUri, errors, settings)
try runParser()
return doc.getChildNodes()
}
}

2
Pods/Target Support Files/SwiftSoup/SwiftSoup-Info.plist → Pods/Target Support Files/Just/Just-Info.plist

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.2.0</string>
<string>0.8.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

5
Pods/Target Support Files/Just/Just-dummy.m

@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_Just : NSObject
@end
@implementation PodsDummy_Just
@end

0
Pods/Target Support Files/SwiftSoup/SwiftSoup-prefix.pch → Pods/Target Support Files/Just/Just-prefix.pch

4
Pods/Target Support Files/SwiftSoup/SwiftSoup-umbrella.h → Pods/Target Support Files/Just/Just-umbrella.h

@ -11,6 +11,6 @@
#endif
FOUNDATION_EXPORT double SwiftSoupVersionNumber;
FOUNDATION_EXPORT const unsigned char SwiftSoupVersionString[];
FOUNDATION_EXPORT double JustVersionNumber;
FOUNDATION_EXPORT const unsigned char JustVersionString[];

6
Pods/Target Support Files/Just/Just.modulemap

@ -0,0 +1,6 @@
framework module Just {
umbrella header "Just-umbrella.h"
export *
module * { export * }
}

5
Pods/Target Support Files/SwiftSoup/SwiftSoup.xcconfig → Pods/Target Support Files/Just/Just.xcconfig

@ -1,10 +1,11 @@
CODE_SIGN_IDENTITY =
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftSoup
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Just
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftSoup
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Just
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

24
Pods/Target Support Files/Pods-RSwitch/Pods-RSwitch-acknowledgements.markdown

@ -1,28 +1,16 @@
# Acknowledgements
This application makes use of the following third party libraries:
## SwiftSoup
## Just
MIT License
The MIT License (MIT)
Copyright (c) 2016 Nabil Chatbi
Copyright (c) 2015 Just contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Generated by CocoaPods - https://cocoapods.org

24
Pods/Target Support Files/Pods-RSwitch/Pods-RSwitch-acknowledgements.plist

@ -14,32 +14,20 @@
</dict>
<dict>
<key>FooterText</key>
<string>MIT License
<string>The MIT License (MIT)
Copyright (c) 2016 Nabil Chatbi
Copyright (c) 2015 Just contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>SwiftSoup</string>
<string>Just</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>

2
Pods/Target Support Files/Pods-RSwitch/Pods-RSwitch-frameworks-Debug-input-files.xcfilelist

@ -1,2 +1,2 @@
${PODS_ROOT}/Target Support Files/Pods-RSwitch/Pods-RSwitch-frameworks.sh
${BUILT_PRODUCTS_DIR}/SwiftSoup/SwiftSoup.framework
${BUILT_PRODUCTS_DIR}/Just/Just.framework

2
Pods/Target Support Files/Pods-RSwitch/Pods-RSwitch-frameworks-Debug-output-files.xcfilelist

@ -1 +1 @@
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftSoup.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Just.framework

2
Pods/Target Support Files/Pods-RSwitch/Pods-RSwitch-frameworks-Release-input-files.xcfilelist

@ -1,2 +1,2 @@
${PODS_ROOT}/Target Support Files/Pods-RSwitch/Pods-RSwitch-frameworks.sh
${BUILT_PRODUCTS_DIR}/SwiftSoup/SwiftSoup.framework
${BUILT_PRODUCTS_DIR}/Just/Just.framework

2
Pods/Target Support Files/Pods-RSwitch/Pods-RSwitch-frameworks-Release-output-files.xcfilelist

@ -1 +1 @@
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftSoup.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Just.framework

4
Pods/Target Support Files/Pods-RSwitch/Pods-RSwitch-frameworks.sh

@ -161,10 +161,10 @@ strip_invalid_archs() {
if [[ "$CONFIGURATION" == "Debug" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/SwiftSoup/SwiftSoup.framework"
install_framework "${BUILT_PRODUCTS_DIR}/Just/Just.framework"
fi
if [[ "$CONFIGURATION" == "Release" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/SwiftSoup/SwiftSoup.framework"
install_framework "${BUILT_PRODUCTS_DIR}/Just/Just.framework"
fi
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
wait

7
Pods/Target Support Files/Pods-RSwitch/Pods-RSwitch.debug.xcconfig

@ -1,11 +1,12 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SwiftSoup"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Just"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SwiftSoup/SwiftSoup.framework/Headers"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Just/Just.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/../Frameworks' '@loader_path/Frameworks'
OTHER_LDFLAGS = $(inherited) -framework "SwiftSoup"
OTHER_LDFLAGS = $(inherited) -framework "Just"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

7
Pods/Target Support Files/Pods-RSwitch/Pods-RSwitch.release.xcconfig

@ -1,11 +1,12 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SwiftSoup"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Just"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SwiftSoup/SwiftSoup.framework/Headers"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Just/Just.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/../Frameworks' '@loader_path/Frameworks'
OTHER_LDFLAGS = $(inherited) -framework "SwiftSoup"
OTHER_LDFLAGS = $(inherited) -framework "Just"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

5
Pods/Target Support Files/SwiftSoup/SwiftSoup-dummy.m

@ -1,5 +0,0 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_SwiftSoup : NSObject
@end
@implementation PodsDummy_SwiftSoup
@end

6
Pods/Target Support Files/SwiftSoup/SwiftSoup.modulemap

@ -1,6 +0,0 @@
framework module SwiftSoup {
umbrella header "SwiftSoup-umbrella.h"
export *
module * { export * }
}

4
RSwitch.xcodeproj/project.pbxproj

@ -17,6 +17,7 @@
01073F1F2311E67D007162C9 /* HandleUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01073F1E2311E67D007162C9 /* HandleUpdate.swift */; };
01073F212311E6BD007162C9 /* HandleRSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01073F202311E6BD007162C9 /* HandleRSwitch.swift */; };
010A1C1423215B0900E32A9A /* SessionWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 010A1C1323215B0900E32A9A /* SessionWindowController.swift */; };
0117C9E0232270F0004BC889 /* RStudioUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0117C9DF232270F0004BC889 /* RStudioUtils.swift */; };
011951B4231F00A000B62C3A /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011951B3231F00A000B62C3A /* Preferences.swift */; };
0178970D230ED25100F8F5BC /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0178970C230ED25100F8F5BC /* AboutViewController.swift */; };
018A8C3B2312C7BC0006E87D /* libprocInfo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 018A8C3A2312C7BC0006E87D /* libprocInfo.a */; };
@ -50,6 +51,7 @@
01073F1E2311E67D007162C9 /* HandleUpdate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandleUpdate.swift; sourceTree = "<group>"; };
01073F202311E6BD007162C9 /* HandleRSwitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandleRSwitch.swift; sourceTree = "<group>"; };
010A1C1323215B0900E32A9A /* SessionWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionWindowController.swift; sourceTree = "<group>"; };
0117C9DF232270F0004BC889 /* RStudioUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RStudioUtils.swift; sourceTree = "<group>"; };
011951B3231F00A000B62C3A /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
016750FB2319A7A4009E2FD6 /* RSwitch.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RSwitch.entitlements; sourceTree = "<group>"; };
0178970C230ED25100F8F5BC /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
@ -177,6 +179,7 @@
children = (
01073F182311E3B8007162C9 /* Bundle.swift */,
01073F122311E1CF007162C9 /* Globals.swift */,
0117C9DF232270F0004BC889 /* RStudioUtils.swift */,
01975549231BD919003FAD7B /* RVersions.swift */,
0198B9A923197D3A003F7578 /* Running.swift */,
011951B3231F00A000B62C3A /* Preferences.swift */,
@ -362,6 +365,7 @@
010A1C1423215B0900E32A9A /* SessionWindowController.swift in Sources */,
01D3E43C23211B4C00E3BC02 /* RStudioServerAction.swift in Sources */,
018A8C3F2312CB480006E87D /* procHelper.m in Sources */,
0117C9E0232270F0004BC889 /* RStudioUtils.swift in Sources */,
01D3E43A232119DD00E3BC02 /* WebViewController.swift in Sources */,
01073F1F2311E67D007162C9 /* HandleUpdate.swift in Sources */,
01073F1B2311E613007162C9 /* DownloadTarball.swift in Sources */,

9
RSwitch/Swift/AppDelegate.swift

@ -7,6 +7,7 @@
//
import Cocoa
import Just
class DeleteSessionViewController : NSViewController {
@ -25,7 +26,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSToolbarDelegate {
NSApp.activate(ignoringOtherApps: true)
}
@objc func performTimer(_ sender: Timer) { print("timer fired") }
@objc func performTimer(_ sender: Timer) {
print("timer fired")
}
var mainStoryboard: NSStoryboard!
var abtController: NSWindowController!
@ -66,7 +69,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSToolbarDelegate {
}
func applicationDidFinishLaunching(_ aNotification: Notification) {
// dial by IconMark from the Noun Project
statusItem.button?.image = #imageLiteral(resourceName: "RSwitch")
statusItem.menu = statusMenu
@ -78,8 +81,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSToolbarDelegate {
newSessController = (mainStoryboard.instantiateController(withIdentifier: "newSessPanel") as! NSWindowController)
sess = RStudioServerSessionManager()
// sess.newSession(url: "https://rstudio.hrbrmstr.de", title: "One")
// sess.newSession(url: "https://rud.is/b", title: "Two")
timer = Timer.scheduledTimer(
timeInterval: 3600,

95
RSwitch/Swift/Downloaders/DownloadRStudio.swift

@ -8,7 +8,6 @@
import Foundation
import Cocoa
import SwiftSoup
extension AppDelegate {
@ -17,74 +16,62 @@ extension AppDelegate {
self.rstudio_enabled = false
let url = URL(string: app_urls.rstudio_dailies)
let dlurl = RStudioUtils.latestVersionURL()!
do {
let dldir = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first!
var dlfile = dldir
dlfile.appendPathComponent(dlurl.lastPathComponent)
if (FileManager.default.fileExists(atPath: dlfile.relativePath)) {
let html = try String.init(contentsOf: url!)
let document = try SwiftSoup.parse(html)
self.notifyUser(title: "Action required", subtitle: "RStudio Download", text: "A local copy of the latest RStudio daily already exists. Please remove or rename it if you wish to re-download it.")
let link = try document.select("td > a").first!
let href = try link.attr("href")
let dlurl = URL(string: href)!
let dldir = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first!
var dlfile = dldir
NSWorkspace.shared.openFile(dldir.path, withApplication: "Finder")
NSWorkspace.shared.activateFileViewerSelecting([dlfile])
self.rstudio_enabled = true
dlfile.appendPathComponent(dlurl.lastPathComponent)
} else {
if (FileManager.default.fileExists(atPath: dlfile.relativePath)) {
let task = URLSession.shared.downloadTask(with: dlurl) {
tempURL, response, error in
self.notifyUser(title: "Action required", subtitle: "RStudio Download", text: "A local copy of the latest RStudio daily already exists. Please remove or rename it if you wish to re-download it.")
NSWorkspace.shared.openFile(dldir.path, withApplication: "Finder")
NSWorkspace.shared.activateFileViewerSelecting([dlfile])
self.rstudio_enabled = true
} else {
let task = URLSession.shared.downloadTask(with: dlurl) {
tempURL, response, error in
if (error != nil) {
self.notifyUser(title: "Action failed", subtitle: "RStudio Download", text: "Error: " + error!.localizedDescription)
} else if (response != nil) {
if (error != nil) {
self.notifyUser(title: "Action failed", subtitle: "RStudio Download", text: "Error: " + error!.localizedDescription)
} else if (response != nil) {
let status = (response as? HTTPURLResponse)!.statusCode
if (status < 300) {
let status = (response as? HTTPURLResponse)!.statusCode
if (status < 300) {
guard let fileURL = tempURL else {
DispatchQueue.main.async { [weak self] in self?.rstudio_enabled = true }
return
}
do {
try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
try FileManager.default.moveItem(at: fileURL, to: dlfile)
self.notifyUser(title: "Success", subtitle: "RStudio Download", text: "Download of latest RStudio daily (" + dlurl.lastPathComponent + ") successful.")
NSWorkspace.shared.openFile(dldir.path, withApplication: "Finder")
NSWorkspace.shared.activateFileViewerSelecting([dlfile])
} catch {
self.notifyUser(title: "Action failed", subtitle: "RStudio Download", text: "Error: \(error)")
}
} else {
self.notifyUser(title: "Action failed", subtitle: "RStudio Download", text: "Error downloading latest RStudio daily. Status code: " + String(status))
guard let fileURL = tempURL else {
DispatchQueue.main.async { [weak self] in self?.rstudio_enabled = true }
return
}
do {
try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
try FileManager.default.moveItem(at: fileURL, to: dlfile)
self.notifyUser(title: "Success", subtitle: "RStudio Download", text: "Download of latest RStudio daily (" + dlurl.lastPathComponent + ") successful.")
NSWorkspace.shared.openFile(dldir.path, withApplication: "Finder")
NSWorkspace.shared.activateFileViewerSelecting([dlfile])
} catch {
self.notifyUser(title: "Action failed", subtitle: "RStudio Download", text: "Error: \(error)")
}
} else {
self.notifyUser(title: "Action failed", subtitle: "RStudio Download", text: "Error downloading latest RStudio daily. Status code: " + String(status))
}
DispatchQueue.main.async { [weak self] in self?.rstudio_enabled = true }
}
task.resume()
DispatchQueue.main.async { [weak self] in self?.rstudio_enabled = true }
}
} catch {
self.notifyUser(title: "Action failed", subtitle: "RStudio Download", text: "Error downloading and saving latest RStudio daily.")
task.resume()
}
}
}

9
RSwitch/Swift/Utils/Preferences.swift

@ -51,26 +51,19 @@ struct DockIcon {
struct Preferences {
static var showDockIcon: Bool {
get { return(defaults.bool(forKey: .showDockIcon)) }
set {
defaults.set(newValue, forKey: .showDockIcon)
defaults.synchronize()
}
}
static var firstRunGone: Bool {
get { return(defaults.bool(forKey: .firstRunGone)) }
set {
defaults.set(newValue, forKey: .firstRunGone)
defaults.synchronize()
}
}
static func restore() { Preferences.showDockIcon = false }

29
RSwitch/Swift/Utils/RStudioUtils.swift

@ -0,0 +1,29 @@
//
// RStudioUtils.swift
// RSwitch
//
// Created by hrbrmstr on 9/6/19.
// Copyright © 2019 Bob Rudis. All rights reserved.
//
import Foundation
import Just
class RStudioUtils {
private static let dailyCleanerRegex = try!NSRegularExpression(
pattern: "https://s3.amazonaws.com/rstudio-ide-build/desktop/macos/RStudio-|.dmg",
options: NSRegularExpression.Options.caseInsensitive
)
public static func latestVersionURL() -> URL? {
let res = Just.head("https://www.rstudio.org/download/latest/daily/desktop/mac/RStudio-latest.dmg", timeout: 10)
return(res.url)
}
public static func latestVersionNumber(fromString : String? = nil) -> String {
let urlString = (fromString == nil) ? latestVersionURL()!.absoluteString : fromString!
return(dailyCleanerRegex.stringByReplacingMatches(in: urlString, options: [], range: NSMakeRange(0, urlString.count), withTemplate: ""))
}
}
Loading…
Cancel
Save