Archived
Initial commit; substitutions can be reviewed, no further functionality implemented yet
This commit is contained in:
Generated
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 - 2015 Atsushi Kiwaki (@_tid_)
|
||||
|
||||
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.
|
||||
Generated
+3
@@ -0,0 +1,3 @@
|
||||
#import <libxml2/libxml/HTMLtree.h>
|
||||
#import <libxml2/libxml/xpath.h>
|
||||
#import <libxml2/libxml/xpathInternals.h>
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
module libxmlKanna [system] {
|
||||
link "xml2"
|
||||
umbrella header "libxml2-kanna.h"
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
||||
Generated
+228
@@ -0,0 +1,228 @@
|
||||
Kanna(鉋)
|
||||
=================
|
||||
|
||||
Kanna(鉋) is an XML/HTML parser for cross-platform(macOS, iOS, tvOS, watchOS and Linux!).
|
||||
|
||||
It was inspired by [Nokogiri](https://github.com/sparklemotion/nokogiri)(鋸).
|
||||
|
||||
[](https://travis-ci.org/tid-kijyun/Kanna)
|
||||
[](https://developer.apple.com/resources/)
|
||||
[](http://cocoadocs.org/docsets/Kanna/)
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
[](https://github.com/apple/swift-package-manager)
|
||||
[](https://www.versioneye.com/objective-c/kanna/references)
|
||||
|
||||
:information_source: [Documentation](http://tid-kijyun.github.io/Kanna/)
|
||||
|
||||
|
||||
Features:
|
||||
=================
|
||||
- [x] XPath 1.0 support for document searching
|
||||
- [x] CSS3 selector support for document searching
|
||||
- [x] Support for namespaces
|
||||
- [x] Comprehensive test suite
|
||||
|
||||
Installation:
|
||||
=================
|
||||
|
||||
### Swift 5
|
||||
##### CocoaPods
|
||||
Adding it to your `Podfile`:
|
||||
```ruby
|
||||
use_frameworks!
|
||||
pod 'Kanna', '~> 5.0.0'
|
||||
```
|
||||
|
||||
##### Carthage
|
||||
Adding it to your `Cartfile`:
|
||||
|
||||
```ogdl
|
||||
github "tid-kijyun/Kanna" ~> 5.0.0
|
||||
```
|
||||
|
||||
1. In the project settings add `$(SDKROOT)/usr/include/libxml2` to the "header search paths" field
|
||||
|
||||
##### Others(Swift PM and manual Installation)
|
||||
Please refer to the case of Swift 4.
|
||||
|
||||
### Swift 4
|
||||
##### CocoaPods
|
||||
**:warning: CocoaPods (`1.1.0 or later`) is required.**
|
||||
|
||||
Adding it to your `Podfile`:
|
||||
```ruby
|
||||
use_frameworks!
|
||||
pod 'Kanna', '~> 4.0.0'
|
||||
```
|
||||
|
||||
##### Carthage
|
||||
Adding it to your `Cartfile`:
|
||||
|
||||
```ogdl
|
||||
github "tid-kijyun/Kanna" ~> 4.0.0
|
||||
```
|
||||
|
||||
1. In the project settings add `$(SDKROOT)/usr/include/libxml2` to the "header search paths" field
|
||||
|
||||
##### Swift Package Manager
|
||||
|
||||
Installing libxml2 to your computer:
|
||||
|
||||
```bash
|
||||
// macOS
|
||||
$ brew install libxml2
|
||||
$ brew link --force libxml2
|
||||
|
||||
// Linux(Ubuntu)
|
||||
$ sudo apt-get install libxml2-dev
|
||||
```
|
||||
|
||||
Adding it to your `Package.swift`:
|
||||
|
||||
```swift
|
||||
// swift-tools-version:4.0
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "YourProject",
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/tid-kijyun/Kanna.git", from: "4.0.0")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "YourTarget",
|
||||
dependencies: ["Kanna"]),
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ swift build
|
||||
```
|
||||
|
||||
*Note: When a build error occurs, please try run the following command:*
|
||||
```bash
|
||||
$ sudo apt-get install pkg-config
|
||||
```
|
||||
|
||||
##### Manual Installation
|
||||
1. Add these files to your project:
|
||||
[Kanna.swift](Source/Kanna.swift)
|
||||
[CSS.swift](Source/CSS.swift)
|
||||
[libxmlHTMLDocument.swift](Source/libxml/libxmlHTMLDocument.swift)
|
||||
[libxmlHTMLNode.swift](Source/libxml/libxmlHTMLNode.swift)
|
||||
[libxmlParserOption.swift](Source/libxml/libxmlParserOption.swift)
|
||||
[Modules](Modules)
|
||||
1. In the target settings add `$(SDKROOT)/usr/include/libxml2` to the `Search Paths > Header Search Paths` field
|
||||
1. In the target settings add `$(SRCROOT)/Modules` to the `Swift Compiler - Search Paths > Import Paths` field
|
||||
|
||||
|
||||
### Swift 3.0
|
||||
For now, please use the `feature/v3.0.0` branch.
|
||||
|
||||
##### CocoaPods
|
||||
**:warning: CocoaPods (`1.1.0 or later`) is required.**
|
||||
|
||||
Adding it to your `Podfile`:
|
||||
```ruby
|
||||
use_frameworks!
|
||||
pod 'Kanna', :git => 'https://github.com/tid-kijyun/Kanna', :branch => 'feature/v3.0.0'
|
||||
```
|
||||
|
||||
##### Carthage
|
||||
Adding it to your `Cartfile`:
|
||||
|
||||
```ogdl
|
||||
github "tid-kijyun/Kanna" "feature/v3.0.0"
|
||||
```
|
||||
|
||||
1. In the project settings add `$(SDKROOT)/usr/include/libxml2` to the "header search paths" field
|
||||
|
||||
##### Swift Package Manager
|
||||
|
||||
Installing libxml2 to your computer:
|
||||
|
||||
```bash
|
||||
// macOS
|
||||
$ brew install libxml2
|
||||
$ brew link --force libxml2
|
||||
|
||||
// Linux(Ubuntu)
|
||||
$ sudo apt-get install libxml2-dev
|
||||
```
|
||||
|
||||
Adding it to your `Package.swift`:
|
||||
|
||||
```swift
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "YourProject",
|
||||
|
||||
dependencies: [
|
||||
.Package(url: "https://github.com/tid-kijyun/Kanna.git", majorVersion: 2)
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ swift build
|
||||
```
|
||||
|
||||
*Note: When a build error occurs, please try run the following command:*
|
||||
```bash
|
||||
$ sudo apt-get install pkg-config
|
||||
```
|
||||
|
||||
##### Manual Installation
|
||||
1. Add these files to your project:
|
||||
[Kanna.swift](Source/Kanna.swift)
|
||||
[CSS.swift](Source/CSS.swift)
|
||||
[libxmlHTMLDocument.swift](Source/libxml/libxmlHTMLDocument.swift)
|
||||
[libxmlHTMLNode.swift](Source/libxml/libxmlHTMLNode.swift)
|
||||
[libxmlParserOption.swift](Source/libxml/libxmlParserOption.swift)
|
||||
[Modules](Modules)
|
||||
1. In the target settings add `$(SDKROOT)/usr/include/libxml2` to the `Search Paths > Header Search Paths` field
|
||||
1. In the target settings add `$(SRCROOT)/Modules` to the `Swift Compiler - Search Paths > Import Paths` field
|
||||
|
||||
Synopsis:
|
||||
=================
|
||||
|
||||
```swift
|
||||
import Kanna
|
||||
|
||||
let html = "<html>...</html>"
|
||||
|
||||
if let doc = try? HTML(html: html, encoding: .utf8) {
|
||||
print(doc.title)
|
||||
|
||||
// Search for nodes by CSS
|
||||
for link in doc.css("a, link") {
|
||||
print(link.text)
|
||||
print(link["href"])
|
||||
}
|
||||
|
||||
// Search for nodes by XPath
|
||||
for link in doc.xpath("//a | //link") {
|
||||
print(link.text)
|
||||
print(link["href"])
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
let xml = "..."
|
||||
if let doc = try? Kanna.XML(xml: xml, encoding: .utf8) {
|
||||
let namespaces = [
|
||||
"o": "urn:schemas-microsoft-com:office:office",
|
||||
"ss": "urn:schemas-microsoft-com:office:spreadsheet"
|
||||
]
|
||||
if let author = doc.at_xpath("//o:Author", namespaces: namespaces) {
|
||||
print(author.text)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
License:
|
||||
=================
|
||||
The MIT License. See the LICENSE file for more information.
|
||||
+366
@@ -0,0 +1,366 @@
|
||||
/**@file CSS.swift
|
||||
|
||||
Kanna
|
||||
|
||||
Copyright (c) 2015 Atsushi Kiwaki (@_tid_)
|
||||
|
||||
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.
|
||||
*/
|
||||
import Foundation
|
||||
|
||||
import libxmlKanna
|
||||
|
||||
typealias AKRegularExpression = NSRegularExpression
|
||||
#if os(Linux) && swift(>=4)
|
||||
typealias AKTextCheckingResult = NSTextCheckingResult
|
||||
#elseif os(Linux) && swift(>=3)
|
||||
typealias AKTextCheckingResult = TextCheckingResult
|
||||
#else
|
||||
typealias AKTextCheckingResult = NSTextCheckingResult
|
||||
#endif
|
||||
|
||||
public enum CSSError: Error {
|
||||
case UnsupportSyntax(String)
|
||||
}
|
||||
|
||||
/**
|
||||
CSS
|
||||
*/
|
||||
public enum CSS {
|
||||
/**
|
||||
CSS3 selector to XPath
|
||||
|
||||
@param selector CSS3 selector
|
||||
|
||||
@return XPath
|
||||
*/
|
||||
public static func toXPath(_ css: String) throws -> String {
|
||||
let selectorGroups = css.components(separatedBy: ",")
|
||||
return try selectorGroups
|
||||
.map { try toXPath(selector: $0) }
|
||||
.joined(separator: " | ")
|
||||
}
|
||||
|
||||
private static func toXPath(selector: String) throws -> String {
|
||||
var xpath = "//"
|
||||
var str = selector
|
||||
var prev = str
|
||||
|
||||
while !str.isEmpty {
|
||||
var attributes: [String] = []
|
||||
var combinator: String = ""
|
||||
|
||||
str = str.trimmingCharacters(in: .whitespaces)
|
||||
|
||||
// element
|
||||
let element = getElement(&str)
|
||||
|
||||
// class / id
|
||||
while let attr = getClassId(&str) {
|
||||
attributes.append(attr)
|
||||
}
|
||||
|
||||
// attribute
|
||||
while let attr = getAttribute(&str) {
|
||||
attributes.append(attr)
|
||||
}
|
||||
|
||||
// matchCombinator
|
||||
if let combi = genCombinator(&str) {
|
||||
combinator = combi
|
||||
}
|
||||
|
||||
// generate xpath phrase
|
||||
let attr = attributes.joined(separator: " and ")
|
||||
if attr.isEmpty {
|
||||
xpath += "\(element)\(combinator)"
|
||||
} else {
|
||||
xpath += "\(element)[\(attr)]\(combinator)"
|
||||
}
|
||||
|
||||
if str == prev {
|
||||
throw CSSError.UnsupportSyntax(selector)
|
||||
}
|
||||
prev = str
|
||||
}
|
||||
return xpath
|
||||
}
|
||||
}
|
||||
|
||||
private func firstMatch(_ pattern: String) -> (String) -> AKTextCheckingResult? {
|
||||
return { str in
|
||||
let length = str.utf16.count
|
||||
do {
|
||||
let regex = try AKRegularExpression(pattern: pattern, options: .caseInsensitive)
|
||||
if let result = regex.firstMatch(in: str, options: .reportProgress, range: NSRange(location: 0, length: length)) {
|
||||
return result
|
||||
}
|
||||
} catch _ {
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func nth(prefix: String, a: Int, b: Int) -> String {
|
||||
let sibling = "\(prefix)-sibling::*"
|
||||
if a == 0 {
|
||||
return "count(\(sibling)) = \(b-1)"
|
||||
} else if a > 0 {
|
||||
if b != 0 {
|
||||
return "((count(\(sibling)) + 1) >= \(b)) and ((((count(\(sibling)) + 1)-\(b)) mod \(a)) = 0)"
|
||||
}
|
||||
return "((count(\(sibling)) + 1) mod \(a)) = 0"
|
||||
}
|
||||
let a = abs(a)
|
||||
return "(count(\(sibling)) + 1) <= \(b)" + ((a != 1) ? " and ((((count(\(sibling)) + 1)-\(b)) mod \(a) = 0)" : "")
|
||||
}
|
||||
|
||||
// a(n) + b | a(n) - b
|
||||
private func nth_child(a: Int, b: Int) -> String {
|
||||
return nth(prefix: "preceding", a: a, b: b)
|
||||
}
|
||||
|
||||
private func nth_last_child(a: Int, b: Int) -> String {
|
||||
return nth(prefix: "following", a: a, b: b)
|
||||
}
|
||||
|
||||
private let escapePattern = "(?:\\\\([!\"#\\$%&\'\\(\\)\\*\\+,\\./:;<=>\\?@\\[\\\\\\]\\^`\\{\\|\\}~]))"
|
||||
private let escapeRepeatPattern = "\(escapePattern)*"
|
||||
private let matchElement = firstMatch("^((?:[a-z0-9\\*_-]+\(escapeRepeatPattern))+)((\\|)((?:[a-z0-9\\*_-]+\(escapeRepeatPattern))+))?")
|
||||
private let matchClassId = firstMatch("^([#.])((?:[a-z0-9\\*_-]+\(escapeRepeatPattern))+)")
|
||||
private let matchAttr1 = firstMatch("^\\[([^\\]]*)\\]")
|
||||
private let matchAttr2 = firstMatch("^\\[\\s*([^~\\|\\^\\$\\*=\\s]+)\\s*([~\\|\\^\\$\\*]?=)\\s*(.*)\\s*\\]")
|
||||
private let matchAttrN = firstMatch("^:not\\((.*?\\)?)\\)")
|
||||
private let matchPseudo = firstMatch("^:([\'\"()a-z0-9_+-]+)")
|
||||
private let matchCombinator = firstMatch("^\\s*([\\s>+~,])\\s*")
|
||||
private let matchSubNthChild = firstMatch("^(nth-child|nth-last-child)\\(\\s*(odd|even|\\d+)\\s*\\)")
|
||||
private let matchSubNthChildN = firstMatch("^(nth-child|nth-last-child)\\(\\s*(-?\\d*)n(\\+\\d+)?\\s*\\)")
|
||||
private let matchSubNthOfType = firstMatch("nth-of-type\\((odd|even|\\d+)\\)")
|
||||
private let matchSubContains = firstMatch("contains\\([\"\'](.*?)[\"\']\\)")
|
||||
|
||||
private func substringWithRangeAtIndex(_ result: AKTextCheckingResult, str: String, at: Int) -> String {
|
||||
if result.numberOfRanges > at {
|
||||
#if swift(>=4.0) || os(Linux)
|
||||
let range = result.range(at: at)
|
||||
#else
|
||||
let range = result.rangeAt(at)
|
||||
#endif
|
||||
if range.length > 0 {
|
||||
let startIndex = str.index(str.startIndex, offsetBy: range.location)
|
||||
let endIndex = str.index(startIndex, offsetBy: range.length)
|
||||
return String(str[startIndex..<endIndex])
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
private func escapeCSS(_ text: String) -> String {
|
||||
return text.replacingOccurrences(of: escapePattern, with: "$1", options: .regularExpression, range: nil)
|
||||
}
|
||||
|
||||
private func getElement(_ str: inout String, skip: Bool = true) -> String {
|
||||
if let result = matchElement(str) {
|
||||
let (text, text2) = (escapeCSS(substringWithRangeAtIndex(result, str: str, at: 1)),
|
||||
escapeCSS(substringWithRangeAtIndex(result, str: str, at: 5)))
|
||||
|
||||
if skip {
|
||||
str = String(str[str.index(str.startIndex, offsetBy: result.range.length)..<str.endIndex])
|
||||
}
|
||||
|
||||
// tag with namespace
|
||||
if !text.isEmpty && !text2.isEmpty {
|
||||
return "\(text):\(text2)"
|
||||
}
|
||||
|
||||
// tag
|
||||
if !text.isEmpty {
|
||||
return text
|
||||
}
|
||||
}
|
||||
return "*"
|
||||
}
|
||||
|
||||
private func getClassId(_ str: inout String, skip: Bool = true) -> String? {
|
||||
if let result = matchClassId(str) {
|
||||
let (attr, text) = (escapeCSS(substringWithRangeAtIndex(result, str: str, at: 1)),
|
||||
escapeCSS(substringWithRangeAtIndex(result, str: str, at: 2)))
|
||||
if skip {
|
||||
str = String(str[str.index(str.startIndex, offsetBy: result.range.length)..<str.endIndex])
|
||||
}
|
||||
|
||||
if attr.hasPrefix("#") {
|
||||
return "@id = '\(text)'"
|
||||
} else if attr.hasPrefix(".") {
|
||||
return "contains(concat(' ', normalize-space(@class), ' '), ' \(text) ')"
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func getAttribute(_ str: inout String, skip: Bool = true) -> String? {
|
||||
if let result = matchAttr2(str) {
|
||||
let (attr, expr, text) = (escapeCSS(substringWithRangeAtIndex(result, str: str, at: 1)),
|
||||
substringWithRangeAtIndex(result, str: str, at: 2),
|
||||
escapeCSS(substringWithRangeAtIndex(result, str: str, at: 3).replacingOccurrences(of: "[\'\"](.*)[\'\"]", with: "$1", options: .regularExpression, range: nil)))
|
||||
|
||||
if skip {
|
||||
str = String(str[str.index(str.startIndex, offsetBy: result.range.length)..<str.endIndex])
|
||||
}
|
||||
|
||||
switch expr {
|
||||
case "!=":
|
||||
return "@\(attr) != \(text)"
|
||||
case "~=":
|
||||
return "contains(concat(' ', @\(attr), ' '),concat(' ', '\(text)', ' '))"
|
||||
case "|=":
|
||||
return "@\(attr) = '\(text)' or starts-with(@\(attr),concat('\(text)', '-'))"
|
||||
case "^=":
|
||||
return "starts-with(@\(attr), '\(text)')"
|
||||
case "$=":
|
||||
return "substring(@\(attr), string-length(@\(attr)) - string-length('\(text)') + 1, string-length('\(text)')) = '\(text)'"
|
||||
case "*=":
|
||||
return "contains(@\(attr), '\(text)')"
|
||||
default:
|
||||
return "@\(attr) = '\(text)'"
|
||||
}
|
||||
} else if let result = matchAttr1(str) {
|
||||
let atr = substringWithRangeAtIndex(result, str: str, at: 1)
|
||||
if skip {
|
||||
str = String(str[str.index(str.startIndex, offsetBy: result.range.length)..<str.endIndex])
|
||||
}
|
||||
|
||||
return "@\(atr)"
|
||||
} else if str.hasPrefix("[") {
|
||||
// bad syntax attribute
|
||||
return nil
|
||||
} else if let attr = getAttrNot(&str) {
|
||||
return "not(\(attr))"
|
||||
} else if let result = matchPseudo(str) {
|
||||
let one = substringWithRangeAtIndex(result, str: str, at: 1)
|
||||
if skip {
|
||||
str = String(str[str.index(str.startIndex, offsetBy: result.range.length)..<str.endIndex])
|
||||
}
|
||||
|
||||
switch one {
|
||||
case "first-child":
|
||||
return "count(preceding-sibling::*) = 0"
|
||||
case "last-child":
|
||||
return "count(following-sibling::*) = 0"
|
||||
case "only-child":
|
||||
return "count(preceding-sibling::*) = 0 and count(following-sibling::*) = 0"
|
||||
case "first-of-type":
|
||||
return "position() = 1"
|
||||
case "last-of-type":
|
||||
return "position() = last()"
|
||||
case "only-of-type":
|
||||
return "last() = 1"
|
||||
case "empty":
|
||||
return "not(node())"
|
||||
case "root":
|
||||
return "not(parent::*)"
|
||||
default:
|
||||
if let sub = matchSubNthChild(one) {
|
||||
let (nth, arg1) = (substringWithRangeAtIndex(sub, str: one, at: 1),
|
||||
substringWithRangeAtIndex(sub, str: one, at: 2))
|
||||
|
||||
let nthFunc = (nth == "nth-child") ? nth_child : nth_last_child
|
||||
if arg1 == "odd" {
|
||||
return nthFunc(2, 1)
|
||||
} else if arg1 == "even" {
|
||||
return nthFunc(2, 0)
|
||||
} else {
|
||||
return nthFunc(0, Int(arg1)!)
|
||||
}
|
||||
} else if let sub = matchSubNthChildN(one) {
|
||||
let (nth, arg1, arg2) = (substringWithRangeAtIndex(sub, str: one, at: 1),
|
||||
substringWithRangeAtIndex(sub, str: one, at: 2),
|
||||
substringWithRangeAtIndex(sub, str: one, at: 3))
|
||||
|
||||
let nthFunc = (nth == "nth-child") ? nth_child : nth_last_child
|
||||
let a: Int = (arg1 == "-") ? -1 : Int(arg1)!
|
||||
let b: Int = (arg2.isEmpty) ? 0 : Int(arg2)!
|
||||
return nthFunc(a, b)
|
||||
} else if let sub = matchSubNthOfType(one) {
|
||||
let arg1 = substringWithRangeAtIndex(sub, str: one, at: 1)
|
||||
if arg1 == "odd" {
|
||||
return "(position() >= 1) and (((position()-1) mod 2) = 0)"
|
||||
} else if arg1 == "even" {
|
||||
return "(position() mod 2) = 0"
|
||||
} else {
|
||||
return "position() = \(arg1)"
|
||||
}
|
||||
} else if let sub = matchSubContains(one) {
|
||||
let text = substringWithRangeAtIndex(sub, str: one, at: 1)
|
||||
return "contains(., '\(text)')"
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func getAttrNot(_ str: inout String, skip: Bool = true) -> String? {
|
||||
if let result = matchAttrN(str) {
|
||||
var one = substringWithRangeAtIndex(result, str: str, at: 1)
|
||||
if skip {
|
||||
str = String(str[str.index(str.startIndex, offsetBy: result.range.length)..<str.endIndex])
|
||||
}
|
||||
|
||||
if let attr = getAttribute(&one, skip: false) {
|
||||
return attr
|
||||
} else if let sub = matchElement(one) {
|
||||
#if swift(>=4.0) || os(Linux)
|
||||
let range = sub.range(at: 1)
|
||||
#else
|
||||
let range = sub.rangeAt(1)
|
||||
#endif
|
||||
let startIndex = one.index(one.startIndex, offsetBy: range.location)
|
||||
let endIndex = one.index(startIndex, offsetBy: range.length)
|
||||
|
||||
let elem = one[startIndex ..< endIndex]
|
||||
return "self::\(elem)"
|
||||
} else if let attr = getClassId(&one) {
|
||||
return attr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func genCombinator(_ str: inout String, skip: Bool = true) -> String? {
|
||||
if let result = matchCombinator(str) {
|
||||
let one = substringWithRangeAtIndex(result, str: str, at: 1)
|
||||
if skip {
|
||||
str = String(str[str.index(str.startIndex, offsetBy: result.range.length)..<str.endIndex])
|
||||
}
|
||||
|
||||
switch one {
|
||||
case ">":
|
||||
return "/"
|
||||
case "+":
|
||||
return "/following-sibling::*[1]/self::"
|
||||
case "~":
|
||||
return "/following-sibling::"
|
||||
default:
|
||||
return "//"
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// Deprecated.swift
|
||||
// Kanna
|
||||
//
|
||||
// Created by Atsushi Kiwaki on 2017/10/27.
|
||||
// Copyright © 2017 Atsushi Kiwaki. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
//-------------------------------------------------------------
|
||||
// XML
|
||||
//-------------------------------------------------------------
|
||||
@available(*, unavailable, message: "Use XML(xml: String, url: String?, encoding: String.Encoding, option: ParseOption). The type of the second argument has been changed to String.Encoding from UInt.")
|
||||
public func XML(xml: String, url: String? = nil, encoding: UInt, option: ParseOption = kDefaultXmlParseOption) -> XMLDocument? {
|
||||
return nil
|
||||
}
|
||||
|
||||
@available(*, unavailable, message: "Use XML(xml: Data, url: String?, encoding: String.Encoding, option: ParseOption). The type of the first argument has been changed to Data and the type of the second argument has been changed to String.Encoding from UInt.")
|
||||
public func XML(xml: NSData, url: String? = nil, encoding: UInt, option: ParseOption = kDefaultXmlParseOption) -> XMLDocument? {
|
||||
return nil
|
||||
}
|
||||
|
||||
@available(*, unavailable, message: "Use XML(url: URL, encoding: String.Encoding, option: ParseOption). The type of the second argument has been changed to String.Encoding from UInt.")
|
||||
public func XML(url: URL, encoding: UInt, option: ParseOption = kDefaultXmlParseOption) -> XMLDocument? {
|
||||
return nil
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------
|
||||
// HTML
|
||||
//-------------------------------------------------------------
|
||||
@available(*, unavailable, message: "Use HTML(html: String, url: String?, encoding: String.Encoding, option: ParseOption). The type of the second argument has been changed to String.Encoding from UInt.")
|
||||
public func HTML(html: String, url: String? = nil, encoding: UInt, option: ParseOption = kDefaultXmlParseOption) -> XMLDocument? {
|
||||
return nil
|
||||
}
|
||||
|
||||
@available(*, unavailable, message: "Use HTML(html: Data, url: String?, encoding: String.Encoding, option: ParseOption). The type of the first argument has been changed to Data and the type of the second argument has been changed to String.Encoding from UInt.")
|
||||
public func HTML(html: NSData, url: String? = nil, encoding: UInt, option: ParseOption = kDefaultXmlParseOption) -> XMLDocument? {
|
||||
return nil
|
||||
}
|
||||
|
||||
@available(*, unavailable, message: "Use HTML(url: URL, encoding: String.Encoding, option: ParseOption). The type of the second argument has been changed to String.Encoding from UInt.")
|
||||
public func HTML(url: URL, encoding: UInt, option: ParseOption = kDefaultXmlParseOption) -> XMLDocument? {
|
||||
return nil
|
||||
}
|
||||
Generated
+33
@@ -0,0 +1,33 @@
|
||||
/**@file Kanna.h
|
||||
|
||||
Kanna
|
||||
|
||||
Copyright (c) 2015 Atsushi Kiwaki (@_tid_)
|
||||
|
||||
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.
|
||||
*/
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
//! Project version number for Kanna.
|
||||
FOUNDATION_EXPORT double KannaVersionNumber;
|
||||
|
||||
//! Project version string for Kanna.
|
||||
FOUNDATION_EXPORT const unsigned char KannaVersionString[];
|
||||
|
||||
// In this header, you should import all the public headers of your framework using statements like #import <Kanna/PublicHeader.h>
|
||||
+381
@@ -0,0 +1,381 @@
|
||||
/**@file Kanna.swift
|
||||
|
||||
Kanna
|
||||
|
||||
Copyright (c) 2015 Atsushi Kiwaki (@_tid_)
|
||||
|
||||
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.
|
||||
*/
|
||||
import Foundation
|
||||
|
||||
import libxmlKanna
|
||||
|
||||
/*
|
||||
ParseOption
|
||||
*/
|
||||
public enum ParseOption {
|
||||
// libxml2
|
||||
case xmlParseUseLibxml(Libxml2XMLParserOptions)
|
||||
case htmlParseUseLibxml(Libxml2HTMLParserOptions)
|
||||
}
|
||||
|
||||
public let kDefaultXmlParseOption = ParseOption.xmlParseUseLibxml([.RECOVER, .NOERROR, .NOWARNING])
|
||||
public let kDefaultHtmlParseOption = ParseOption.htmlParseUseLibxml([.RECOVER, .NOERROR, .NOWARNING])
|
||||
|
||||
public enum ParseError: Error {
|
||||
case Empty
|
||||
case EncodingMismatch
|
||||
case InvalidOptions
|
||||
}
|
||||
|
||||
/**
|
||||
Parse XML
|
||||
|
||||
@param xml an XML string
|
||||
@param url the base URL to use for the document
|
||||
@param encoding the document encoding
|
||||
@param options a ParserOption
|
||||
*/
|
||||
public func XML(xml: String, url: String? = nil, encoding: String.Encoding, option: ParseOption = kDefaultXmlParseOption) throws -> XMLDocument {
|
||||
switch option {
|
||||
case .xmlParseUseLibxml(let opt):
|
||||
return try libxmlXMLDocument(xml: xml, url: url, encoding: encoding, option: opt.rawValue)
|
||||
default:
|
||||
throw ParseError.InvalidOptions
|
||||
}
|
||||
}
|
||||
|
||||
// NSData
|
||||
public func XML(xml: Data, url: String? = nil, encoding: String.Encoding, option: ParseOption = kDefaultXmlParseOption) throws -> XMLDocument {
|
||||
guard let xmlStr = String(data: xml, encoding: encoding) else {
|
||||
throw ParseError.EncodingMismatch
|
||||
}
|
||||
return try XML(xml: xmlStr, url: url, encoding: encoding, option: option)
|
||||
}
|
||||
|
||||
// NSURL
|
||||
public func XML(url: URL, encoding: String.Encoding, option: ParseOption = kDefaultXmlParseOption) throws -> XMLDocument {
|
||||
guard let data = try? Data(contentsOf: url) else {
|
||||
throw ParseError.EncodingMismatch
|
||||
}
|
||||
return try XML(xml: data, url: url.absoluteString, encoding: encoding, option: option)
|
||||
}
|
||||
|
||||
/**
|
||||
Parse HTML
|
||||
|
||||
@param html an HTML string
|
||||
@param url the base URL to use for the document
|
||||
@param encoding the document encoding
|
||||
@param options a ParserOption
|
||||
*/
|
||||
public func HTML(html: String, url: String? = nil, encoding: String.Encoding, option: ParseOption = kDefaultHtmlParseOption) throws -> HTMLDocument {
|
||||
switch option {
|
||||
case .htmlParseUseLibxml(let opt):
|
||||
return try libxmlHTMLDocument(html: html, url: url, encoding: encoding, option: opt.rawValue)
|
||||
default:
|
||||
throw ParseError.InvalidOptions
|
||||
}
|
||||
}
|
||||
|
||||
// NSData
|
||||
public func HTML(html: Data, url: String? = nil, encoding: String.Encoding, option: ParseOption = kDefaultHtmlParseOption) throws -> HTMLDocument {
|
||||
guard let htmlStr = String(data: html, encoding: encoding) else {
|
||||
throw ParseError.EncodingMismatch
|
||||
}
|
||||
return try HTML(html: htmlStr, url: url, encoding: encoding, option: option)
|
||||
}
|
||||
|
||||
// NSURL
|
||||
public func HTML(url: URL, encoding: String.Encoding, option: ParseOption = kDefaultHtmlParseOption) throws -> HTMLDocument {
|
||||
guard let data = try? Data(contentsOf: url) else {
|
||||
throw ParseError.EncodingMismatch
|
||||
}
|
||||
return try HTML(html: data, url: url.absoluteString, encoding: encoding, option: option)
|
||||
}
|
||||
|
||||
/**
|
||||
Searchable
|
||||
*/
|
||||
public protocol Searchable {
|
||||
/**
|
||||
Search for node from current node by XPath.
|
||||
|
||||
@param xpath
|
||||
*/
|
||||
func xpath(_ xpath: String, namespaces: [String:String]?) -> XPathObject
|
||||
func xpath(_ xpath: String) -> XPathObject
|
||||
func at_xpath(_ xpath: String, namespaces: [String:String]?) -> XMLElement?
|
||||
func at_xpath(_ xpath: String) -> XMLElement?
|
||||
|
||||
/**
|
||||
Search for node from current node by CSS selector.
|
||||
|
||||
@param selector a CSS selector
|
||||
*/
|
||||
func css(_ selector: String, namespaces: [String:String]?) -> XPathObject
|
||||
func css(_ selector: String) -> XPathObject
|
||||
func at_css(_ selector: String, namespaces: [String:String]?) -> XMLElement?
|
||||
func at_css(_ selector: String) -> XMLElement?
|
||||
}
|
||||
|
||||
/**
|
||||
SearchableNode
|
||||
*/
|
||||
public protocol SearchableNode: Searchable {
|
||||
var text: String? { get }
|
||||
var toHTML: String? { get }
|
||||
var toXML: String? { get }
|
||||
var innerHTML: String? { get }
|
||||
var className: String? { get }
|
||||
var tagName: String? { get set }
|
||||
var content: String? { get set }
|
||||
}
|
||||
|
||||
/**
|
||||
XMLElement
|
||||
*/
|
||||
public protocol XMLElement: SearchableNode {
|
||||
var parent: XMLElement? { get set }
|
||||
subscript(attr: String) -> String? { get set }
|
||||
|
||||
func addPrevSibling(_ node: XMLElement)
|
||||
func addNextSibling(_ node: XMLElement)
|
||||
func removeChild(_ node: XMLElement)
|
||||
var nextSibling: XMLElement? { get }
|
||||
var previousSibling: XMLElement? { get }
|
||||
}
|
||||
|
||||
/**
|
||||
XMLDocument
|
||||
*/
|
||||
public protocol XMLDocument: class, SearchableNode {
|
||||
}
|
||||
|
||||
/**
|
||||
HTMLDocument
|
||||
*/
|
||||
public protocol HTMLDocument: XMLDocument {
|
||||
var title: String? { get }
|
||||
var head: XMLElement? { get }
|
||||
var body: XMLElement? { get }
|
||||
}
|
||||
|
||||
/**
|
||||
XMLNodeSet
|
||||
*/
|
||||
public final class XMLNodeSet {
|
||||
fileprivate var nodes: [XMLElement] = []
|
||||
|
||||
public var toHTML: String? {
|
||||
let html = nodes.reduce("") {
|
||||
if let text = $1.toHTML {
|
||||
return $0 + text
|
||||
}
|
||||
return $0
|
||||
}
|
||||
return html.isEmpty == false ? html : nil
|
||||
}
|
||||
|
||||
public var innerHTML: String? {
|
||||
let html = nodes.reduce("") {
|
||||
if let text = $1.innerHTML {
|
||||
return $0 + text
|
||||
}
|
||||
return $0
|
||||
}
|
||||
return html.isEmpty == false ? html : nil
|
||||
}
|
||||
|
||||
public var text: String? {
|
||||
let html = nodes.reduce("") {
|
||||
if let text = $1.text {
|
||||
return $0 + text
|
||||
}
|
||||
return $0
|
||||
}
|
||||
return html
|
||||
}
|
||||
|
||||
public subscript(index: Int) -> XMLElement {
|
||||
return nodes[index]
|
||||
}
|
||||
|
||||
public var count: Int {
|
||||
return nodes.count
|
||||
}
|
||||
|
||||
internal init() {
|
||||
}
|
||||
|
||||
internal init(nodes: [XMLElement]) {
|
||||
self.nodes = nodes
|
||||
}
|
||||
|
||||
public func at(_ index: Int) -> XMLElement? {
|
||||
return count > index ? nodes[index] : nil
|
||||
}
|
||||
|
||||
public var first: XMLElement? {
|
||||
return at(0)
|
||||
}
|
||||
|
||||
public var last: XMLElement? {
|
||||
return at(count-1)
|
||||
}
|
||||
}
|
||||
|
||||
extension XMLNodeSet: Sequence {
|
||||
public typealias Iterator = AnyIterator<XMLElement>
|
||||
public func makeIterator() -> Iterator {
|
||||
var index = 0
|
||||
return AnyIterator {
|
||||
if index < self.nodes.count {
|
||||
let n = self.nodes[index]
|
||||
index += 1
|
||||
return n
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
XPathObject
|
||||
*/
|
||||
|
||||
public enum XPathObject {
|
||||
case none
|
||||
case NodeSet(nodeset: XMLNodeSet)
|
||||
case Bool(bool: Swift.Bool)
|
||||
case Number(num: Double)
|
||||
case String(text: Swift.String)
|
||||
}
|
||||
|
||||
extension XPathObject {
|
||||
internal init(document: XMLDocument?, docPtr: xmlDocPtr, object: xmlXPathObject) {
|
||||
switch object.type {
|
||||
case XPATH_NODESET:
|
||||
let nodeSet = object.nodesetval
|
||||
if nodeSet == nil || nodeSet?.pointee.nodeNr == 0 || nodeSet?.pointee.nodeTab == nil {
|
||||
self = .none
|
||||
return
|
||||
}
|
||||
|
||||
var nodes : [XMLElement] = []
|
||||
let size = Int((nodeSet?.pointee.nodeNr)!)
|
||||
for i in 0 ..< size {
|
||||
let node: xmlNodePtr = nodeSet!.pointee.nodeTab[i]!
|
||||
let htmlNode = libxmlHTMLNode(document: document, docPtr: docPtr, node: node)
|
||||
nodes.append(htmlNode)
|
||||
}
|
||||
self = .NodeSet(nodeset: XMLNodeSet(nodes: nodes))
|
||||
return
|
||||
case XPATH_BOOLEAN:
|
||||
self = .Bool(bool: object.boolval != 0)
|
||||
return
|
||||
case XPATH_NUMBER:
|
||||
self = .Number(num: object.floatval)
|
||||
case XPATH_STRING:
|
||||
guard let str = UnsafeRawPointer(object.stringval)?.assumingMemoryBound(to: CChar.self) else {
|
||||
self = .String(text: "")
|
||||
return
|
||||
}
|
||||
self = .String(text: Swift.String(cString: str))
|
||||
return
|
||||
default:
|
||||
self = .none
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
public subscript(index: Int) -> XMLElement {
|
||||
return nodeSet![index]
|
||||
}
|
||||
|
||||
public var first: XMLElement? {
|
||||
return nodeSet?.first
|
||||
}
|
||||
|
||||
public var count: Int {
|
||||
guard let nodeset = nodeSet else {
|
||||
return 0
|
||||
}
|
||||
return nodeset.count
|
||||
}
|
||||
|
||||
var nodeSet: XMLNodeSet? {
|
||||
if case let .NodeSet(nodeset) = self {
|
||||
return nodeset
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var bool: Swift.Bool? {
|
||||
if case let .Bool(value) = self {
|
||||
return value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var number: Double? {
|
||||
if case let .Number(value) = self {
|
||||
return value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var string: Swift.String? {
|
||||
if case let .String(value) = self {
|
||||
return value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var nodeSetValue: XMLNodeSet {
|
||||
return nodeSet ?? XMLNodeSet()
|
||||
}
|
||||
|
||||
var boolValue: Swift.Bool {
|
||||
return bool ?? false
|
||||
}
|
||||
|
||||
var numberValue: Double {
|
||||
return number ?? 0.0
|
||||
}
|
||||
|
||||
var stringValue: Swift.String {
|
||||
return string ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
extension XPathObject: Sequence {
|
||||
public typealias Iterator = AnyIterator<XMLElement>
|
||||
public func makeIterator() -> Iterator {
|
||||
var index = 0
|
||||
return AnyIterator {
|
||||
if index < self.nodeSetValue.count {
|
||||
let obj = self.nodeSetValue[index]
|
||||
index += 1
|
||||
return obj
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
+393
@@ -0,0 +1,393 @@
|
||||
/**@file libxmlHTMLDocument.swift
|
||||
|
||||
Kanna
|
||||
|
||||
Copyright (c) 2015 Atsushi Kiwaki (@_tid_)
|
||||
|
||||
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.
|
||||
*/
|
||||
import Foundation
|
||||
import CoreFoundation
|
||||
|
||||
import libxmlKanna
|
||||
|
||||
extension String.Encoding {
|
||||
var IANACharSetName: String? {
|
||||
#if os(Linux) && swift(>=4)
|
||||
switch self {
|
||||
case .ascii:
|
||||
return "us-ascii"
|
||||
case .iso2022JP:
|
||||
return "iso-2022-jp"
|
||||
case .isoLatin1:
|
||||
return "iso-8859-1"
|
||||
case .isoLatin2:
|
||||
return "iso-8859-2"
|
||||
case .japaneseEUC:
|
||||
return "euc-jp"
|
||||
case .macOSRoman:
|
||||
return "macintosh"
|
||||
case .nextstep:
|
||||
return "x-nextstep"
|
||||
case .nonLossyASCII:
|
||||
return nil
|
||||
case .shiftJIS:
|
||||
return "cp932"
|
||||
case .symbol:
|
||||
return "x-mac-symbol"
|
||||
case .unicode:
|
||||
return "utf-16"
|
||||
case .utf16:
|
||||
return "utf-16"
|
||||
case .utf16BigEndian:
|
||||
return "utf-16be"
|
||||
case .utf32:
|
||||
return "utf-32"
|
||||
case .utf32BigEndian:
|
||||
return "utf-32be"
|
||||
case .utf32LittleEndian:
|
||||
return "utf-32le"
|
||||
case .utf8:
|
||||
return "utf-8"
|
||||
case .windowsCP1250:
|
||||
return "windows-1250"
|
||||
case .windowsCP1251:
|
||||
return "windows-1251"
|
||||
case .windowsCP1252:
|
||||
return "windows-1252"
|
||||
case .windowsCP1253:
|
||||
return "windows-1253"
|
||||
case .windowsCP1254:
|
||||
return "windows-1254"
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
#elseif os(Linux) && swift(>=3)
|
||||
switch self {
|
||||
case String.Encoding.ascii:
|
||||
return "us-ascii"
|
||||
case String.Encoding.iso2022JP:
|
||||
return "iso-2022-jp"
|
||||
case String.Encoding.isoLatin1:
|
||||
return "iso-8859-1"
|
||||
case String.Encoding.isoLatin2:
|
||||
return "iso-8859-2"
|
||||
case String.Encoding.japaneseEUC:
|
||||
return "euc-jp"
|
||||
case String.Encoding.macOSRoman:
|
||||
return "macintosh"
|
||||
case String.Encoding.nextstep:
|
||||
return "x-nextstep"
|
||||
case String.Encoding.nonLossyASCII:
|
||||
return nil
|
||||
case String.Encoding.shiftJIS:
|
||||
return "cp932"
|
||||
case String.Encoding.symbol:
|
||||
return "x-mac-symbol"
|
||||
case String.Encoding.unicode:
|
||||
return "utf-16"
|
||||
case String.Encoding.utf16:
|
||||
return "utf-16"
|
||||
case String.Encoding.utf16BigEndian:
|
||||
return "utf-16be"
|
||||
case String.Encoding.utf32:
|
||||
return "utf-32"
|
||||
case String.Encoding.utf32BigEndian:
|
||||
return "utf-32be"
|
||||
case String.Encoding.utf32LittleEndian:
|
||||
return "utf-32le"
|
||||
case String.Encoding.utf8:
|
||||
return "utf-8"
|
||||
case String.Encoding.windowsCP1250:
|
||||
return "windows-1250"
|
||||
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"
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
#else
|
||||
let cfenc = CFStringConvertNSStringEncodingToEncoding(self.rawValue)
|
||||
guard let cfencstr = CFStringConvertEncodingToIANACharSetName(cfenc) else {
|
||||
return nil
|
||||
}
|
||||
return String(describing: cfencstr)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
libxmlHTMLDocument
|
||||
*/
|
||||
internal final class libxmlHTMLDocument: HTMLDocument {
|
||||
fileprivate var docPtr: htmlDocPtr? = nil
|
||||
fileprivate var rootNode: XMLElement?
|
||||
fileprivate var html: String
|
||||
fileprivate var url: String?
|
||||
fileprivate var encoding: String.Encoding
|
||||
|
||||
var text: String? {
|
||||
return rootNode?.text
|
||||
}
|
||||
|
||||
var toHTML: String? {
|
||||
let buf = xmlBufferCreate()
|
||||
let outputBuf = xmlOutputBufferCreateBuffer(buf, nil)
|
||||
defer {
|
||||
xmlOutputBufferClose(outputBuf)
|
||||
xmlBufferFree(buf)
|
||||
}
|
||||
|
||||
htmlDocContentDumpOutput(outputBuf, docPtr, nil)
|
||||
let html = String(cString: UnsafePointer(xmlOutputBufferGetContent(outputBuf)))
|
||||
return html
|
||||
}
|
||||
|
||||
var toXML: String? {
|
||||
var buf: UnsafeMutablePointer<xmlChar>? = nil
|
||||
let size: UnsafeMutablePointer<Int32>? = nil
|
||||
defer {
|
||||
xmlFree(buf)
|
||||
}
|
||||
|
||||
xmlDocDumpMemory(docPtr, &buf, size)
|
||||
let html = String(cString: UnsafePointer<UInt8>(buf!))
|
||||
return html
|
||||
}
|
||||
|
||||
var innerHTML: String? {
|
||||
return rootNode?.innerHTML
|
||||
}
|
||||
|
||||
var className: String? {
|
||||
return nil
|
||||
}
|
||||
|
||||
var tagName: String? {
|
||||
get {
|
||||
return nil
|
||||
}
|
||||
|
||||
set {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var content: String? {
|
||||
get {
|
||||
return text
|
||||
}
|
||||
|
||||
set {
|
||||
rootNode?.content = newValue
|
||||
}
|
||||
}
|
||||
|
||||
init(html: String, url: String?, encoding: String.Encoding, option: UInt) throws {
|
||||
self.html = html
|
||||
self.url = url
|
||||
self.encoding = encoding
|
||||
|
||||
guard html.lengthOfBytes(using: encoding) > 0 else {
|
||||
throw ParseError.Empty
|
||||
}
|
||||
|
||||
guard let charsetName = encoding.IANACharSetName,
|
||||
let cur = html.cString(using: encoding) else {
|
||||
throw ParseError.EncodingMismatch
|
||||
}
|
||||
|
||||
let url : String = ""
|
||||
docPtr = htmlReadDoc(UnsafeRawPointer(cur).assumingMemoryBound(to: xmlChar.self), url, charsetName, CInt(option))
|
||||
|
||||
guard let docPtr = docPtr else {
|
||||
throw ParseError.EncodingMismatch
|
||||
}
|
||||
|
||||
rootNode = libxmlHTMLNode(document: self, docPtr: docPtr)
|
||||
}
|
||||
|
||||
deinit {
|
||||
xmlFreeDoc(self.docPtr)
|
||||
}
|
||||
|
||||
var title: String? { return at_xpath("//title")?.text }
|
||||
var head: XMLElement? { return at_xpath("//head") }
|
||||
var body: XMLElement? { return at_xpath("//body") }
|
||||
|
||||
func xpath(_ xpath: String, namespaces: [String:String]?) -> XPathObject {
|
||||
return rootNode?.xpath(xpath, namespaces: namespaces) ?? XPathObject.none
|
||||
}
|
||||
|
||||
func xpath(_ xpath: String) -> XPathObject {
|
||||
return self.xpath(xpath, namespaces: nil)
|
||||
}
|
||||
|
||||
func at_xpath(_ xpath: String, namespaces: [String:String]?) -> XMLElement? {
|
||||
return rootNode?.at_xpath(xpath, namespaces: namespaces)
|
||||
}
|
||||
|
||||
func at_xpath(_ xpath: String) -> XMLElement? {
|
||||
return self.at_xpath(xpath, namespaces: nil)
|
||||
}
|
||||
|
||||
func css(_ selector: String, namespaces: [String:String]?) -> XPathObject {
|
||||
return rootNode?.css(selector, namespaces: namespaces) ?? XPathObject.none
|
||||
}
|
||||
|
||||
func css(_ selector: String) -> XPathObject {
|
||||
return self.css(selector, namespaces: nil)
|
||||
}
|
||||
|
||||
func at_css(_ selector: String, namespaces: [String:String]?) -> XMLElement? {
|
||||
return rootNode?.at_css(selector, namespaces: namespaces)
|
||||
}
|
||||
|
||||
func at_css(_ selector: String) -> XMLElement? {
|
||||
return self.at_css(selector, namespaces: nil)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
libxmlXMLDocument
|
||||
*/
|
||||
internal final class libxmlXMLDocument: XMLDocument {
|
||||
fileprivate var docPtr: xmlDocPtr? = nil
|
||||
fileprivate var rootNode: XMLElement?
|
||||
fileprivate var xml: String
|
||||
fileprivate var url: String?
|
||||
fileprivate var encoding: String.Encoding
|
||||
|
||||
var text: String? {
|
||||
return rootNode?.text
|
||||
}
|
||||
|
||||
var toHTML: String? {
|
||||
let buf = xmlBufferCreate()
|
||||
let outputBuf = xmlOutputBufferCreateBuffer(buf, nil)
|
||||
defer {
|
||||
xmlOutputBufferClose(outputBuf)
|
||||
xmlBufferFree(buf)
|
||||
}
|
||||
|
||||
htmlDocContentDumpOutput(outputBuf, docPtr, nil)
|
||||
let html = String(cString: UnsafePointer(xmlOutputBufferGetContent(outputBuf)))
|
||||
return html
|
||||
}
|
||||
|
||||
var toXML: String? {
|
||||
var buf: UnsafeMutablePointer<xmlChar>? = nil
|
||||
let size: UnsafeMutablePointer<Int32>? = nil
|
||||
defer {
|
||||
xmlFree(buf)
|
||||
}
|
||||
|
||||
xmlDocDumpMemory(docPtr, &buf, size)
|
||||
let html = String(cString: UnsafePointer<UInt8>(buf!))
|
||||
return html
|
||||
}
|
||||
|
||||
var innerHTML: String? {
|
||||
return rootNode?.innerHTML
|
||||
}
|
||||
|
||||
var className: String? {
|
||||
return nil
|
||||
}
|
||||
|
||||
var tagName: String? {
|
||||
get {
|
||||
return nil
|
||||
}
|
||||
|
||||
set {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var content: String? {
|
||||
get {
|
||||
return text
|
||||
}
|
||||
|
||||
set {
|
||||
rootNode?.content = newValue
|
||||
}
|
||||
}
|
||||
|
||||
init(xml: String, url: String?, encoding: String.Encoding, option: UInt) throws {
|
||||
self.xml = xml
|
||||
self.url = url
|
||||
self.encoding = encoding
|
||||
|
||||
if xml.isEmpty {
|
||||
throw ParseError.Empty
|
||||
}
|
||||
|
||||
|
||||
guard let charsetName = encoding.IANACharSetName,
|
||||
let cur = xml.cString(using: encoding) else {
|
||||
throw ParseError.EncodingMismatch
|
||||
}
|
||||
let url : String = ""
|
||||
docPtr = xmlReadDoc(UnsafeRawPointer(cur).assumingMemoryBound(to: xmlChar.self), url, charsetName, CInt(option))
|
||||
rootNode = libxmlHTMLNode(document: self, docPtr: docPtr!)
|
||||
}
|
||||
|
||||
deinit {
|
||||
xmlFreeDoc(self.docPtr)
|
||||
}
|
||||
|
||||
func xpath(_ xpath: String, namespaces: [String:String]?) -> XPathObject {
|
||||
return rootNode?.xpath(xpath, namespaces: namespaces) ?? XPathObject.none
|
||||
}
|
||||
|
||||
func xpath(_ xpath: String) -> XPathObject {
|
||||
return self.xpath(xpath, namespaces: nil)
|
||||
}
|
||||
|
||||
func at_xpath(_ xpath: String, namespaces: [String:String]?) -> XMLElement? {
|
||||
return rootNode?.at_xpath(xpath, namespaces: namespaces)
|
||||
}
|
||||
|
||||
func at_xpath(_ xpath: String) -> XMLElement? {
|
||||
return self.at_xpath(xpath, namespaces: nil)
|
||||
}
|
||||
|
||||
func css(_ selector: String, namespaces: [String:String]?) -> XPathObject {
|
||||
return rootNode?.css(selector, namespaces: namespaces) ?? XPathObject.none
|
||||
}
|
||||
|
||||
func css(_ selector: String) -> XPathObject {
|
||||
return self.css(selector, namespaces: nil)
|
||||
}
|
||||
|
||||
func at_css(_ selector: String, namespaces: [String:String]?) -> XMLElement? {
|
||||
return rootNode?.at_css(selector, namespaces: namespaces)
|
||||
}
|
||||
|
||||
func at_css(_ selector: String) -> XMLElement? {
|
||||
return self.at_css(selector, namespaces: nil)
|
||||
}
|
||||
}
|
||||
+300
@@ -0,0 +1,300 @@
|
||||
/**@file libxmlHTMLNode.swift
|
||||
|
||||
Kanna
|
||||
|
||||
Copyright (c) 2015 Atsushi Kiwaki (@_tid_)
|
||||
|
||||
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.
|
||||
*/
|
||||
import Foundation
|
||||
|
||||
import libxmlKanna
|
||||
|
||||
/**
|
||||
libxmlHTMLNode
|
||||
*/
|
||||
internal final class libxmlHTMLNode: XMLElement {
|
||||
var text: String? {
|
||||
if nodePtr != nil {
|
||||
return libxmlGetNodeContent(nodePtr!)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var toHTML: String? {
|
||||
let buf = xmlBufferCreate()
|
||||
htmlNodeDump(buf, docPtr, nodePtr)
|
||||
let html = String(cString: UnsafePointer<UInt8>((buf?.pointee.content)!))
|
||||
xmlBufferFree(buf)
|
||||
return html
|
||||
}
|
||||
|
||||
var toXML: String? {
|
||||
let buf = xmlBufferCreate()
|
||||
xmlNodeDump(buf, docPtr, nodePtr, 0, 0)
|
||||
let html = String(cString: UnsafePointer<UInt8>((buf?.pointee.content)!))
|
||||
xmlBufferFree(buf)
|
||||
return html
|
||||
}
|
||||
|
||||
var innerHTML: String? {
|
||||
if let html = self.toHTML {
|
||||
let inner = html.replacingOccurrences(of: "</[^>]*>$", with: "", options: .regularExpression, range: nil)
|
||||
.replacingOccurrences(of: "^<[^>]*>", with: "", options: .regularExpression, range: nil)
|
||||
return inner
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var className: String? {
|
||||
return self["class"]
|
||||
}
|
||||
|
||||
var tagName: String? {
|
||||
get {
|
||||
guard let name = nodePtr?.pointee.name else {
|
||||
return nil
|
||||
}
|
||||
return String(cString: name)
|
||||
}
|
||||
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
xmlNodeSetName(nodePtr, newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var content: String? {
|
||||
get {
|
||||
return text
|
||||
}
|
||||
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
let v = escape(newValue)
|
||||
xmlNodeSetContent(nodePtr, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var parent: XMLElement? {
|
||||
get {
|
||||
return libxmlHTMLNode(document: doc, docPtr: docPtr!, node: (nodePtr?.pointee.parent)!)
|
||||
}
|
||||
|
||||
set {
|
||||
if let node = newValue as? libxmlHTMLNode {
|
||||
node.addChild(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var nextSibling: XMLElement? {
|
||||
let val = xmlNextElementSibling(self.nodePtr)
|
||||
return self.node(from: val)
|
||||
}
|
||||
|
||||
var previousSibling: XMLElement? {
|
||||
let val = xmlPreviousElementSibling(self.nodePtr)
|
||||
return self.node(from: val)
|
||||
}
|
||||
|
||||
fileprivate weak var weakDocument: XMLDocument?
|
||||
fileprivate var document: XMLDocument?
|
||||
fileprivate var docPtr: htmlDocPtr? = nil
|
||||
fileprivate var nodePtr: xmlNodePtr? = nil
|
||||
fileprivate var isRoot: Bool = false
|
||||
fileprivate var doc: XMLDocument? {
|
||||
return weakDocument ?? document
|
||||
}
|
||||
|
||||
subscript(attributeName: String) -> String?
|
||||
{
|
||||
get {
|
||||
var attr = nodePtr?.pointee.properties
|
||||
while attr != nil {
|
||||
let mem = attr?.pointee
|
||||
if let tagName = String(validatingUTF8: UnsafeRawPointer((mem?.name)!).assumingMemoryBound(to: CChar.self)) {
|
||||
if attributeName == tagName {
|
||||
if let children = mem?.children {
|
||||
return libxmlGetNodeContent(children)
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
attr = attr?.pointee.next
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
set(newValue) {
|
||||
if let newValue = newValue {
|
||||
xmlSetProp(nodePtr, attributeName, newValue)
|
||||
} else {
|
||||
xmlUnsetProp(nodePtr, attributeName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(document: XMLDocument?, docPtr: xmlDocPtr) {
|
||||
self.weakDocument = document
|
||||
self.docPtr = docPtr
|
||||
self.nodePtr = xmlDocGetRootElement(docPtr)
|
||||
self.isRoot = true
|
||||
}
|
||||
|
||||
init(document: XMLDocument?, docPtr: xmlDocPtr, node: xmlNodePtr) {
|
||||
self.document = document
|
||||
self.docPtr = docPtr
|
||||
self.nodePtr = node
|
||||
}
|
||||
|
||||
// MARK: Searchable
|
||||
func xpath(_ xpath: String, namespaces: [String:String]?) -> XPathObject {
|
||||
let ctxt = xmlXPathNewContext(docPtr)
|
||||
if ctxt == nil {
|
||||
return XPathObject.none
|
||||
}
|
||||
ctxt?.pointee.node = nodePtr
|
||||
|
||||
if let nsDictionary = namespaces {
|
||||
for (ns, name) in nsDictionary {
|
||||
xmlXPathRegisterNs(ctxt, ns, name)
|
||||
}
|
||||
}
|
||||
|
||||
let result = xmlXPathEvalExpression(xpath, ctxt)
|
||||
defer {
|
||||
xmlXPathFreeObject(result)
|
||||
}
|
||||
xmlXPathFreeContext(ctxt)
|
||||
if result == nil {
|
||||
return XPathObject.none
|
||||
}
|
||||
|
||||
return XPathObject(document: doc, docPtr: docPtr!, object: result!.pointee)
|
||||
}
|
||||
|
||||
func xpath(_ xpath: String) -> XPathObject {
|
||||
return self.xpath(xpath, namespaces: nil)
|
||||
}
|
||||
|
||||
func at_xpath(_ xpath: String, namespaces: [String:String]?) -> XMLElement? {
|
||||
return self.xpath(xpath, namespaces: namespaces).nodeSetValue.first
|
||||
}
|
||||
|
||||
func at_xpath(_ xpath: String) -> XMLElement? {
|
||||
return self.at_xpath(xpath, namespaces: nil)
|
||||
}
|
||||
|
||||
func css(_ selector: String, namespaces: [String:String]?) -> XPathObject {
|
||||
if let xpath = try? CSS.toXPath(selector) {
|
||||
if isRoot {
|
||||
return self.xpath(xpath, namespaces: namespaces)
|
||||
} else {
|
||||
return self.xpath("." + xpath, namespaces: namespaces)
|
||||
}
|
||||
}
|
||||
return XPathObject.none
|
||||
}
|
||||
|
||||
func css(_ selector: String) -> XPathObject {
|
||||
return self.css(selector, namespaces: nil)
|
||||
}
|
||||
|
||||
func at_css(_ selector: String, namespaces: [String:String]?) -> XMLElement? {
|
||||
return self.css(selector, namespaces: namespaces).nodeSetValue.first
|
||||
}
|
||||
|
||||
func at_css(_ selector: String) -> XMLElement? {
|
||||
return self.css(selector, namespaces: nil).nodeSetValue.first
|
||||
}
|
||||
|
||||
func addPrevSibling(_ node: XMLElement) {
|
||||
guard let node = node as? libxmlHTMLNode else {
|
||||
return
|
||||
}
|
||||
xmlAddPrevSibling(nodePtr, node.nodePtr)
|
||||
}
|
||||
|
||||
func addNextSibling(_ node: XMLElement) {
|
||||
guard let node = node as? libxmlHTMLNode else {
|
||||
return
|
||||
}
|
||||
xmlAddNextSibling(nodePtr, node.nodePtr)
|
||||
}
|
||||
|
||||
func addChild(_ node: XMLElement) {
|
||||
guard let node = node as? libxmlHTMLNode else {
|
||||
return
|
||||
}
|
||||
xmlUnlinkNode(node.nodePtr)
|
||||
xmlAddChild(nodePtr, node.nodePtr)
|
||||
}
|
||||
|
||||
func removeChild(_ node: XMLElement) {
|
||||
|
||||
guard let node = node as? libxmlHTMLNode else {
|
||||
return
|
||||
}
|
||||
xmlUnlinkNode(node.nodePtr)
|
||||
xmlFree(node.nodePtr)
|
||||
}
|
||||
|
||||
private func node(from ptr: xmlNodePtr?) -> XMLElement? {
|
||||
guard let doc = self.doc, let docPtr = self.docPtr, let nodePtr = ptr else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let element = libxmlHTMLNode(document: doc, docPtr: docPtr, node: nodePtr)
|
||||
return element
|
||||
}
|
||||
}
|
||||
|
||||
private func libxmlGetNodeContent(_ nodePtr: xmlNodePtr) -> String? {
|
||||
let content = xmlNodeGetContent(nodePtr)
|
||||
defer {
|
||||
#if swift(>=4.1)
|
||||
content?.deallocate()
|
||||
#else
|
||||
content?.deallocate(capacity: 1)
|
||||
#endif
|
||||
}
|
||||
if let result = String(validatingUTF8: UnsafeRawPointer(content!).assumingMemoryBound(to: CChar.self)) {
|
||||
return result
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
let entities = [
|
||||
"&": "&",
|
||||
"<" : "<",
|
||||
">" : ">",
|
||||
]
|
||||
|
||||
private func escape(_ str: String) -> String {
|
||||
var newStr = str
|
||||
for (unesc, esc) in entities {
|
||||
newStr = newStr.replacingOccurrences(of: unesc, with: esc, options: .regularExpression, range: nil)
|
||||
}
|
||||
return newStr
|
||||
}
|
||||
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/**@file libxmlParserOption.swift
|
||||
|
||||
Kanna
|
||||
|
||||
Copyright (c) 2015 Atsushi Kiwaki (@_tid_)
|
||||
|
||||
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.
|
||||
*/
|
||||
import Foundation
|
||||
|
||||
import libxmlKanna
|
||||
|
||||
/*
|
||||
Libxml2HTMLParserOptions
|
||||
*/
|
||||
public struct Libxml2HTMLParserOptions : OptionSet {
|
||||
public typealias RawValue = UInt
|
||||
private var value: UInt = 0
|
||||
init(_ value: UInt) { self.value = value }
|
||||
private init(_ opt: htmlParserOption) { self.value = UInt(opt.rawValue) }
|
||||
public init(rawValue value: UInt) { self.value = value }
|
||||
public init(nilLiteral: ()) { self.value = 0 }
|
||||
public static var allZeros: Libxml2HTMLParserOptions { return .init(0) }
|
||||
static func fromMask(raw: UInt) -> Libxml2HTMLParserOptions { return .init(raw) }
|
||||
public var rawValue: UInt { return self.value }
|
||||
|
||||
public static let STRICT = Libxml2HTMLParserOptions(0)
|
||||
public static let RECOVER = Libxml2HTMLParserOptions(HTML_PARSE_RECOVER)
|
||||
public static let NODEFDTD = Libxml2HTMLParserOptions(HTML_PARSE_NODEFDTD)
|
||||
public static let NOERROR = Libxml2HTMLParserOptions(HTML_PARSE_NOERROR)
|
||||
public static let NOWARNING = Libxml2HTMLParserOptions(HTML_PARSE_NOWARNING)
|
||||
public static let PEDANTIC = Libxml2HTMLParserOptions(HTML_PARSE_PEDANTIC)
|
||||
public static let NOBLANKS = Libxml2HTMLParserOptions(HTML_PARSE_NOBLANKS)
|
||||
public static let NONET = Libxml2HTMLParserOptions(HTML_PARSE_NONET)
|
||||
public static let NOIMPLIED = Libxml2HTMLParserOptions(HTML_PARSE_NOIMPLIED)
|
||||
public static let COMPACT = Libxml2HTMLParserOptions(HTML_PARSE_COMPACT)
|
||||
public static let IGNORE_ENC = Libxml2HTMLParserOptions(HTML_PARSE_IGNORE_ENC)
|
||||
}
|
||||
|
||||
/*
|
||||
Libxml2XMLParserOptions
|
||||
*/
|
||||
public struct Libxml2XMLParserOptions: OptionSet {
|
||||
public typealias RawValue = UInt
|
||||
private var value: UInt = 0
|
||||
init(_ value: UInt) { self.value = value }
|
||||
private init(_ opt: xmlParserOption) { self.value = UInt(opt.rawValue) }
|
||||
public init(rawValue value: UInt) { self.value = value }
|
||||
public init(nilLiteral: ()) { self.value = 0 }
|
||||
public static var allZeros: Libxml2XMLParserOptions { return .init(0) }
|
||||
static func fromMask(raw: UInt) -> Libxml2XMLParserOptions { return .init(raw) }
|
||||
public var rawValue: UInt { return self.value }
|
||||
|
||||
public static let STRICT = Libxml2XMLParserOptions(0)
|
||||
public static let RECOVER = Libxml2XMLParserOptions(XML_PARSE_RECOVER)
|
||||
public static let NOENT = Libxml2XMLParserOptions(XML_PARSE_NOENT)
|
||||
public static let DTDLOAD = Libxml2XMLParserOptions(XML_PARSE_DTDLOAD)
|
||||
public static let DTDATTR = Libxml2XMLParserOptions(XML_PARSE_DTDATTR)
|
||||
public static let DTDVALID = Libxml2XMLParserOptions(XML_PARSE_DTDVALID)
|
||||
public static let NOERROR = Libxml2XMLParserOptions(XML_PARSE_NOERROR)
|
||||
public static let NOWARNING = Libxml2XMLParserOptions(XML_PARSE_NOWARNING)
|
||||
public static let PEDANTIC = Libxml2XMLParserOptions(XML_PARSE_PEDANTIC)
|
||||
public static let NOBLANKS = Libxml2XMLParserOptions(XML_PARSE_NOBLANKS)
|
||||
public static let SAX1 = Libxml2XMLParserOptions(XML_PARSE_SAX1)
|
||||
public static let XINCLUDE = Libxml2XMLParserOptions(XML_PARSE_XINCLUDE)
|
||||
public static let NONET = Libxml2XMLParserOptions(XML_PARSE_NONET)
|
||||
public static let NODICT = Libxml2XMLParserOptions(XML_PARSE_NODICT)
|
||||
public static let NSCLEAN = Libxml2XMLParserOptions(XML_PARSE_NSCLEAN)
|
||||
public static let NOCDATA = Libxml2XMLParserOptions(XML_PARSE_NOCDATA)
|
||||
public static let NOXINCNODE = Libxml2XMLParserOptions(XML_PARSE_NOXINCNODE)
|
||||
public static let COMPACT = Libxml2XMLParserOptions(XML_PARSE_COMPACT)
|
||||
public static let OLD10 = Libxml2XMLParserOptions(XML_PARSE_OLD10)
|
||||
public static let NOBASEFIX = Libxml2XMLParserOptions(XML_PARSE_NOBASEFIX)
|
||||
public static let HUGE = Libxml2XMLParserOptions(XML_PARSE_HUGE)
|
||||
public static let OLDSAX = Libxml2XMLParserOptions(XML_PARSE_OLDSAX)
|
||||
public static let IGNORE_ENC = Libxml2XMLParserOptions(XML_PARSE_IGNORE_ENC)
|
||||
public static let BIG_LINES = Libxml2XMLParserOptions(XML_PARSE_BIG_LINES)
|
||||
}
|
||||
Reference in New Issue
Block a user