* Removed StringBuilder from Element.cssSelector
* Lint Code * Swift 4.1
This commit is contained in:
parent
306be6efbe
commit
d98300a7ec
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -1,10 +1,20 @@
|
||||||
# Change Log
|
# Change Log
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## [1.6.3](https://github.com/scinfu/SwiftSoup/tree/1.6.4)
|
## [1.6.6](https://github.com/scinfu/SwiftSoup/tree/1.6.6)
|
||||||
|
* Removed StringBuilder from Element.cssSelector
|
||||||
|
* Lint Code
|
||||||
|
* Swift 4.1
|
||||||
|
|
||||||
|
## [1.6.5](https://github.com/scinfu/SwiftSoup/tree/1.6.5)
|
||||||
|
* Removed StringBuilder from Element.cssSelector
|
||||||
|
* Lint Code
|
||||||
|
*
|
||||||
|
|
||||||
|
## [1.6.4](https://github.com/scinfu/SwiftSoup/tree/1.6.4)
|
||||||
* Add newer simulators to targeted devices to build with Carthage [tvOS]
|
* Add newer simulators to targeted devices to build with Carthage [tvOS]
|
||||||
|
|
||||||
## [1.6.3](https://github.com/scinfu/SwiftSoup/tree/1.6.3)
|
## [1.6.3](https://github.com/scinfu/SwiftSoup/tree/1.6.3)
|
||||||
|
|
||||||
* Add newer tvOS simulators to targeted devices to build with Carthage.
|
* Add newer tvOS simulators to targeted devices to build with Carthage.
|
||||||
* Add newer watchOS simulators to targeted devices to build with Carthage.
|
* Add newer watchOS simulators to targeted devices to build with Carthage.
|
||||||
|
|
|
@ -13,7 +13,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
|
|
||||||
var window: UIWindow?
|
var window: UIWindow?
|
||||||
|
|
||||||
|
|
||||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
|
||||||
// Override point for customization after application launch.
|
// Override point for customization after application launch.
|
||||||
return true
|
return true
|
||||||
|
@ -41,6 +40,4 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,14 +12,13 @@ class QueryViewControllerCell: UITableViewCell {
|
||||||
@IBOutlet weak var selector: UILabel!
|
@IBOutlet weak var selector: UILabel!
|
||||||
@IBOutlet weak var example: UILabel!
|
@IBOutlet weak var example: UILabel!
|
||||||
@IBOutlet weak var descriptionLabel: UILabel!
|
@IBOutlet weak var descriptionLabel: UILabel!
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
class QueryViewController: UIViewController {
|
class QueryViewController: UIViewController {
|
||||||
|
|
||||||
typealias Item = (selector: String,example: String, description: String)
|
typealias Item = (selector: String, example: String, description: String)
|
||||||
|
|
||||||
//example items
|
//example items
|
||||||
let items: [
|
let items: [
|
||||||
Item] = [ Item(selector: "*", example: "*", description: "any element"),
|
Item] = [ Item(selector: "*", example: "*", description: "any element"),
|
||||||
|
@ -36,51 +35,47 @@ class QueryViewController: UIViewController {
|
||||||
Item(selector: "[attribute^=value]", example: "a[href^=https]", description: "Selects every <a> element whose href attribute value begins with \"https\""),
|
Item(selector: "[attribute^=value]", example: "a[href^=https]", description: "Selects every <a> element whose href attribute value begins with \"https\""),
|
||||||
Item(selector: "[attribute$=value]", example: "a[href$=.com/]", description: "Selects every <a> element whose href attribute value ends with \".com/\""),
|
Item(selector: "[attribute$=value]", example: "a[href$=.com/]", description: "Selects every <a> element whose href attribute value ends with \".com/\""),
|
||||||
Item(selector: "[attribute*=value]", example: "a[href*=login]", description: "Selects every <a> element whose href attribute value contains the substring \"login\""),
|
Item(selector: "[attribute*=value]", example: "a[href*=login]", description: "Selects every <a> element whose href attribute value contains the substring \"login\""),
|
||||||
Item(selector: "[attr~=regex]", example: "img[src~=[gif]]", description: "elements with an attribute named \"img\", and value matching the regular expression"),
|
Item(selector: "[attr~=regex]", example: "img[src~=[gif]]", description: "elements with an attribute named \"img\", and value matching the regular expression")
|
||||||
]
|
]
|
||||||
|
|
||||||
var completionHandler: (Item)->Void = { arg in }
|
var completionHandler: (Item) -> Void = { arg in }
|
||||||
@IBOutlet weak var tableView: UITableView!
|
@IBOutlet weak var tableView: UITableView!
|
||||||
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
self.title = ""
|
self.title = ""
|
||||||
|
|
||||||
self.tableView.rowHeight = UITableViewAutomaticDimension;
|
self.tableView.rowHeight = UITableViewAutomaticDimension
|
||||||
self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;
|
self.tableView.estimatedRowHeight = UITableViewAutomaticDimension
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension QueryViewController: UITableViewDataSource
|
extension QueryViewController: UITableViewDataSource {
|
||||||
{
|
|
||||||
func numberOfSections(in tableView: UITableView) -> Int {
|
func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
|
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
return items.count
|
return items.count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
|
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: "QueryViewControllerCell", for: indexPath) as! QueryViewControllerCell
|
let cell = tableView.dequeueReusableCell(withIdentifier: "QueryViewControllerCell", for: indexPath) as! QueryViewControllerCell
|
||||||
|
|
||||||
cell.selector.text = items[indexPath.row].selector
|
cell.selector.text = items[indexPath.row].selector
|
||||||
cell.example.text = items[indexPath.row].example
|
cell.example.text = items[indexPath.row].example
|
||||||
cell.descriptionLabel.text = items[indexPath.row].description
|
cell.descriptionLabel.text = items[indexPath.row].description
|
||||||
|
|
||||||
let color1 = UIColor.init(red: 245.0/255, green: 245.0/255, blue: 245.0/255, alpha: 1)
|
let color1 = UIColor.init(red: 245.0/255, green: 245.0/255, blue: 245.0/255, alpha: 1)
|
||||||
let color2 = UIColor.init(red: 240.0/255, green: 240.0/255, blue: 240.0/255, alpha: 1)
|
let color2 = UIColor.init(red: 240.0/255, green: 240.0/255, blue: 240.0/255, alpha: 1)
|
||||||
cell.backgroundColor = (indexPath.row % 2) == 0 ? color1 : color2
|
cell.backgroundColor = (indexPath.row % 2) == 0 ? color1 : color2
|
||||||
|
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension QueryViewController: UITableViewDelegate
|
extension QueryViewController: UITableViewDelegate {
|
||||||
{
|
|
||||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
// user select an item
|
// user select an item
|
||||||
completionHandler(items[indexPath.row])
|
completionHandler(items[indexPath.row])
|
||||||
|
|
|
@ -10,43 +10,42 @@ import UIKit
|
||||||
import SwiftSoup
|
import SwiftSoup
|
||||||
|
|
||||||
class ViewController: UIViewController {
|
class ViewController: UIViewController {
|
||||||
|
|
||||||
typealias Item = (text: String, html: String)
|
typealias Item = (text: String, html: String)
|
||||||
|
|
||||||
@IBOutlet weak var tableView: UITableView!
|
@IBOutlet weak var tableView: UITableView!
|
||||||
@IBOutlet var urlTextField: UITextField!
|
@IBOutlet var urlTextField: UITextField!
|
||||||
@IBOutlet var cssTextField: UITextField!
|
@IBOutlet var cssTextField: UITextField!
|
||||||
|
|
||||||
// current document
|
// current document
|
||||||
var document: Document = Document.init("")
|
var document: Document = Document.init("")
|
||||||
// item founds
|
// item founds
|
||||||
var items: [Item] = []
|
var items: [Item] = []
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
self.title = "SwiftSoup Example"
|
self.title = "SwiftSoup Example"
|
||||||
|
|
||||||
self.tableView.rowHeight = UITableViewAutomaticDimension;
|
self.tableView.rowHeight = UITableViewAutomaticDimension
|
||||||
self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;
|
self.tableView.estimatedRowHeight = UITableViewAutomaticDimension
|
||||||
|
|
||||||
urlTextField.text = "http://www.facebook.com"
|
urlTextField.text = "http://www.facebook.com"
|
||||||
cssTextField.text = "div"
|
cssTextField.text = "div"
|
||||||
|
|
||||||
// start first request
|
// start first request
|
||||||
downloadHTML()
|
downloadHTML()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//Download HTML
|
//Download HTML
|
||||||
func downloadHTML(){
|
func downloadHTML() {
|
||||||
// url string to URL
|
// url string to URL
|
||||||
guard let url = URL(string: urlTextField.text ?? "") else {
|
guard let url = URL(string: urlTextField.text ?? "") else {
|
||||||
// an error occurred
|
// an error occurred
|
||||||
UIAlertController.showAlert("Error: \(urlTextField.text ?? "") doesn't seem to be a valid URL", self)
|
UIAlertController.showAlert("Error: \(urlTextField.text ?? "") doesn't seem to be a valid URL", self)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// content of url
|
// content of url
|
||||||
let html = try String.init(contentsOf: url)
|
let html = try String.init(contentsOf: url)
|
||||||
|
@ -58,32 +57,32 @@ class ViewController: UIViewController {
|
||||||
// an error occurred
|
// an error occurred
|
||||||
UIAlertController.showAlert("Error: \(error)", self)
|
UIAlertController.showAlert("Error: \(error)", self)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Parse CSS selector
|
//Parse CSS selector
|
||||||
func parse(){
|
func parse() {
|
||||||
do {
|
do {
|
||||||
//empty old items
|
//empty old items
|
||||||
items = []
|
items = []
|
||||||
// firn css selector
|
// firn css selector
|
||||||
let elements: Elements = try document.select(cssTextField.text ?? "")
|
let elements: Elements = try document.select(cssTextField.text ?? "")
|
||||||
//transform it into a local object (Item)
|
//transform it into a local object (Item)
|
||||||
for element in elements{
|
for element in elements {
|
||||||
let text = try element.text()
|
let text = try element.text()
|
||||||
let html = try element.outerHtml()
|
let html = try element.outerHtml()
|
||||||
items.append(Item(text: text, html: html))
|
items.append(Item(text: text, html: html))
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch let error {
|
} catch let error {
|
||||||
UIAlertController.showAlert("Error: \(error)", self)
|
UIAlertController.showAlert("Error: \(error)", self)
|
||||||
}
|
}
|
||||||
|
|
||||||
tableView.reloadData()
|
tableView.reloadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func chooseQuery(_ sender: Any) {
|
@IBAction func chooseQuery(_ sender: Any) {
|
||||||
guard let vc = storyboard?.instantiateViewController(withIdentifier: "QueryViewController") as? QueryViewController else{
|
guard let vc = storyboard?.instantiateViewController(withIdentifier: "QueryViewController") as? QueryViewController else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
vc.completionHandler = {[weak self](resilt) in
|
vc.completionHandler = {[weak self](resilt) in
|
||||||
|
@ -93,78 +92,67 @@ class ViewController: UIViewController {
|
||||||
}
|
}
|
||||||
self.show(vc, sender: self)
|
self.show(vc, sender: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ViewController: UITableViewDataSource
|
extension ViewController: UITableViewDataSource {
|
||||||
{
|
|
||||||
func numberOfSections(in tableView: UITableView) -> Int {
|
func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
|
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
return items.count
|
return items.count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
|
|
||||||
var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
|
var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
|
||||||
if cell == nil {
|
if cell == nil {
|
||||||
cell = UITableViewCell.init(style: UITableViewCellStyle.subtitle , reuseIdentifier: "cell")
|
cell = UITableViewCell.init(style: UITableViewCellStyle.subtitle, reuseIdentifier: "cell")
|
||||||
cell?.textLabel?.numberOfLines = 2
|
cell?.textLabel?.numberOfLines = 2
|
||||||
cell?.detailTextLabel?.numberOfLines = 6
|
cell?.detailTextLabel?.numberOfLines = 6
|
||||||
|
|
||||||
cell?.textLabel?.textColor = UIColor.init(red: 1.0/255, green: 174.0/255, blue: 66.0/255, alpha: 1)
|
cell?.textLabel?.textColor = UIColor.init(red: 1.0/255, green: 174.0/255, blue: 66.0/255, alpha: 1)
|
||||||
cell?.detailTextLabel?.textColor = UIColor.init(red: 55.0/255, green: 67.0/255, blue: 55.0/255, alpha: 1)
|
cell?.detailTextLabel?.textColor = UIColor.init(red: 55.0/255, green: 67.0/255, blue: 55.0/255, alpha: 1)
|
||||||
|
|
||||||
cell?.backgroundColor = UIColor.init(red: 245.0/255, green: 245.0/255, blue: 245.0/255, alpha: 1)
|
cell?.backgroundColor = UIColor.init(red: 245.0/255, green: 245.0/255, blue: 245.0/255, alpha: 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
cell?.textLabel?.text = items[indexPath.row].text
|
cell?.textLabel?.text = items[indexPath.row].text
|
||||||
cell?.detailTextLabel?.text = items[indexPath.row].html
|
cell?.detailTextLabel?.text = items[indexPath.row].html
|
||||||
|
|
||||||
let color1 = UIColor.init(red: 245.0/255, green: 245.0/255, blue: 245.0/255, alpha: 1)
|
let color1 = UIColor.init(red: 245.0/255, green: 245.0/255, blue: 245.0/255, alpha: 1)
|
||||||
let color2 = UIColor.init(red: 240.0/255, green: 240.0/255, blue: 240.0/255, alpha: 1)
|
let color2 = UIColor.init(red: 240.0/255, green: 240.0/255, blue: 240.0/255, alpha: 1)
|
||||||
cell?.backgroundColor = (indexPath.row % 2) == 0 ? color1 : color2
|
cell?.backgroundColor = (indexPath.row % 2) == 0 ? color1 : color2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return cell!
|
return cell!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ViewController: UITableViewDelegate
|
extension ViewController: UITableViewDelegate {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ViewController: UITextFieldDelegate
|
extension ViewController: UITextFieldDelegate {
|
||||||
{
|
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||||
public func textFieldShouldReturn(_ textField: UITextField) -> Bool{
|
|
||||||
textField.resignFirstResponder()
|
textField.resignFirstResponder()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
public func textFieldDidEndEditing(_ textField: UITextField) {
|
public func textFieldDidEndEditing(_ textField: UITextField) {
|
||||||
|
|
||||||
if textField == urlTextField {
|
if textField == urlTextField {
|
||||||
downloadHTML()
|
downloadHTML()
|
||||||
}
|
}
|
||||||
|
|
||||||
if textField == cssTextField {
|
if textField == cssTextField {
|
||||||
parse()
|
parse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extension UIAlertController {
|
extension UIAlertController {
|
||||||
static public func showAlert(_ message: String, _ controller: UIViewController){
|
static public func showAlert(_ message: String, _ controller: UIViewController) {
|
||||||
let alert = UIAlertController(title: "Alert", message: message, preferredStyle: UIAlertControllerStyle.alert)
|
let alert = UIAlertController(title: "Alert", message: message, preferredStyle: UIAlertControllerStyle.alert)
|
||||||
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
|
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
|
||||||
controller.present(alert, animated: true, completion: nil)
|
controller.present(alert, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,10 @@ import PackageDescription
|
||||||
let package = Package(
|
let package = Package(
|
||||||
name: "SwiftSoup",
|
name: "SwiftSoup",
|
||||||
products: [
|
products: [
|
||||||
.library(name: "SwiftSoup", targets: ["SwiftSoup"]),
|
.library(name: "SwiftSoup", targets: ["SwiftSoup"])
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.target(name: "SwiftSoup", path: "Sources"),
|
.target(name: "SwiftSoup", path: "Sources"),
|
||||||
.testTarget(name: "SwiftSoupTests", dependencies: ["SwiftSoup"]),
|
.testTarget(name: "SwiftSoupTests", dependencies: ["SwiftSoup"])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -140,7 +140,7 @@ open class Attribute {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Attribute : Equatable {
|
extension Attribute: Equatable {
|
||||||
static public func == (lhs: Attribute, rhs: Attribute) -> Bool {
|
static public func == (lhs: Attribute, rhs: Attribute) -> Bool {
|
||||||
return lhs.value == rhs.value && lhs.key == rhs.key
|
return lhs.value == rhs.value && lhs.key == rhs.key
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ open class Attributes: NSCopying {
|
||||||
@see #hasKey(String)
|
@see #hasKey(String)
|
||||||
*/
|
*/
|
||||||
open func get(key: String) -> String {
|
open func get(key: String) -> String {
|
||||||
let attr: Attribute? = attributes.get(key:key)
|
let attr: Attribute? = attributes.get(key: key)
|
||||||
return attr != nil ? attr!.getValue() : ""
|
return attr != nil ? attr!.getValue() : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ open class Attributes: NSCopying {
|
||||||
@param attribute attribute
|
@param attribute attribute
|
||||||
*/
|
*/
|
||||||
open func put(attribute: Attribute) {
|
open func put(attribute: Attribute) {
|
||||||
attributes.put(value: attribute, forKey:attribute.getKey())
|
attributes.put(value: attribute, forKey: attribute.getKey())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -254,7 +254,7 @@ open class Attributes: NSCopying {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Attributes : Sequence {
|
extension Attributes: Sequence {
|
||||||
public func makeIterator() -> AnyIterator<Attribute> {
|
public func makeIterator() -> AnyIterator<Attribute> {
|
||||||
var list = attributes.orderedValues
|
var list = attributes.orderedValues
|
||||||
return AnyIterator {
|
return AnyIterator {
|
||||||
|
|
|
@ -21,10 +21,10 @@ extension Character {
|
||||||
public static let MIN_SUPPLEMENTARY_CODE_POINT: UInt32 = 0x010000
|
public static let MIN_SUPPLEMENTARY_CODE_POINT: UInt32 = 0x010000
|
||||||
|
|
||||||
/// True for any space character, and the control characters \t, \n, \r, \f, \v.
|
/// True for any space character, and the control characters \t, \n, \r, \f, \v.
|
||||||
|
|
||||||
var isWhitespace: Bool {
|
var isWhitespace: Bool {
|
||||||
switch self {
|
switch self {
|
||||||
case Character.space, Character.BackslashT, Character.BackslashN,Character.BackslashF,Character.BackslashR: return true
|
case Character.space, Character.BackslashT, Character.BackslashN, Character.BackslashF, Character.BackslashR: return true
|
||||||
case Character.BackshashRBackslashN: return true
|
case Character.BackshashRBackslashN: return true
|
||||||
default: return false
|
default: return false
|
||||||
|
|
||||||
|
|
|
@ -169,7 +169,7 @@ public final class CharacterReader {
|
||||||
let val = input
|
let val = input
|
||||||
|
|
||||||
while (pos < remaining) {
|
while (pos < remaining) {
|
||||||
|
|
||||||
if chars.contains(val[pos]) {
|
if chars.contains(val[pos]) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,7 +118,7 @@ extension Cleaner {
|
||||||
//let sourceData: DataNode = (DataNode) source
|
//let sourceData: DataNode = (DataNode) source
|
||||||
let destData: DataNode = DataNode(sourceData.getWholeData(), source.getBaseUri())
|
let destData: DataNode = DataNode(sourceData.getWholeData(), source.getBaseUri())
|
||||||
try destination?.appendChild(destData)
|
try destination?.appendChild(destData)
|
||||||
}else{
|
} else {
|
||||||
numDiscarded+=1
|
numDiscarded+=1
|
||||||
}
|
}
|
||||||
} else { // else, we don't care about comments, xml proc instructions, etc
|
} else { // else, we don't care about comments, xml proc instructions, etc
|
||||||
|
|
|
@ -41,7 +41,7 @@ private final class Accumulator: NodeVisitor {
|
||||||
self.elements = elements
|
self.elements = elements
|
||||||
self.eval = eval
|
self.eval = eval
|
||||||
}
|
}
|
||||||
|
|
||||||
open func head(_ node: Node, _ depth: Int) {
|
open func head(_ node: Node, _ depth: Int) {
|
||||||
guard let el = node as? Element else {
|
guard let el = node as? Element else {
|
||||||
return
|
return
|
||||||
|
|
|
@ -26,7 +26,7 @@ public class CombiningEvaluator: Evaluator {
|
||||||
super.init()
|
super.init()
|
||||||
updateNumEvaluators()
|
updateNumEvaluators()
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(_ evaluators: Evaluator...) {
|
public init(_ evaluators: Evaluator...) {
|
||||||
self.evaluators = evaluators
|
self.evaluators = evaluators
|
||||||
super.init()
|
super.init()
|
||||||
|
|
|
@ -13,7 +13,7 @@ import Foundation
|
||||||
*/
|
*/
|
||||||
public class DocumentType: Node {
|
public class DocumentType: Node {
|
||||||
static let PUBLIC_KEY: String = "PUBLIC"
|
static let PUBLIC_KEY: String = "PUBLIC"
|
||||||
static let SYSTEM_KEY: String = "SYSTEM";
|
static let SYSTEM_KEY: String = "SYSTEM"
|
||||||
private static let NAME: String = "name"
|
private static let NAME: String = "name"
|
||||||
private static let PUB_SYS_KEY: String = "pubSysKey"; // PUBLIC or SYSTEM
|
private static let PUB_SYS_KEY: String = "pubSysKey"; // PUBLIC or SYSTEM
|
||||||
private static let PUBLIC_ID: String = "publicId"
|
private static let PUBLIC_ID: String = "publicId"
|
||||||
|
@ -30,15 +30,15 @@ public class DocumentType: Node {
|
||||||
public init(_ name: String, _ publicId: String, _ systemId: String, _ baseUri: String) {
|
public init(_ name: String, _ publicId: String, _ systemId: String, _ baseUri: String) {
|
||||||
super.init(baseUri)
|
super.init(baseUri)
|
||||||
do {
|
do {
|
||||||
try attr(DocumentType.NAME, name);
|
try attr(DocumentType.NAME, name)
|
||||||
try attr(DocumentType.PUBLIC_ID, publicId);
|
try attr(DocumentType.PUBLIC_ID, publicId)
|
||||||
if (has(DocumentType.PUBLIC_ID)) {
|
if (has(DocumentType.PUBLIC_ID)) {
|
||||||
try attr(DocumentType.PUB_SYS_KEY, DocumentType.PUBLIC_KEY);
|
try attr(DocumentType.PUB_SYS_KEY, DocumentType.PUBLIC_KEY)
|
||||||
}
|
}
|
||||||
try attr(DocumentType.SYSTEM_ID, systemId);
|
try attr(DocumentType.SYSTEM_ID, systemId)
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new doctype element.
|
* Create a new doctype element.
|
||||||
* @param name the doctype's name
|
* @param name the doctype's name
|
||||||
|
@ -50,17 +50,13 @@ public class DocumentType: Node {
|
||||||
super.init(baseUri)
|
super.init(baseUri)
|
||||||
do {
|
do {
|
||||||
try attr(DocumentType.NAME, name)
|
try attr(DocumentType.NAME, name)
|
||||||
if(pubSysKey != nil){
|
if(pubSysKey != nil) {
|
||||||
try attr(DocumentType.PUB_SYS_KEY, pubSysKey!)
|
try attr(DocumentType.PUB_SYS_KEY, pubSysKey!)
|
||||||
}
|
}
|
||||||
try attr(DocumentType.PUBLIC_ID, publicId);
|
try attr(DocumentType.PUBLIC_ID, publicId)
|
||||||
try attr(DocumentType.SYSTEM_ID, systemId);
|
try attr(DocumentType.SYSTEM_ID, systemId)
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public override func nodeName() -> String {
|
public override func nodeName() -> String {
|
||||||
return "#doctype"
|
return "#doctype"
|
||||||
|
@ -79,16 +75,16 @@ public class DocumentType: Node {
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has(DocumentType.PUB_SYS_KEY)){
|
if (has(DocumentType.PUB_SYS_KEY)) {
|
||||||
do {
|
do {
|
||||||
try accum.append(" ").append(attr(DocumentType.PUB_SYS_KEY))
|
try accum.append(" ").append(attr(DocumentType.PUB_SYS_KEY))
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has(DocumentType.PUBLIC_ID)) {
|
if (has(DocumentType.PUBLIC_ID)) {
|
||||||
do {
|
do {
|
||||||
try accum.append(" \"").append(attr(DocumentType.PUBLIC_ID)).append("\"");
|
try accum.append(" \"").append(attr(DocumentType.PUBLIC_ID)).append("\"")
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,12 @@ import Foundation
|
||||||
|
|
||||||
open class Element: Node {
|
open class Element: Node {
|
||||||
var _tag: Tag
|
var _tag: Tag
|
||||||
|
|
||||||
private static let classString = "class"
|
private static let classString = "class"
|
||||||
private static let emptyString = ""
|
private static let emptyString = ""
|
||||||
private static let idString = "id"
|
private static let idString = "id"
|
||||||
private static let rootString = "#root"
|
private static let rootString = "#root"
|
||||||
|
|
||||||
//private static let classSplit : Pattern = Pattern("\\s+")
|
//private static let classSplit : Pattern = Pattern("\\s+")
|
||||||
private static let classSplit = "\\s+"
|
private static let classSplit = "\\s+"
|
||||||
|
|
||||||
|
@ -276,16 +276,16 @@ open class Element: Node {
|
||||||
public func select(_ cssQuery: String)throws->Elements {
|
public func select(_ cssQuery: String)throws->Elements {
|
||||||
return try Selector.select(cssQuery, self)
|
return try Selector.select(cssQuery, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this element matches the given {@link Selector} CSS query.
|
* Check if this element matches the given {@link Selector} CSS query.
|
||||||
* @param cssQuery a {@link Selector} CSS query
|
* @param cssQuery a {@link Selector} CSS query
|
||||||
* @return if this element matches the query
|
* @return if this element matches the query
|
||||||
*/
|
*/
|
||||||
public func iS(_ cssQuery: String)throws->Bool {
|
public func iS(_ cssQuery: String)throws->Bool {
|
||||||
return try iS(QueryParser.parse(cssQuery));
|
return try iS(QueryParser.parse(cssQuery))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this element matches the given {@link Selector} CSS query.
|
* Check if this element matches the given {@link Selector} CSS query.
|
||||||
* @param cssQuery a {@link Selector} CSS query
|
* @param cssQuery a {@link Selector} CSS query
|
||||||
|
@ -293,15 +293,11 @@ open class Element: Node {
|
||||||
*/
|
*/
|
||||||
public func iS(_ evaluator: Evaluator)throws->Bool {
|
public func iS(_ evaluator: Evaluator)throws->Bool {
|
||||||
guard let od = self.ownerDocument() else {
|
guard let od = self.ownerDocument() else {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
return try evaluator.matches(od, self);
|
return try evaluator.matches(od, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a node child node to this element.
|
* Add a node child node to this element.
|
||||||
*
|
*
|
||||||
|
@ -512,7 +508,7 @@ open class Element: Node {
|
||||||
if (elementId.count > 0) {
|
if (elementId.count > 0) {
|
||||||
return "#" + elementId
|
return "#" + elementId
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translate HTML namespace ns:tag to CSS namespace syntax ns|tag
|
// Translate HTML namespace ns:tag to CSS namespace syntax ns|tag
|
||||||
let tagName: String = self.tagName().replacingOccurrences(of: ":", with: "|")
|
let tagName: String = self.tagName().replacingOccurrences(of: ":", with: "|")
|
||||||
var selector: String = tagName
|
var selector: String = tagName
|
||||||
|
@ -522,17 +518,17 @@ open class Element: Node {
|
||||||
selector.append(".")
|
selector.append(".")
|
||||||
selector.append(classes)
|
selector.append(classes)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parent() == nil || ((parent() as? Document) != nil)) // don't add Document to selector, as will always have a html node
|
if (parent() == nil || ((parent() as? Document) != nil)) // don't add Document to selector, as will always have a html node
|
||||||
{
|
{
|
||||||
return selector
|
return selector
|
||||||
}
|
}
|
||||||
|
|
||||||
selector.insert(contentsOf: " > ", at: selector.startIndex)
|
selector.insert(contentsOf: " > ", at: selector.startIndex)
|
||||||
if (try parent()!.select(selector).array().count > 1) {
|
if (try parent()!.select(selector).array().count > 1) {
|
||||||
selector.append(":nth-child(\(try elementSiblingIndex() + 1))")
|
selector.append(":nth-child(\(try elementSiblingIndex() + 1))")
|
||||||
}
|
}
|
||||||
|
|
||||||
return try parent()!.cssSelector() + (selector)
|
return try parent()!.cssSelector() + (selector)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1065,9 +1061,9 @@ open class Element: Node {
|
||||||
* @return set of classnames, empty if no class attribute
|
* @return set of classnames, empty if no class attribute
|
||||||
*/
|
*/
|
||||||
public func classNames()throws->OrderedSet<String> {
|
public func classNames()throws->OrderedSet<String> {
|
||||||
let fitted = try className().replaceAll(of: Element.classSplit, with: " ", options:.caseInsensitive)
|
let fitted = try className().replaceAll(of: Element.classSplit, with: " ", options: .caseInsensitive)
|
||||||
let names: [String] = fitted.components(separatedBy: " ")
|
let names: [String] = fitted.components(separatedBy: " ")
|
||||||
let classNames: OrderedSet<String> = OrderedSet(sequence:names)
|
let classNames: OrderedSet<String> = OrderedSet(sequence: names)
|
||||||
classNames.remove(Element.emptyString) // if classNames() was empty, would include an empty class
|
classNames.remove(Element.emptyString) // if classNames() was empty, would include an empty class
|
||||||
return classNames
|
return classNames
|
||||||
}
|
}
|
||||||
|
|
|
@ -459,14 +459,14 @@ open class Elements: NSCopying {
|
||||||
* @return true if at least one element in the list matches the query.
|
* @return true if at least one element in the list matches the query.
|
||||||
*/
|
*/
|
||||||
open func iS(_ query: String)throws->Bool {
|
open func iS(_ query: String)throws->Bool {
|
||||||
let eval: Evaluator = try QueryParser.parse(query);
|
let eval: Evaluator = try QueryParser.parse(query)
|
||||||
for e: Element in this {
|
for e: Element in this {
|
||||||
if (try e.iS(eval)){
|
if (try e.iS(eval)) {
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -591,16 +591,16 @@ public struct ElementsIterator: IteratorProtocol {
|
||||||
let elements: Elements
|
let elements: Elements
|
||||||
//current element index
|
//current element index
|
||||||
var index = 0
|
var index = 0
|
||||||
|
|
||||||
/// Initializer
|
/// Initializer
|
||||||
init(_ countdown: Elements) {
|
init(_ countdown: Elements) {
|
||||||
self.elements = countdown
|
self.elements = countdown
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advances to the next element and returns it, or `nil` if no next element
|
/// Advances to the next element and returns it, or `nil` if no next element
|
||||||
mutating public func next() -> Element? {
|
mutating public func next() -> Element? {
|
||||||
let result = index < elements.size() ? elements.get(index) : nil
|
let result = index < elements.size() ? elements.get(index) : nil
|
||||||
index += 1;
|
index += 1
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -614,6 +614,3 @@ extension Elements: Sequence {
|
||||||
return ElementsIterator(self)
|
return ElementsIterator(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -278,7 +278,7 @@ public class Evaluator {
|
||||||
open override func matches(_ root: Element, _ element: Element)throws->Bool {
|
open override func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||||
if(element.hasAttr(key)) {
|
if(element.hasAttr(key)) {
|
||||||
let s = try element.attr(key)
|
let s = try element.attr(key)
|
||||||
return pattern.matcher(in:s).find()
|
return pattern.matcher(in: s).find()
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ class HtmlTreeBuilder: TreeBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
root = try Element(Tag.valueOf("html", settings), baseUri)
|
root = try Element(Tag.valueOf("html", settings), baseUri)
|
||||||
try Validate.notNull(obj:root)
|
try Validate.notNull(obj: root)
|
||||||
try doc.appendChild(root!)
|
try doc.appendChild(root!)
|
||||||
stack.append(root!)
|
stack.append(root!)
|
||||||
resetInsertionMode()
|
resetInsertionMode()
|
||||||
|
@ -418,12 +418,12 @@ class HtmlTreeBuilder: TreeBuilder {
|
||||||
queue[i] = input
|
queue[i] = input
|
||||||
return queue
|
return queue
|
||||||
}
|
}
|
||||||
|
|
||||||
private func replaceInQueue(_ queue: Array<Element?>, _ out: Element, _ input: Element)throws->Array<Element?> {
|
private func replaceInQueue(_ queue: Array<Element?>, _ out: Element, _ input: Element)throws->Array<Element?> {
|
||||||
var queue = queue
|
var queue = queue
|
||||||
var i: Int = -1
|
var i: Int = -1
|
||||||
for index in 0..<queue.count{
|
for index in 0..<queue.count {
|
||||||
if(out == queue[index]){
|
if(out == queue[index]) {
|
||||||
i = index
|
i = index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol {
|
||||||
// todo: charset switches
|
// todo: charset switches
|
||||||
} else if (name.equals("title")) {
|
} else if (name.equals("title")) {
|
||||||
try HtmlTreeBuilderState.handleRcData(start, tb)
|
try HtmlTreeBuilderState.handleRcData(start, tb)
|
||||||
} else if (StringUtil.inString(name, haystack:"noframes", "style")) {
|
} else if (StringUtil.inString(name, haystack: "noframes", "style")) {
|
||||||
try HtmlTreeBuilderState.handleRawtext(start, tb)
|
try HtmlTreeBuilderState.handleRawtext(start, tb)
|
||||||
} else if (name.equals("noscript")) {
|
} else if (name.equals("noscript")) {
|
||||||
// else if noscript && scripting flag = true: rawtext (jsoup doesn't run script, to handle as noscript)
|
// else if noscript && scripting flag = true: rawtext (jsoup doesn't run script, to handle as noscript)
|
||||||
|
@ -179,7 +179,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol {
|
||||||
if (name?.equals("head"))! {
|
if (name?.equals("head"))! {
|
||||||
tb.pop()
|
tb.pop()
|
||||||
tb.transition(.AfterHead)
|
tb.transition(.AfterHead)
|
||||||
} else if (name != nil && StringUtil.inString(name!, haystack:"body", "html", "br")) {
|
} else if (name != nil && StringUtil.inString(name!, haystack: "body", "html", "br")) {
|
||||||
return try anythingElse(t, tb)
|
return try anythingElse(t, tb)
|
||||||
} else {
|
} else {
|
||||||
tb.error(self)
|
tb.error(self)
|
||||||
|
|
|
@ -560,7 +560,7 @@ open class Node: Equatable, Hashable {
|
||||||
@return next sibling, or null if this is the last sibling
|
@return next sibling, or null if this is the last sibling
|
||||||
*/
|
*/
|
||||||
open func nextSibling() -> Node? {
|
open func nextSibling() -> Node? {
|
||||||
guard let siblings: Array<Node> = parentNode?.childNodes else{
|
guard let siblings: Array<Node> = parentNode?.childNodes else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -708,7 +708,7 @@ open class Node: Equatable, Hashable {
|
||||||
let currParent: Node = nodesToProcess.removeFirst()
|
let currParent: Node = nodesToProcess.removeFirst()
|
||||||
|
|
||||||
for i in 0..<currParent.childNodes.count {
|
for i in 0..<currParent.childNodes.count {
|
||||||
let childClone: Node = currParent.childNodes[i].copy(parent:currParent)
|
let childClone: Node = currParent.childNodes[i].copy(parent: currParent)
|
||||||
currParent.childNodes[i] = childClone
|
currParent.childNodes[i] = childClone
|
||||||
nodesToProcess.append(childClone)
|
nodesToProcess.append(childClone)
|
||||||
}
|
}
|
||||||
|
@ -778,7 +778,7 @@ open class Node: Equatable, Hashable {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Node : CustomStringConvertible {
|
extension Node: CustomStringConvertible {
|
||||||
public var description: String {
|
public var description: String {
|
||||||
do {
|
do {
|
||||||
return try outerHtml()
|
return try outerHtml()
|
||||||
|
@ -789,7 +789,7 @@ extension Node : CustomStringConvertible {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Node : CustomDebugStringConvertible {
|
extension Node: CustomDebugStringConvertible {
|
||||||
private static let space = " "
|
private static let space = " "
|
||||||
public var debugDescription: String {
|
public var debugDescription: String {
|
||||||
do {
|
do {
|
||||||
|
|
|
@ -74,7 +74,7 @@ public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollecti
|
||||||
}
|
}
|
||||||
|
|
||||||
public var orderedValues: [Value] {
|
public var orderedValues: [Value] {
|
||||||
return _orderedKeys.flatMap { _keysToValues[$0] }
|
return _orderedKeys.compactMap { _keysToValues[$0] }
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================================================= //
|
// ======================================================= //
|
||||||
|
@ -117,7 +117,7 @@ public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollecti
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
private func updateValue(value: Value, forKey key: Key) -> Value? {
|
private func updateValue(value: Value, forKey key: Key) -> Value? {
|
||||||
|
|
||||||
guard let currentValue = _keysToValues[key] else {
|
guard let currentValue = _keysToValues[key] else {
|
||||||
_orderedKeys.append(key)
|
_orderedKeys.append(key)
|
||||||
_keysToValues[key] = value
|
_keysToValues[key] = value
|
||||||
|
@ -125,7 +125,7 @@ public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollecti
|
||||||
}
|
}
|
||||||
_keysToValues[key] = value
|
_keysToValues[key] = value
|
||||||
return currentValue
|
return currentValue
|
||||||
|
|
||||||
// if _orderedKeys.contains(key) {
|
// if _orderedKeys.contains(key) {
|
||||||
// guard let currentValue = _keysToValues[key] else {
|
// guard let currentValue = _keysToValues[key] else {
|
||||||
// fatalError("Inconsistency error occured in OrderedDictionary")
|
// fatalError("Inconsistency error occured in OrderedDictionary")
|
||||||
|
@ -148,7 +148,7 @@ public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollecti
|
||||||
|
|
||||||
public func putAll(all: OrderedDictionary<Key, Value>) {
|
public func putAll(all: OrderedDictionary<Key, Value>) {
|
||||||
for i in all.orderedKeys {
|
for i in all.orderedKeys {
|
||||||
put(value:all[i]!, forKey: i)
|
put(value: all[i]!, forKey: i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@ public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollecti
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func remove(key: Key) -> Value? {
|
public func remove(key: Key) -> Value? {
|
||||||
return removeValueForKey(key:key)
|
return removeValueForKey(key: key)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func removeAll(keepCapacity: Bool = true) {
|
public func removeAll(keepCapacity: Bool = true) {
|
||||||
|
@ -391,28 +391,26 @@ extension OrderedDictionary: Equatable {
|
||||||
// return lhs._orderedKeys == rhs._orderedKeys && lhs._keysToValues == rhs._keysToValues
|
// return lhs._orderedKeys == rhs._orderedKeys && lhs._keysToValues == rhs._keysToValues
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Elements IteratorProtocol.
|
* Elements IteratorProtocol.
|
||||||
*/
|
*/
|
||||||
public struct OrderedDictionaryIterator<Key: Hashable, Value: Equatable>: IteratorProtocol {
|
public struct OrderedDictionaryIterator<Key: Hashable, Value: Equatable>: IteratorProtocol {
|
||||||
|
|
||||||
/// Elements reference
|
/// Elements reference
|
||||||
let orderedDictionary: OrderedDictionary<Key, Value>
|
let orderedDictionary: OrderedDictionary<Key, Value>
|
||||||
//current element index
|
//current element index
|
||||||
var index = 0
|
var index = 0
|
||||||
|
|
||||||
/// Initializer
|
/// Initializer
|
||||||
init(_ od: OrderedDictionary<Key, Value>) {
|
init(_ od: OrderedDictionary<Key, Value>) {
|
||||||
self.orderedDictionary = od
|
self.orderedDictionary = od
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advances to the next element and returns it, or `nil` if no next element
|
/// Advances to the next element and returns it, or `nil` if no next element
|
||||||
mutating public func next() -> Value? {
|
mutating public func next() -> Value? {
|
||||||
|
|
||||||
let result = index < orderedDictionary.orderedKeys.count ? orderedDictionary[orderedDictionary.orderedKeys[index]] : nil
|
let result = index < orderedDictionary.orderedKeys.count ? orderedDictionary[orderedDictionary.orderedKeys[index]] : nil
|
||||||
index += 1;
|
index += 1
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -422,13 +420,7 @@ public struct OrderedDictionaryIterator<Key: Hashable, Value: Equatable>: Iterat
|
||||||
*/
|
*/
|
||||||
extension OrderedDictionary: Sequence {
|
extension OrderedDictionary: Sequence {
|
||||||
/// Returns an iterator over the elements of this sequence.
|
/// Returns an iterator over the elements of this sequence.
|
||||||
func generate()->OrderedDictionaryIterator<Key, Value>
|
func generate()->OrderedDictionaryIterator<Key, Value> {
|
||||||
{
|
|
||||||
return OrderedDictionaryIterator(self)
|
return OrderedDictionaryIterator(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,7 @@ public class OrderedSet<T: Hashable> {
|
||||||
public func remove(_ object: T) {
|
public func remove(_ object: T) {
|
||||||
if let index = contents[object] {
|
if let index = contents[object] {
|
||||||
contents[object] = nil
|
contents[object] = nil
|
||||||
sequencedContents[index].deallocate(capacity: 1)
|
sequencedContents[index].deallocate()
|
||||||
sequencedContents.remove(at: index)
|
sequencedContents.remove(at: index)
|
||||||
|
|
||||||
for (object, i) in contents {
|
for (object, i) in contents {
|
||||||
|
@ -151,7 +151,7 @@ public class OrderedSet<T: Hashable> {
|
||||||
contents.removeAll()
|
contents.removeAll()
|
||||||
|
|
||||||
for sequencedContent in sequencedContents {
|
for sequencedContent in sequencedContents {
|
||||||
sequencedContent.deallocate(capacity: 1)
|
sequencedContent.deallocate()
|
||||||
}
|
}
|
||||||
|
|
||||||
sequencedContents.removeAll()
|
sequencedContents.removeAll()
|
||||||
|
|
|
@ -57,8 +57,3 @@ open class ParseSettings {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -146,7 +146,7 @@ public class Parser {
|
||||||
let nodeList: Array<Node> = try parseFragment(bodyHtml, body, baseUri)
|
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
|
//var nodes: [Node] = nodeList.toArray(Node[nodeList.size()]) // the node list gets modified when re-parented
|
||||||
if nodeList.count > 0 {
|
if nodeList.count > 0 {
|
||||||
for i in 1..<nodeList.count{
|
for i in 1..<nodeList.count {
|
||||||
try nodeList[i].remove()
|
try nodeList[i].remove()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,14 +24,14 @@ public struct Pattern {
|
||||||
}
|
}
|
||||||
|
|
||||||
func validate()throws {
|
func validate()throws {
|
||||||
_ = try NSRegularExpression(pattern: self.pattern, options:[])
|
_ = try NSRegularExpression(pattern: self.pattern, options: [])
|
||||||
}
|
}
|
||||||
|
|
||||||
func matcher(in text: String) -> Matcher {
|
func matcher(in text: String) -> Matcher {
|
||||||
do {
|
do {
|
||||||
let regex = try NSRegularExpression(pattern: self.pattern, options:[])
|
let regex = try NSRegularExpression(pattern: self.pattern, options: [])
|
||||||
let nsString = NSString(string: text)
|
let nsString = NSString(string: text)
|
||||||
let results = regex.matches(in: text, options:[], range: NSRange(location: 0, length: nsString.length))
|
let results = regex.matches(in: text, options: [], range: NSRange(location: 0, length: nsString.length))
|
||||||
|
|
||||||
return Matcher(results, text)
|
return Matcher(results, text)
|
||||||
} catch let error {
|
} catch let error {
|
||||||
|
@ -73,7 +73,7 @@ public class Matcher {
|
||||||
#else
|
#else
|
||||||
let c = b.range(at: i)
|
let c = b.range(at: i)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(c.location == NSNotFound) {return nil}
|
if(c.location == NSNotFound) {return nil}
|
||||||
let result = string.substring(c.location, c.length)
|
let result = string.substring(c.location, c.length)
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -140,7 +140,7 @@ public class QueryParser {
|
||||||
} else if (tq.matchChomp(".")) {
|
} 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
|
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())")
|
throw Exception.Error(type: ExceptionType.SelectorParseException, Message: "Could not parse query \(query): unexpected token at \(tq.remainder())")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +203,7 @@ public class QueryParser {
|
||||||
} else if (cq.matchChomp("~=")) {
|
} else if (cq.matchChomp("~=")) {
|
||||||
evals.append( Evaluator.AttributeWithValueMatching(key, Pattern.compile(cq.remainder())))
|
evals.append( Evaluator.AttributeWithValueMatching(key, Pattern.compile(cq.remainder())))
|
||||||
} else {
|
} else {
|
||||||
throw Exception.Error(type: ExceptionType.SelectorParseException, Message:"Could not parse attribute query '\(query)': unexpected token at '\(cq.remainder())'")
|
throw Exception.Error(type: ExceptionType.SelectorParseException, Message: "Could not parse attribute query '\(query)': unexpected token at '\(cq.remainder())'")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -250,7 +250,7 @@ public class QueryParser {
|
||||||
mB.find()
|
mB.find()
|
||||||
b = Int(mB.group()!.replaceFirst(of: "^\\+", with: ""))!
|
b = Int(mB.group()!.replaceFirst(of: "^\\+", with: ""))!
|
||||||
} else {
|
} else {
|
||||||
throw Exception.Error(type: ExceptionType.SelectorParseException, Message:"Could not parse nth-index '\(argS)': unexpected format")
|
throw Exception.Error(type: ExceptionType.SelectorParseException, Message: "Could not parse nth-index '\(argS)': unexpected format")
|
||||||
}
|
}
|
||||||
if (ofType) {
|
if (ofType) {
|
||||||
if (backwards) {
|
if (backwards) {
|
||||||
|
|
|
@ -67,20 +67,20 @@ class StreamReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start reading from the beginning of file.
|
/// Start reading from the beginning of file.
|
||||||
func rewind() -> Void {
|
func rewind() {
|
||||||
fileHandle.seek(toFileOffset: 0)
|
fileHandle.seek(toFileOffset: 0)
|
||||||
buffer.count = 0
|
buffer.count = 0
|
||||||
atEof = false
|
atEof = false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Close the underlying file. No reading must be done after calling this method.
|
/// Close the underlying file. No reading must be done after calling this method.
|
||||||
func close() -> Void {
|
func close() {
|
||||||
fileHandle?.closeFile()
|
fileHandle?.closeFile()
|
||||||
fileHandle = nil
|
fileHandle = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension StreamReader : Sequence {
|
extension StreamReader: Sequence {
|
||||||
func makeIterator() -> AnyIterator<String> {
|
func makeIterator() -> AnyIterator<String> {
|
||||||
return AnyIterator {
|
return AnyIterator {
|
||||||
return self.nextLine()
|
return self.nextLine()
|
||||||
|
|
|
@ -17,9 +17,8 @@ extension String {
|
||||||
subscript (i: Int) -> String {
|
subscript (i: Int) -> String {
|
||||||
return String(self[i] as Character)
|
return String(self[i] as Character)
|
||||||
}
|
}
|
||||||
|
|
||||||
init<S: Sequence>(_ ucs: S)where S.Iterator.Element == UnicodeScalar
|
init<S: Sequence>(_ ucs: S)where S.Iterator.Element == UnicodeScalar {
|
||||||
{
|
|
||||||
var s = ""
|
var s = ""
|
||||||
s.unicodeScalars.append(contentsOf: ucs)
|
s.unicodeScalars.append(contentsOf: ucs)
|
||||||
self = s
|
self = s
|
||||||
|
@ -91,7 +90,7 @@ extension String {
|
||||||
}
|
}
|
||||||
|
|
||||||
static func toHexString(n: Int) -> String {
|
static func toHexString(n: Int) -> String {
|
||||||
return String(format:"%2x", n)
|
return String(format: "%2x", n)
|
||||||
}
|
}
|
||||||
|
|
||||||
func insert(string: String, ind: Int) -> String {
|
func insert(string: String, ind: Int) -> String {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*/
|
*/
|
||||||
open class StringBuilder {
|
open class StringBuilder {
|
||||||
fileprivate var stringValue: Array<Character>
|
fileprivate var stringValue: Array<Character>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Construct with initial String contents
|
Construct with initial String contents
|
||||||
|
|
||||||
|
@ -13,11 +13,11 @@ open class StringBuilder {
|
||||||
public init(string: String = "") {
|
public init(string: String = "") {
|
||||||
self.stringValue = Array(string)
|
self.stringValue = Array(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(_ size: Int) {
|
public init(_ size: Int) {
|
||||||
self.stringValue = Array()
|
self.stringValue = Array()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Return the String object
|
Return the String object
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ open class StringBuilder {
|
||||||
open func toString() -> String {
|
open func toString() -> String {
|
||||||
return String(stringValue)
|
return String(stringValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Return the current length of the String object
|
Return the current length of the String object
|
||||||
*/
|
*/
|
||||||
|
@ -34,7 +34,7 @@ open class StringBuilder {
|
||||||
return self.stringValue.count
|
return self.stringValue.count
|
||||||
//return countElements(stringValue)
|
//return countElements(stringValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Append a String to the object
|
Append a String to the object
|
||||||
|
|
||||||
|
@ -45,29 +45,29 @@ open class StringBuilder {
|
||||||
open func append(_ string: String) {
|
open func append(_ string: String) {
|
||||||
stringValue.append(contentsOf: string)
|
stringValue.append(contentsOf: string)
|
||||||
}
|
}
|
||||||
|
|
||||||
open func appendCodePoint(_ chr: Character) {
|
open func appendCodePoint(_ chr: Character) {
|
||||||
stringValue.append(chr)
|
stringValue.append(chr)
|
||||||
}
|
}
|
||||||
|
|
||||||
open func appendCodePoints(_ chr: [Character]) {
|
open func appendCodePoints(_ chr: [Character]) {
|
||||||
stringValue.append(contentsOf: chr)
|
stringValue.append(contentsOf: chr)
|
||||||
}
|
}
|
||||||
|
|
||||||
open func appendCodePoint(_ ch: Int) {
|
open func appendCodePoint(_ ch: Int) {
|
||||||
stringValue.append(Character(UnicodeScalar(ch)!))
|
stringValue.append(Character(UnicodeScalar(ch)!))
|
||||||
}
|
}
|
||||||
|
|
||||||
open func appendCodePoint(_ ch: UnicodeScalar) {
|
open func appendCodePoint(_ ch: UnicodeScalar) {
|
||||||
stringValue.append(Character(ch))
|
stringValue.append(Character(ch))
|
||||||
}
|
}
|
||||||
|
|
||||||
open func appendCodePoints(_ chr: [UnicodeScalar]) {
|
open func appendCodePoints(_ chr: [UnicodeScalar]) {
|
||||||
for c in chr {
|
for c in chr {
|
||||||
appendCodePoint(c)
|
appendCodePoint(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Append a Printable to the object
|
Append a Printable to the object
|
||||||
|
|
||||||
|
@ -80,19 +80,19 @@ open class StringBuilder {
|
||||||
stringValue.append(contentsOf: value.description)
|
stringValue.append(contentsOf: value.description)
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
open func append(_ value: UnicodeScalar) -> StringBuilder {
|
open func append(_ value: UnicodeScalar) -> StringBuilder {
|
||||||
stringValue.append(contentsOf: value.description)
|
stringValue.append(contentsOf: value.description)
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
open func insert<T: CustomStringConvertible>(_ offset: Int, _ value: T) -> StringBuilder {
|
open func insert<T: CustomStringConvertible>(_ offset: Int, _ value: T) -> StringBuilder {
|
||||||
stringValue.insert(contentsOf: value.description, at: offset)
|
stringValue.insert(contentsOf: value.description, at: offset)
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Append a String and a newline to the object
|
Append a String and a newline to the object
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ open class StringBuilder {
|
||||||
stringValue.append(contentsOf: "\n")
|
stringValue.append(contentsOf: "\n")
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Append a Printable and a newline to the object
|
Append a Printable and a newline to the object
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ open class StringBuilder {
|
||||||
stringValue.append(contentsOf: "\n")
|
stringValue.append(contentsOf: "\n")
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Reset the object to an empty string
|
Reset the object to an empty string
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ open class StringBuilder {
|
||||||
*/
|
*/
|
||||||
@discardableResult
|
@discardableResult
|
||||||
open func clear() -> StringBuilder {
|
open func clear() -> StringBuilder {
|
||||||
stringValue = Array();
|
stringValue = Array()
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,7 +215,7 @@ open class StringUtil {
|
||||||
if(base.pathComponents.count == 0 && base.absoluteString.last != "/" && !base.isFileURL) {
|
if(base.pathComponents.count == 0 && base.absoluteString.last != "/" && !base.isFileURL) {
|
||||||
base = base.appendingPathComponent("/", isDirectory: false)
|
base = base.appendingPathComponent("/", isDirectory: false)
|
||||||
}
|
}
|
||||||
let u = URL(string: relUrl, relativeTo : base)
|
let u = URL(string: relUrl, relativeTo: base)
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -232,7 +232,7 @@ open class SwiftSoup {
|
||||||
clean.outputSettings(outputSettings)
|
clean.outputSettings(outputSettings)
|
||||||
return try clean.body()?.html()
|
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
|
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.
|
still be run through the cleaner to set up enforced attributes, and to tidy the output.
|
||||||
|
@ -242,9 +242,9 @@ open class SwiftSoup {
|
||||||
@see #clean(String, org.jsoup.safety.Whitelist)
|
@see #clean(String, org.jsoup.safety.Whitelist)
|
||||||
*/
|
*/
|
||||||
public static func isValid(_ bodyHtml: String, _ whitelist: Whitelist)throws->Bool {
|
public static func isValid(_ bodyHtml: String, _ whitelist: Whitelist)throws->Bool {
|
||||||
let dirty = try parseBodyFragment(bodyHtml, "");
|
let dirty = try parseBodyFragment(bodyHtml, "")
|
||||||
let cleaner = Cleaner(whitelist);
|
let cleaner = Cleaner(whitelist)
|
||||||
return try cleaner.isValid(dirty);
|
return try cleaner.isValid(dirty)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,7 +215,7 @@ open class Tag: Hashable {
|
||||||
let this = lhs
|
let this = lhs
|
||||||
let o = rhs
|
let o = rhs
|
||||||
if (this === o) {return true}
|
if (this === o) {return true}
|
||||||
if (type(of:this) != type(of:o)) {return false}
|
if (type(of: this) != type(of: o)) {return false}
|
||||||
|
|
||||||
let tag: Tag = o
|
let tag: Tag = o
|
||||||
|
|
||||||
|
|
|
@ -60,13 +60,11 @@ open class Token {
|
||||||
func getName() -> String {
|
func getName() -> String {
|
||||||
return name.toString()
|
return name.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPubSysKey()->String? {
|
|
||||||
return pubSysKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
func getPubSysKey() -> String? {
|
||||||
|
return pubSysKey
|
||||||
|
}
|
||||||
|
|
||||||
func getPublicIdentifier() -> String {
|
func getPublicIdentifier() -> String {
|
||||||
return publicIdentifier.toString()
|
return publicIdentifier.toString()
|
||||||
}
|
}
|
||||||
|
@ -389,9 +387,9 @@ open class Token {
|
||||||
|
|
||||||
extension Token: CustomDebugStringConvertible {
|
extension Token: CustomDebugStringConvertible {
|
||||||
public var debugDescription: String {
|
public var debugDescription: String {
|
||||||
do{
|
do {
|
||||||
return try self.toString()
|
return try self.toString()
|
||||||
}catch{
|
} catch {
|
||||||
return "Error while get string debug"
|
return "Error while get string debug"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ final class Tokeniser {
|
||||||
private var state: TokeniserState = TokeniserState.Data // current tokenisation state
|
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 emitPending: Token? // the token we are about to emit on next read
|
||||||
private var isEmitPending: Bool = false
|
private var isEmitPending: Bool = false
|
||||||
private var charsString: String? = nil // characters pending an emit. Will fall to charsBuilder if more than one
|
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
|
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>
|
let dataBuffer: StringBuilder = StringBuilder(1024) // buffers data looking for </script>
|
||||||
|
|
||||||
|
|
|
@ -620,7 +620,7 @@ enum TokeniserState: TokeniserStateProtocol {
|
||||||
t.eofError(self)
|
t.eofError(self)
|
||||||
t.transition(.Data)
|
t.transition(.Data)
|
||||||
break
|
break
|
||||||
case "\"", "'",UnicodeScalar.LessThan, "=":
|
case "\"", "'", UnicodeScalar.LessThan, "=":
|
||||||
t.error(self)
|
t.error(self)
|
||||||
try t.tagPending.newAttribute()
|
try t.tagPending.newAttribute()
|
||||||
t.tagPending.appendAttributeName(c)
|
t.tagPending.appendAttributeName(c)
|
||||||
|
@ -1185,7 +1185,7 @@ enum TokeniserState: TokeniserStateProtocol {
|
||||||
t.doctypePending.pubSysKey = DocumentType.PUBLIC_KEY
|
t.doctypePending.pubSysKey = DocumentType.PUBLIC_KEY
|
||||||
t.transition(.AfterDoctypePublicKeyword)
|
t.transition(.AfterDoctypePublicKeyword)
|
||||||
} else if (r.matchConsumeIgnoreCase(DocumentType.SYSTEM_KEY)) {
|
} else if (r.matchConsumeIgnoreCase(DocumentType.SYSTEM_KEY)) {
|
||||||
t.doctypePending.pubSysKey = DocumentType.SYSTEM_KEY;
|
t.doctypePending.pubSysKey = DocumentType.SYSTEM_KEY
|
||||||
t.transition(.AfterDoctypeSystemKeyword)
|
t.transition(.AfterDoctypeSystemKeyword)
|
||||||
} else {
|
} else {
|
||||||
t.error(self)
|
t.error(self)
|
||||||
|
|
|
@ -16,17 +16,17 @@ private let symbolSet = CharacterSet.symbols
|
||||||
private let digitSet = CharacterSet.decimalDigits
|
private let digitSet = CharacterSet.decimalDigits
|
||||||
|
|
||||||
extension UnicodeScalar {
|
extension UnicodeScalar {
|
||||||
public static let Ampersand : UnicodeScalar = "&"
|
public static let Ampersand: UnicodeScalar = "&"
|
||||||
public static let LessThan : UnicodeScalar = "<"
|
public static let LessThan: UnicodeScalar = "<"
|
||||||
public static let GreaterThan : UnicodeScalar = ">"
|
public static let GreaterThan: UnicodeScalar = ">"
|
||||||
|
|
||||||
public static let Space: UnicodeScalar = " "
|
public static let Space: UnicodeScalar = " "
|
||||||
public static let BackslashF: UnicodeScalar = UnicodeScalar(12)
|
public static let BackslashF: UnicodeScalar = UnicodeScalar(12)
|
||||||
public static let BackslashT: UnicodeScalar = "\t"
|
public static let BackslashT: UnicodeScalar = "\t"
|
||||||
public static let BackslashN: UnicodeScalar = "\n"
|
public static let BackslashN: UnicodeScalar = "\n"
|
||||||
public static let BackslashR: UnicodeScalar = "\r"
|
public static let BackslashR: UnicodeScalar = "\r"
|
||||||
public static let Slash: UnicodeScalar = "/"
|
public static let Slash: UnicodeScalar = "/"
|
||||||
|
|
||||||
public static let FormFeed: UnicodeScalar = "\u{000B}"// Form Feed
|
public static let FormFeed: UnicodeScalar = "\u{000B}"// Form Feed
|
||||||
public static let VerticalTab: UnicodeScalar = "\u{000C}"// vertical tab
|
public static let VerticalTab: UnicodeScalar = "\u{000C}"// vertical tab
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ struct Validate {
|
||||||
* Validates that the object is not null
|
* Validates that the object is not null
|
||||||
* @param obj object to test
|
* @param obj object to test
|
||||||
*/
|
*/
|
||||||
public static func notNull(obj:Any?) throws {
|
public static func notNull(obj: Any?) throws {
|
||||||
if (obj == nil) {
|
if (obj == nil) {
|
||||||
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Object must not be null")
|
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Object must not be null")
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ public class Whitelist {
|
||||||
private var enforcedAttributes: Dictionary<TagName, Dictionary<AttributeKey, AttributeValue>> // always set these attribute values
|
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 protocols: Dictionary<TagName, Dictionary<AttributeKey, Set<Protocol>>> // allowed URL protocols for attributes
|
||||||
private var preserveRelativeLinks: Bool // option to preserve relative links
|
private var preserveRelativeLinks: Bool // option to preserve relative links
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This whitelist allows only text nodes: all HTML will be stripped.
|
This whitelist allows only text nodes: all HTML will be stripped.
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ public class Whitelist {
|
||||||
public static func none() -> Whitelist {
|
public static func none() -> Whitelist {
|
||||||
return Whitelist()
|
return Whitelist()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This whitelist allows only simple text formatting: <code>b, em, i, strong, u</code>. All other HTML (tags and
|
This whitelist allows only simple text formatting: <code>b, em, i, strong, u</code>. All other HTML (tags and
|
||||||
attributes) will be removed.
|
attributes) will be removed.
|
||||||
|
@ -82,7 +82,7 @@ public class Whitelist {
|
||||||
public static func simpleText()throws ->Whitelist {
|
public static func simpleText()throws ->Whitelist {
|
||||||
return try Whitelist().addTags("b", "em", "i", "strong", "u")
|
return try Whitelist().addTags("b", "em", "i", "strong", "u")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
<p>
|
<p>
|
||||||
This whitelist allows a fuller range of text nodes: <code>a, b, blockquote, br, cite, code, dd, dl, dt, em, i, li,
|
This whitelist allows a fuller range of text nodes: <code>a, b, blockquote, br, cite, code, dd, dl, dt, em, i, li,
|
||||||
|
@ -104,18 +104,18 @@ public class Whitelist {
|
||||||
"a", "b", "blockquote", "br", "cite", "code", "dd", "dl", "dt", "em",
|
"a", "b", "blockquote", "br", "cite", "code", "dd", "dl", "dt", "em",
|
||||||
"i", "li", "ol", "p", "pre", "q", "small", "span", "strike", "strong", "sub",
|
"i", "li", "ol", "p", "pre", "q", "small", "span", "strike", "strong", "sub",
|
||||||
"sup", "u", "ul")
|
"sup", "u", "ul")
|
||||||
|
|
||||||
.addAttributes("a", "href")
|
.addAttributes("a", "href")
|
||||||
.addAttributes("blockquote", "cite")
|
.addAttributes("blockquote", "cite")
|
||||||
.addAttributes("q", "cite")
|
.addAttributes("q", "cite")
|
||||||
|
|
||||||
.addProtocols("a", "href", "ftp", "http", "https", "mailto")
|
.addProtocols("a", "href", "ftp", "http", "https", "mailto")
|
||||||
.addProtocols("blockquote", "cite", "http", "https")
|
.addProtocols("blockquote", "cite", "http", "https")
|
||||||
.addProtocols("cite", "cite", "http", "https")
|
.addProtocols("cite", "cite", "http", "https")
|
||||||
|
|
||||||
.addEnforcedAttribute("a", "rel", "nofollow")
|
.addEnforcedAttribute("a", "rel", "nofollow")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This whitelist allows the same text tags as {@link #basic}, and also allows <code>img</code> tags, with appropriate
|
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>.
|
attributes, with <code>src</code> pointing to <code>http</code> or <code>https</code>.
|
||||||
|
@ -127,9 +127,9 @@ public class Whitelist {
|
||||||
.addTags("img")
|
.addTags("img")
|
||||||
.addAttributes("img", "align", "alt", "height", "src", "title", "width")
|
.addAttributes("img", "align", "alt", "height", "src", "title", "width")
|
||||||
.addProtocols("img", "src", "http", "https")
|
.addProtocols("img", "src", "http", "https")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This whitelist allows a full range of text and structural body HTML: <code>a, b, blockquote, br, caption, cite,
|
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,
|
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,
|
||||||
|
@ -148,7 +148,7 @@ public class Whitelist {
|
||||||
"i", "img", "li", "ol", "p", "pre", "q", "small", "span", "strike", "strong",
|
"i", "img", "li", "ol", "p", "pre", "q", "small", "span", "strike", "strong",
|
||||||
"sub", "sup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "u",
|
"sub", "sup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "u",
|
||||||
"ul")
|
"ul")
|
||||||
|
|
||||||
.addAttributes("a", "href", "title")
|
.addAttributes("a", "href", "title")
|
||||||
.addAttributes("blockquote", "cite")
|
.addAttributes("blockquote", "cite")
|
||||||
.addAttributes("col", "span", "width")
|
.addAttributes("col", "span", "width")
|
||||||
|
@ -162,14 +162,14 @@ public class Whitelist {
|
||||||
"th", "abbr", "axis", "colspan", "rowspan", "scope",
|
"th", "abbr", "axis", "colspan", "rowspan", "scope",
|
||||||
"width")
|
"width")
|
||||||
.addAttributes("ul", "type")
|
.addAttributes("ul", "type")
|
||||||
|
|
||||||
.addProtocols("a", "href", "ftp", "http", "https", "mailto")
|
.addProtocols("a", "href", "ftp", "http", "https", "mailto")
|
||||||
.addProtocols("blockquote", "cite", "http", "https")
|
.addProtocols("blockquote", "cite", "http", "https")
|
||||||
.addProtocols("cite", "cite", "http", "https")
|
.addProtocols("cite", "cite", "http", "https")
|
||||||
.addProtocols("img", "src", "http", "https")
|
.addProtocols("img", "src", "http", "https")
|
||||||
.addProtocols("q", "cite", "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.
|
Create a new, empty whitelist. Generally it will be better to start with a default prepared whitelist instead.
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ public class Whitelist {
|
||||||
protocols = Dictionary<TagName, Dictionary<AttributeKey, Set<Protocol>>>()
|
protocols = Dictionary<TagName, Dictionary<AttributeKey, Set<Protocol>>>()
|
||||||
preserveRelativeLinks = false
|
preserveRelativeLinks = false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Add a list of allowed elements to a whitelist. (If a tag is not allowed, it will be removed from the HTML.)
|
Add a list of allowed elements to a whitelist. (If a tag is not allowed, it will be removed from the HTML.)
|
||||||
|
|
||||||
|
@ -200,7 +200,7 @@ public class Whitelist {
|
||||||
}
|
}
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Remove a list of allowed elements from a whitelist. (If a tag is not allowed, it will be removed from the HTML.)
|
Remove a list of allowed elements from a whitelist. (If a tag is not allowed, it will be removed from the HTML.)
|
||||||
|
|
||||||
|
@ -209,12 +209,12 @@ public class Whitelist {
|
||||||
*/
|
*/
|
||||||
@discardableResult
|
@discardableResult
|
||||||
open func removeTags(_ tags: String...)throws ->Whitelist {
|
open func removeTags(_ tags: String...)throws ->Whitelist {
|
||||||
try Validate.notNull(obj:tags)
|
try Validate.notNull(obj: tags)
|
||||||
|
|
||||||
for tag in tags {
|
for tag in tags {
|
||||||
try Validate.notEmpty(string: tag)
|
try Validate.notEmpty(string: tag)
|
||||||
let tagName: TagName = TagName.valueOf(tag)
|
let tagName: TagName = TagName.valueOf(tag)
|
||||||
|
|
||||||
if(tagNames.contains(tagName)) { // Only look in sub-maps if tag was allowed
|
if(tagNames.contains(tagName)) { // Only look in sub-maps if tag was allowed
|
||||||
tagNames.remove(tagName)
|
tagNames.remove(tagName)
|
||||||
attributes.removeValue(forKey: tagName)
|
attributes.removeValue(forKey: tagName)
|
||||||
|
@ -224,7 +224,7 @@ public class Whitelist {
|
||||||
}
|
}
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Add a list of allowed attributes to a tag. (If an attribute is not allowed on an element, it will be removed.)
|
Add a list of allowed attributes to a tag. (If an attribute is not allowed on an element, it will be removed.)
|
||||||
<p>
|
<p>
|
||||||
|
@ -244,7 +244,7 @@ public class Whitelist {
|
||||||
open func addAttributes(_ tag: String, _ keys: String...)throws->Whitelist {
|
open func addAttributes(_ tag: String, _ keys: String...)throws->Whitelist {
|
||||||
try Validate.notEmpty(string: tag)
|
try Validate.notEmpty(string: tag)
|
||||||
try Validate.isTrue(val: keys.count > 0, msg: "No attributes supplied.")
|
try Validate.isTrue(val: keys.count > 0, msg: "No attributes supplied.")
|
||||||
|
|
||||||
let tagName = TagName.valueOf(tag)
|
let tagName = TagName.valueOf(tag)
|
||||||
if (!tagNames.contains(tagName)) {
|
if (!tagNames.contains(tagName)) {
|
||||||
tagNames.insert(tagName)
|
tagNames.insert(tagName)
|
||||||
|
@ -254,7 +254,7 @@ public class Whitelist {
|
||||||
try Validate.notEmpty(string: key)
|
try Validate.notEmpty(string: key)
|
||||||
attributeSet.insert(AttributeKey.valueOf(key))
|
attributeSet.insert(AttributeKey.valueOf(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
if var currentSet = attributes[tagName] {
|
if var currentSet = attributes[tagName] {
|
||||||
for at in attributeSet {
|
for at in attributeSet {
|
||||||
currentSet.insert(at)
|
currentSet.insert(at)
|
||||||
|
@ -263,10 +263,10 @@ public class Whitelist {
|
||||||
} else {
|
} else {
|
||||||
attributes[tagName] = attributeSet
|
attributes[tagName] = attributeSet
|
||||||
}
|
}
|
||||||
|
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Remove a list of allowed attributes from a tag. (If an attribute is not allowed on an element, it will be removed.)
|
Remove a list of allowed attributes from a tag. (If an attribute is not allowed on an element, it will be removed.)
|
||||||
<p>
|
<p>
|
||||||
|
@ -286,14 +286,14 @@ public class Whitelist {
|
||||||
open func removeAttributes(_ tag: String, _ keys: String...)throws->Whitelist {
|
open func removeAttributes(_ tag: String, _ keys: String...)throws->Whitelist {
|
||||||
try Validate.notEmpty(string: tag)
|
try Validate.notEmpty(string: tag)
|
||||||
try Validate.isTrue(val: keys.count > 0, msg: "No attributes supplied.")
|
try Validate.isTrue(val: keys.count > 0, msg: "No attributes supplied.")
|
||||||
|
|
||||||
let tagName: TagName = TagName.valueOf(tag)
|
let tagName: TagName = TagName.valueOf(tag)
|
||||||
var attributeSet = Set<AttributeKey>()
|
var attributeSet = Set<AttributeKey>()
|
||||||
for key in keys {
|
for key in keys {
|
||||||
try Validate.notEmpty(string: key)
|
try Validate.notEmpty(string: key)
|
||||||
attributeSet.insert(AttributeKey.valueOf(key))
|
attributeSet.insert(AttributeKey.valueOf(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
if(tagNames.contains(tagName)) { // Only look in sub-maps if tag was allowed
|
if(tagNames.contains(tagName)) { // Only look in sub-maps if tag was allowed
|
||||||
if var currentSet = attributes[tagName] {
|
if var currentSet = attributes[tagName] {
|
||||||
for l in attributeSet {
|
for l in attributeSet {
|
||||||
|
@ -304,9 +304,9 @@ public class Whitelist {
|
||||||
attributes.removeValue(forKey: tagName)
|
attributes.removeValue(forKey: tagName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(tag == ":all") { // Attribute needs to be removed from all individually set tags
|
if(tag == ":all") { // Attribute needs to be removed from all individually set tags
|
||||||
for name in attributes.keys {
|
for name in attributes.keys {
|
||||||
var currentSet: Set<AttributeKey> = attributes[name]!
|
var currentSet: Set<AttributeKey> = attributes[name]!
|
||||||
|
@ -321,7 +321,7 @@ public class Whitelist {
|
||||||
}
|
}
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Add an enforced attribute to a tag. An enforced attribute will always be added to the element. If the element
|
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.
|
already has the attribute set, it will be overridden.
|
||||||
|
@ -340,14 +340,14 @@ public class Whitelist {
|
||||||
try Validate.notEmpty(string: tag)
|
try Validate.notEmpty(string: tag)
|
||||||
try Validate.notEmpty(string: key)
|
try Validate.notEmpty(string: key)
|
||||||
try Validate.notEmpty(string: value)
|
try Validate.notEmpty(string: value)
|
||||||
|
|
||||||
let tagName: TagName = TagName.valueOf(tag)
|
let tagName: TagName = TagName.valueOf(tag)
|
||||||
if (!tagNames.contains(tagName)) {
|
if (!tagNames.contains(tagName)) {
|
||||||
tagNames.insert(tagName)
|
tagNames.insert(tagName)
|
||||||
}
|
}
|
||||||
let attrKey: AttributeKey = AttributeKey.valueOf(key)
|
let attrKey: AttributeKey = AttributeKey.valueOf(key)
|
||||||
let attrVal: AttributeValue = AttributeValue.valueOf(value)
|
let attrVal: AttributeValue = AttributeValue.valueOf(value)
|
||||||
|
|
||||||
if (enforcedAttributes[tagName] != nil) {
|
if (enforcedAttributes[tagName] != nil) {
|
||||||
enforcedAttributes[tagName]?[attrKey] = attrVal
|
enforcedAttributes[tagName]?[attrKey] = attrVal
|
||||||
} else {
|
} else {
|
||||||
|
@ -357,7 +357,7 @@ public class Whitelist {
|
||||||
}
|
}
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Remove a previously configured enforced attribute from a tag.
|
Remove a previously configured enforced attribute from a tag.
|
||||||
|
|
||||||
|
@ -369,21 +369,21 @@ public class Whitelist {
|
||||||
open func removeEnforcedAttribute(_ tag: String, _ key: String)throws->Whitelist {
|
open func removeEnforcedAttribute(_ tag: String, _ key: String)throws->Whitelist {
|
||||||
try Validate.notEmpty(string: tag)
|
try Validate.notEmpty(string: tag)
|
||||||
try Validate.notEmpty(string: key)
|
try Validate.notEmpty(string: key)
|
||||||
|
|
||||||
let tagName: TagName = TagName.valueOf(tag)
|
let tagName: TagName = TagName.valueOf(tag)
|
||||||
if(tagNames.contains(tagName) && (enforcedAttributes[tagName] != nil)) {
|
if(tagNames.contains(tagName) && (enforcedAttributes[tagName] != nil)) {
|
||||||
let attrKey: AttributeKey = AttributeKey.valueOf(key)
|
let attrKey: AttributeKey = AttributeKey.valueOf(key)
|
||||||
var attrMap: Dictionary<AttributeKey, AttributeValue> = enforcedAttributes[tagName]!
|
var attrMap: Dictionary<AttributeKey, AttributeValue> = enforcedAttributes[tagName]!
|
||||||
attrMap.removeValue(forKey: attrKey)
|
attrMap.removeValue(forKey: attrKey)
|
||||||
enforcedAttributes[tagName] = attrMap
|
enforcedAttributes[tagName] = attrMap
|
||||||
|
|
||||||
if(attrMap.isEmpty) { // Remove tag from enforced attribute map if no enforced attributes are present
|
if(attrMap.isEmpty) { // Remove tag from enforced attribute map if no enforced attributes are present
|
||||||
enforcedAttributes.removeValue(forKey: tagName)
|
enforcedAttributes.removeValue(forKey: tagName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure this Whitelist to preserve relative links in an element's URL attribute, or convert them to absolute
|
* 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
|
* links. By default, this is <b>false</b>: URLs will be made absolute (e.g. start with an allowed protocol, like
|
||||||
|
@ -404,7 +404,7 @@ public class Whitelist {
|
||||||
preserveRelativeLinks = preserve
|
preserveRelativeLinks = preserve
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Add allowed URL protocols for an element's URL attribute. This restricts the possible values of the attribute to
|
Add allowed URL protocols for an element's URL attribute. This restricts the possible values of the attribute to
|
||||||
URLs with the defined protocol.
|
URLs with the defined protocol.
|
||||||
|
@ -425,19 +425,19 @@ public class Whitelist {
|
||||||
open func addProtocols(_ tag: String, _ key: String, _ protocols: String...)throws->Whitelist {
|
open func addProtocols(_ tag: String, _ key: String, _ protocols: String...)throws->Whitelist {
|
||||||
try Validate.notEmpty(string: tag)
|
try Validate.notEmpty(string: tag)
|
||||||
try Validate.notEmpty(string: key)
|
try Validate.notEmpty(string: key)
|
||||||
|
|
||||||
let tagName: TagName = TagName.valueOf(tag)
|
let tagName: TagName = TagName.valueOf(tag)
|
||||||
let attrKey: AttributeKey = AttributeKey.valueOf(key)
|
let attrKey: AttributeKey = AttributeKey.valueOf(key)
|
||||||
var attrMap: Dictionary<AttributeKey, Set<Protocol>>
|
var attrMap: Dictionary<AttributeKey, Set<Protocol>>
|
||||||
var protSet: Set<Protocol>
|
var protSet: Set<Protocol>
|
||||||
|
|
||||||
if (self.protocols[tagName] != nil) {
|
if (self.protocols[tagName] != nil) {
|
||||||
attrMap = self.protocols[tagName]!
|
attrMap = self.protocols[tagName]!
|
||||||
} else {
|
} else {
|
||||||
attrMap = Dictionary<AttributeKey, Set<Protocol>>()
|
attrMap = Dictionary<AttributeKey, Set<Protocol>>()
|
||||||
self.protocols[tagName] = attrMap
|
self.protocols[tagName] = attrMap
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attrMap[attrKey] != nil) {
|
if (attrMap[attrKey] != nil) {
|
||||||
protSet = attrMap[attrKey]!
|
protSet = attrMap[attrKey]!
|
||||||
} else {
|
} else {
|
||||||
|
@ -452,10 +452,10 @@ public class Whitelist {
|
||||||
}
|
}
|
||||||
attrMap[attrKey] = protSet
|
attrMap[attrKey] = protSet
|
||||||
self.protocols[tagName] = attrMap
|
self.protocols[tagName] = attrMap
|
||||||
|
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Remove allowed URL protocols for an element's URL attribute.
|
Remove allowed URL protocols for an element's URL attribute.
|
||||||
<p>
|
<p>
|
||||||
|
@ -471,10 +471,10 @@ public class Whitelist {
|
||||||
open func removeProtocols(_ tag: String, _ key: String, _ protocols: String...)throws->Whitelist {
|
open func removeProtocols(_ tag: String, _ key: String, _ protocols: String...)throws->Whitelist {
|
||||||
try Validate.notEmpty(string: tag)
|
try Validate.notEmpty(string: tag)
|
||||||
try Validate.notEmpty(string: key)
|
try Validate.notEmpty(string: key)
|
||||||
|
|
||||||
let tagName: TagName = TagName.valueOf(tag)
|
let tagName: TagName = TagName.valueOf(tag)
|
||||||
let attrKey: AttributeKey = AttributeKey.valueOf(key)
|
let attrKey: AttributeKey = AttributeKey.valueOf(key)
|
||||||
|
|
||||||
if(self.protocols[tagName] != nil) {
|
if(self.protocols[tagName] != nil) {
|
||||||
var attrMap: Dictionary<AttributeKey, Set<Protocol>> = self.protocols[tagName]!
|
var attrMap: Dictionary<AttributeKey, Set<Protocol>> = self.protocols[tagName]!
|
||||||
if(attrMap[attrKey] != nil) {
|
if(attrMap[attrKey] != nil) {
|
||||||
|
@ -485,20 +485,20 @@ public class Whitelist {
|
||||||
protSet.remove(prot)
|
protSet.remove(prot)
|
||||||
}
|
}
|
||||||
attrMap[attrKey] = protSet
|
attrMap[attrKey] = protSet
|
||||||
|
|
||||||
if(protSet.isEmpty) { // Remove protocol set if empty
|
if(protSet.isEmpty) { // Remove protocol set if empty
|
||||||
attrMap.removeValue(forKey: attrKey)
|
attrMap.removeValue(forKey: attrKey)
|
||||||
if(attrMap.isEmpty) { // Remove entry for tag if empty
|
if(attrMap.isEmpty) { // Remove entry for tag if empty
|
||||||
self.protocols.removeValue(forKey: tagName)
|
self.protocols.removeValue(forKey: tagName)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.protocols[tagName] = attrMap
|
self.protocols[tagName] = attrMap
|
||||||
}
|
}
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test if the supplied tag is allowed by this whitelist
|
* Test if the supplied tag is allowed by this whitelist
|
||||||
* @param tag test tag
|
* @param tag test tag
|
||||||
|
@ -507,7 +507,7 @@ public class Whitelist {
|
||||||
public func isSafeTag(_ tag: String) -> Bool {
|
public func isSafeTag(_ tag: String) -> Bool {
|
||||||
return tagNames.contains(TagName.valueOf(tag))
|
return tagNames.contains(TagName.valueOf(tag))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test if the supplied attribute is allowed by this whitelist for this tag
|
* Test if the supplied attribute is allowed by this whitelist for this tag
|
||||||
* @param tagName tag to consider allowing the attribute in
|
* @param tagName tag to consider allowing the attribute in
|
||||||
|
@ -518,7 +518,7 @@ public class Whitelist {
|
||||||
public func isSafeAttribute(_ tagName: String, _ el: Element, _ attr: Attribute)throws -> Bool {
|
public func isSafeAttribute(_ tagName: String, _ el: Element, _ attr: Attribute)throws -> Bool {
|
||||||
let tag: TagName = TagName.valueOf(tagName)
|
let tag: TagName = TagName.valueOf(tagName)
|
||||||
let key: AttributeKey = AttributeKey.valueOf(attr.getKey())
|
let key: AttributeKey = AttributeKey.valueOf(attr.getKey())
|
||||||
|
|
||||||
if (attributes[tag] != nil) {
|
if (attributes[tag] != nil) {
|
||||||
if (attributes[tag]?.contains(key))! {
|
if (attributes[tag]?.contains(key))! {
|
||||||
if (protocols[tag] != nil) {
|
if (protocols[tag] != nil) {
|
||||||
|
@ -533,7 +533,7 @@ public class Whitelist {
|
||||||
// no attributes defined for tag, try :all tag
|
// no attributes defined for tag, try :all tag
|
||||||
return try !(tagName == ":all") && isSafeAttribute(":all", el, attr)
|
return try !(tagName == ":all") && isSafeAttribute(":all", el, attr)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func testValidProtocol(_ el: Element, _ attr: Attribute, _ protocols: Set<Protocol>)throws->Bool {
|
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.
|
// try to resolve relative urls to abs, and optionally update the attribute so output html has abs.
|
||||||
// rels without a baseuri get removed
|
// rels without a baseuri get removed
|
||||||
|
@ -544,10 +544,10 @@ public class Whitelist {
|
||||||
if (!preserveRelativeLinks) {
|
if (!preserveRelativeLinks) {
|
||||||
attr.setValue(value: value)
|
attr.setValue(value: value)
|
||||||
}
|
}
|
||||||
|
|
||||||
for ptl in protocols {
|
for ptl in protocols {
|
||||||
var prot: String = ptl.toString()
|
var prot: String = ptl.toString()
|
||||||
|
|
||||||
if (prot=="#") { // allows anchor links
|
if (prot=="#") { // allows anchor links
|
||||||
if (isValidAnchor(value)) {
|
if (isValidAnchor(value)) {
|
||||||
return true
|
return true
|
||||||
|
@ -555,22 +555,22 @@ public class Whitelist {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
prot += ":"
|
prot += ":"
|
||||||
|
|
||||||
if (value.lowercased().hasPrefix(prot)) {
|
if (value.lowercased().hasPrefix(prot)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private func isValidAnchor(_ value: String) -> Bool {
|
private func isValidAnchor(_ value: String) -> Bool {
|
||||||
return value.startsWith("#") && !(Pattern(".*\\s.*").matcher(in: value).count > 0)
|
return value.startsWith("#") && !(Pattern(".*\\s.*").matcher(in: value).count > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getEnforcedAttributes(_ tagName: String)throws->Attributes {
|
public func getEnforcedAttributes(_ tagName: String)throws->Attributes {
|
||||||
let attrs: Attributes = Attributes()
|
let attrs: Attributes = Attributes()
|
||||||
let tag: TagName = TagName.valueOf(tagName)
|
let tag: TagName = TagName.valueOf(tagName)
|
||||||
|
@ -581,7 +581,7 @@ public class Whitelist {
|
||||||
}
|
}
|
||||||
return attrs
|
return attrs
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// named types for config. All just hold strings, but here for my sanity.
|
// named types for config. All just hold strings, but here for my sanity.
|
||||||
|
@ -590,7 +590,7 @@ open class TagName: TypedValue {
|
||||||
override init(_ value: String) {
|
override init(_ value: String) {
|
||||||
super.init(value)
|
super.init(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func valueOf(_ value: String) -> TagName {
|
static func valueOf(_ value: String) -> TagName {
|
||||||
return TagName(value)
|
return TagName(value)
|
||||||
}
|
}
|
||||||
|
@ -600,7 +600,7 @@ open class AttributeKey: TypedValue {
|
||||||
override init(_ value: String) {
|
override init(_ value: String) {
|
||||||
super.init(value)
|
super.init(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func valueOf(_ value: String) -> AttributeKey {
|
static func valueOf(_ value: String) -> AttributeKey {
|
||||||
return AttributeKey(value)
|
return AttributeKey(value)
|
||||||
}
|
}
|
||||||
|
@ -610,7 +610,7 @@ open class AttributeValue: TypedValue {
|
||||||
override init(_ value: String) {
|
override init(_ value: String) {
|
||||||
super.init(value)
|
super.init(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func valueOf(_ value: String) -> AttributeValue {
|
static func valueOf(_ value: String) -> AttributeValue {
|
||||||
return AttributeValue(value)
|
return AttributeValue(value)
|
||||||
}
|
}
|
||||||
|
@ -620,7 +620,7 @@ open class Protocol: TypedValue {
|
||||||
override init(_ value: String) {
|
override init(_ value: String) {
|
||||||
super.init(value)
|
super.init(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func valueOf(_ value: String) -> Protocol {
|
static func valueOf(_ value: String) -> Protocol {
|
||||||
return Protocol(value)
|
return Protocol(value)
|
||||||
}
|
}
|
||||||
|
@ -628,11 +628,11 @@ open class Protocol: TypedValue {
|
||||||
|
|
||||||
open class TypedValue {
|
open class TypedValue {
|
||||||
fileprivate let value: String
|
fileprivate let value: String
|
||||||
|
|
||||||
init(_ value: String) {
|
init(_ value: String) {
|
||||||
self.value = value
|
self.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
public func toString() -> String {
|
public func toString() -> String {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
|
@ -366,6 +366,7 @@
|
||||||
BD3B5BED1FC063BD001FDB3B /* InfotvOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = InfotvOS.plist; path = /Users/nabil/Documents/nabil/SwiftSoup/Sources/InfotvOS.plist; sourceTree = "<absolute>"; };
|
BD3B5BED1FC063BD001FDB3B /* InfotvOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = InfotvOS.plist; path = /Users/nabil/Documents/nabil/SwiftSoup/Sources/InfotvOS.plist; sourceTree = "<absolute>"; };
|
||||||
BD3B5C2F1FC06423001FDB3B /* SwiftSoup.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftSoup.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
BD3B5C2F1FC06423001FDB3B /* SwiftSoup.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftSoup.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
BD3B5C301FC06424001FDB3B /* InfoWatchOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = InfoWatchOS.plist; path = /Users/nabil/Documents/nabil/SwiftSoup/Sources/InfoWatchOS.plist; sourceTree = "<absolute>"; };
|
BD3B5C301FC06424001FDB3B /* InfoWatchOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = InfoWatchOS.plist; path = /Users/nabil/Documents/nabil/SwiftSoup/Sources/InfoWatchOS.plist; sourceTree = "<absolute>"; };
|
||||||
|
BD76883E206D8B6900B7F940 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -522,6 +523,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BD36975B20135EBB00D8FAC6 /* SwiftSoup.podspec */,
|
BD36975B20135EBB00D8FAC6 /* SwiftSoup.podspec */,
|
||||||
|
BD76883E206D8B6900B7F940 /* CHANGELOG.md */,
|
||||||
8CE418181DAA54A900240B42 /* Sources */,
|
8CE418181DAA54A900240B42 /* Sources */,
|
||||||
8CE418231DAA54A900240B42 /* Tests */,
|
8CE418231DAA54A900240B42 /* Tests */,
|
||||||
8CE418171DAA54A900240B42 /* Products */,
|
8CE418171DAA54A900240B42 /* Products */,
|
||||||
|
@ -667,6 +669,7 @@
|
||||||
8CE418121DAA54A900240B42 /* Frameworks */,
|
8CE418121DAA54A900240B42 /* Frameworks */,
|
||||||
8CE418131DAA54A900240B42 /* Headers */,
|
8CE418131DAA54A900240B42 /* Headers */,
|
||||||
8CE418141DAA54A900240B42 /* Resources */,
|
8CE418141DAA54A900240B42 /* Resources */,
|
||||||
|
BD76883F206D9DAC00B7F940 /* ShellScript */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
|
@ -756,7 +759,7 @@
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastSwiftUpdateCheck = 0800;
|
LastSwiftUpdateCheck = 0800;
|
||||||
LastUpgradeCheck = 0900;
|
LastUpgradeCheck = 0930;
|
||||||
ORGANIZATIONNAME = "Nabil Chatbi";
|
ORGANIZATIONNAME = "Nabil Chatbi";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
8CE418151DAA54A900240B42 = {
|
8CE418151DAA54A900240B42 = {
|
||||||
|
@ -836,6 +839,22 @@
|
||||||
};
|
};
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
BD76883F206D9DAC00B7F940 /* ShellScript */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "";
|
||||||
|
};
|
||||||
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
8CE418111DAA54A900240B42 /* Sources */ = {
|
8CE418111DAA54A900240B42 /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
|
@ -1144,6 +1163,7 @@
|
||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
CLANG_WARN_COMMA = YES;
|
CLANG_WARN_COMMA = YES;
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
@ -1151,6 +1171,7 @@
|
||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
@ -1206,6 +1227,7 @@
|
||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
CLANG_WARN_COMMA = YES;
|
CLANG_WARN_COMMA = YES;
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
@ -1213,6 +1235,7 @@
|
||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0900"
|
LastUpgradeVersion = "0930"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
@ -26,7 +26,6 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
language = ""
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<Testables>
|
<Testables>
|
||||||
<TestableReference
|
<TestableReference
|
||||||
|
@ -56,7 +55,6 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
language = ""
|
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0910"
|
LastUpgradeVersion = "0930"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
@ -26,7 +26,6 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
language = ""
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<Testables>
|
<Testables>
|
||||||
</Testables>
|
</Testables>
|
||||||
|
@ -46,7 +45,6 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
language = ""
|
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0910"
|
LastUpgradeVersion = "0930"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
@ -26,7 +26,6 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
language = ""
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<Testables>
|
<Testables>
|
||||||
</Testables>
|
</Testables>
|
||||||
|
@ -37,7 +36,6 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
language = ""
|
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0910"
|
LastUpgradeVersion = "0930"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
@ -26,7 +26,6 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
language = ""
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<Testables>
|
<Testables>
|
||||||
</Testables>
|
</Testables>
|
||||||
|
@ -37,7 +36,6 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
language = ""
|
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0900"
|
LastUpgradeVersion = "0930"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
@ -26,7 +26,6 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
language = ""
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<Testables>
|
<Testables>
|
||||||
<TestableReference
|
<TestableReference
|
||||||
|
@ -56,7 +55,6 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
language = ""
|
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
|
|
@ -31,5 +31,5 @@ XCTMain([
|
||||||
testCase(NodeTest.allTests),
|
testCase(NodeTest.allTests),
|
||||||
testCase(AttributeTest.allTests),
|
testCase(AttributeTest.allTests),
|
||||||
testCase(CleanerTest.allTests),
|
testCase(CleanerTest.allTests),
|
||||||
testCase(StringUtilTest.allTests),
|
testCase(StringUtilTest.allTests)
|
||||||
])
|
])
|
||||||
|
|
|
@ -115,7 +115,7 @@ class AttributeParseTest: XCTestCase {
|
||||||
("teststrictAttributeUnescapes", teststrictAttributeUnescapes),
|
("teststrictAttributeUnescapes", teststrictAttributeUnescapes),
|
||||||
("testmoreAttributeUnescapes", testmoreAttributeUnescapes),
|
("testmoreAttributeUnescapes", testmoreAttributeUnescapes),
|
||||||
("testparsesBooleanAttributes", testparsesBooleanAttributes),
|
("testparsesBooleanAttributes", testparsesBooleanAttributes),
|
||||||
("testdropsSlashFromAttributeName", testdropsSlashFromAttributeName),
|
("testdropsSlashFromAttributeName", testdropsSlashFromAttributeName)
|
||||||
]
|
]
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ class AttributeTest: XCTestCase {
|
||||||
XCTAssertEqual(s + "=\"A" + s + "B\"", attr.html())
|
XCTAssertEqual(s + "=\"A" + s + "B\"", attr.html())
|
||||||
XCTAssertEqual(attr.html(), attr.toString())
|
XCTAssertEqual(attr.html(), attr.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRemoveCaseSensitive()throws {
|
func testRemoveCaseSensitive()throws {
|
||||||
let a: Attributes = Attributes()
|
let a: Attributes = Attributes()
|
||||||
try a.put("Tot", "a&p")
|
try a.put("Tot", "a&p")
|
||||||
|
@ -39,22 +39,21 @@ class AttributeTest: XCTestCase {
|
||||||
try a.put("Hello", "There")
|
try a.put("Hello", "There")
|
||||||
try a.put("hello", "There")
|
try a.put("hello", "There")
|
||||||
try a.put("data-name", "Jsoup")
|
try a.put("data-name", "Jsoup")
|
||||||
|
|
||||||
XCTAssertEqual(5, a.size());
|
|
||||||
try a.remove(key: "Tot");
|
|
||||||
try a.remove(key: "Hello");
|
|
||||||
XCTAssertEqual(3, a.size());
|
|
||||||
XCTAssertTrue(a.hasKey(key: "tot"));
|
|
||||||
XCTAssertFalse(a.hasKey(key: "Tot"));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
XCTAssertEqual(5, a.size())
|
||||||
|
try a.remove(key: "Tot")
|
||||||
|
try a.remove(key: "Hello")
|
||||||
|
XCTAssertEqual(3, a.size())
|
||||||
|
XCTAssertTrue(a.hasKey(key: "tot"))
|
||||||
|
XCTAssertFalse(a.hasKey(key: "Tot"))
|
||||||
|
}
|
||||||
|
|
||||||
static var allTests = {
|
static var allTests = {
|
||||||
return [
|
return [
|
||||||
("testLinuxTestSuiteIncludesAllTests", testLinuxTestSuiteIncludesAllTests),
|
("testLinuxTestSuiteIncludesAllTests", testLinuxTestSuiteIncludesAllTests),
|
||||||
("testHtml", testHtml),
|
("testHtml", testHtml),
|
||||||
("testWithSupplementaryCharacterInAttributeKeyAndValue", testWithSupplementaryCharacterInAttributeKeyAndValue),
|
("testWithSupplementaryCharacterInAttributeKeyAndValue", testWithSupplementaryCharacterInAttributeKeyAndValue),
|
||||||
("testRemoveCaseSensitive",testRemoveCaseSensitive)
|
("testRemoveCaseSensitive", testRemoveCaseSensitive)
|
||||||
]
|
]
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import XCTest
|
||||||
@testable import SwiftSoup
|
@testable import SwiftSoup
|
||||||
|
|
||||||
class CleanerTest: XCTestCase {
|
class CleanerTest: XCTestCase {
|
||||||
|
|
||||||
func testLinuxTestSuiteIncludesAllTests() {
|
func testLinuxTestSuiteIncludesAllTests() {
|
||||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||||
let thisClass = type(of: self)
|
let thisClass = type(of: self)
|
||||||
|
@ -19,145 +19,144 @@ class CleanerTest: XCTestCase {
|
||||||
XCTAssertEqual(linuxCount, darwinCount, "\(darwinCount - linuxCount) tests are missing from allTests")
|
XCTAssertEqual(linuxCount, darwinCount, "\(darwinCount - linuxCount) tests are missing from allTests")
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func testHandlesCustomProtocols()throws{
|
func testHandlesCustomProtocols()throws {
|
||||||
let html = "<img src='cid:12345' /> <img src='data:gzzt' />"
|
let html = "<img src='cid:12345' /> <img src='data:gzzt' />"
|
||||||
// let dropped = try SwiftSoup.clean(html, Whitelist.basicWithImages())
|
// let dropped = try SwiftSoup.clean(html, Whitelist.basicWithImages())
|
||||||
// XCTAssertEqual("<img> \n<img>", dropped)
|
// XCTAssertEqual("<img> \n<img>", dropped)
|
||||||
|
|
||||||
let preserved = try SwiftSoup.clean(html, Whitelist.basicWithImages().addProtocols("img", "src", "cid", "data"))
|
let preserved = try SwiftSoup.clean(html, Whitelist.basicWithImages().addProtocols("img", "src", "cid", "data"))
|
||||||
XCTAssertEqual("<img src=\"cid:12345\"> \n<img src=\"data:gzzt\">", preserved)
|
XCTAssertEqual("<img src=\"cid:12345\"> \n<img src=\"data:gzzt\">", preserved)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func testSimpleBehaviourTest()throws {
|
func testSimpleBehaviourTest()throws {
|
||||||
let h = "<div><p class=foo><a href='http://evil.com'>Hello <b id=bar>there</b>!</a></div>"
|
let h = "<div><p class=foo><a href='http://evil.com'>Hello <b id=bar>there</b>!</a></div>"
|
||||||
let cleanHtml = try SwiftSoup.clean(h, Whitelist.simpleText())
|
let cleanHtml = try SwiftSoup.clean(h, Whitelist.simpleText())
|
||||||
XCTAssertEqual("Hello <b>there</b>!", TextUtil.stripNewlines(cleanHtml!))
|
XCTAssertEqual("Hello <b>there</b>!", TextUtil.stripNewlines(cleanHtml!))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSimpleBehaviourTest2()throws {
|
func testSimpleBehaviourTest2()throws {
|
||||||
let h = "Hello <b>there</b>!"
|
let h = "Hello <b>there</b>!"
|
||||||
let cleanHtml = try SwiftSoup.clean(h, Whitelist.simpleText())
|
let cleanHtml = try SwiftSoup.clean(h, Whitelist.simpleText())
|
||||||
|
|
||||||
XCTAssertEqual("Hello <b>there</b>!", TextUtil.stripNewlines(cleanHtml!))
|
XCTAssertEqual("Hello <b>there</b>!", TextUtil.stripNewlines(cleanHtml!))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testBasicBehaviourTest()throws {
|
func testBasicBehaviourTest()throws {
|
||||||
let h = "<div><p><a href='javascript:sendAllMoney()'>Dodgy</a> <A HREF='HTTP://nice.com'>Nice</a></p><blockquote>Hello</blockquote>"
|
let h = "<div><p><a href='javascript:sendAllMoney()'>Dodgy</a> <A HREF='HTTP://nice.com'>Nice</a></p><blockquote>Hello</blockquote>"
|
||||||
let cleanHtml = try SwiftSoup.clean(h, Whitelist.basic())
|
let cleanHtml = try SwiftSoup.clean(h, Whitelist.basic())
|
||||||
|
|
||||||
XCTAssertEqual("<p><a rel=\"nofollow\">Dodgy</a> <a href=\"HTTP://nice.com\" rel=\"nofollow\">Nice</a></p><blockquote>Hello</blockquote>",
|
XCTAssertEqual("<p><a rel=\"nofollow\">Dodgy</a> <a href=\"HTTP://nice.com\" rel=\"nofollow\">Nice</a></p><blockquote>Hello</blockquote>",
|
||||||
TextUtil.stripNewlines(cleanHtml!))
|
TextUtil.stripNewlines(cleanHtml!))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testBasicWithImagesTest()throws {
|
func testBasicWithImagesTest()throws {
|
||||||
let h = "<div><p><img src='http://example.com/' alt=Image></p><p><img src='ftp://ftp.example.com'></p></div>"
|
let h = "<div><p><img src='http://example.com/' alt=Image></p><p><img src='ftp://ftp.example.com'></p></div>"
|
||||||
let cleanHtml = try SwiftSoup.clean(h, Whitelist.basicWithImages())
|
let cleanHtml = try SwiftSoup.clean(h, Whitelist.basicWithImages())
|
||||||
XCTAssertEqual("<p><img src=\"http://example.com/\" alt=\"Image\"></p><p><img></p>", TextUtil.stripNewlines(cleanHtml!))
|
XCTAssertEqual("<p><img src=\"http://example.com/\" alt=\"Image\"></p><p><img></p>", TextUtil.stripNewlines(cleanHtml!))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRelaxed()throws {
|
func testRelaxed()throws {
|
||||||
let h = "<h1>Head</h1><table><tr><td>One<td>Two</td></tr></table>"
|
let h = "<h1>Head</h1><table><tr><td>One<td>Two</td></tr></table>"
|
||||||
let cleanHtml = try SwiftSoup.clean(h, Whitelist.relaxed())
|
let cleanHtml = try SwiftSoup.clean(h, Whitelist.relaxed())
|
||||||
XCTAssertEqual("<h1>Head</h1><table><tbody><tr><td>One</td><td>Two</td></tr></tbody></table>", TextUtil.stripNewlines(cleanHtml!))
|
XCTAssertEqual("<h1>Head</h1><table><tbody><tr><td>One</td><td>Two</td></tr></tbody></table>", TextUtil.stripNewlines(cleanHtml!))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRemoveTags()throws {
|
func testRemoveTags()throws {
|
||||||
let h = "<div><p><A HREF='HTTP://nice.com'>Nice</a></p><blockquote>Hello</blockquote>"
|
let h = "<div><p><A HREF='HTTP://nice.com'>Nice</a></p><blockquote>Hello</blockquote>"
|
||||||
let cleanHtml = try SwiftSoup.clean(h, Whitelist.basic().removeTags("a"))
|
let cleanHtml = try SwiftSoup.clean(h, Whitelist.basic().removeTags("a"))
|
||||||
|
|
||||||
XCTAssertEqual("<p>Nice</p><blockquote>Hello</blockquote>", TextUtil.stripNewlines(cleanHtml!))
|
XCTAssertEqual("<p>Nice</p><blockquote>Hello</blockquote>", TextUtil.stripNewlines(cleanHtml!))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRemoveAttributes()throws{
|
func testRemoveAttributes()throws {
|
||||||
let h = "<div><p>Nice</p><blockquote cite='http://example.com/quotations'>Hello</blockquote>"
|
let h = "<div><p>Nice</p><blockquote cite='http://example.com/quotations'>Hello</blockquote>"
|
||||||
let cleanHtml = try SwiftSoup.clean(h, Whitelist.basic().removeAttributes("blockquote", "cite"))
|
let cleanHtml = try SwiftSoup.clean(h, Whitelist.basic().removeAttributes("blockquote", "cite"))
|
||||||
|
|
||||||
XCTAssertEqual("<p>Nice</p><blockquote>Hello</blockquote>", TextUtil.stripNewlines(cleanHtml!))
|
XCTAssertEqual("<p>Nice</p><blockquote>Hello</blockquote>", TextUtil.stripNewlines(cleanHtml!))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRemoveEnforcedAttributes()throws{
|
func testRemoveEnforcedAttributes()throws {
|
||||||
let h = "<div><p><A HREF='HTTP://nice.com'>Nice</a></p><blockquote>Hello</blockquote>"
|
let h = "<div><p><A HREF='HTTP://nice.com'>Nice</a></p><blockquote>Hello</blockquote>"
|
||||||
let cleanHtml = try SwiftSoup.clean(h, Whitelist.basic().removeEnforcedAttribute("a", "rel"))
|
let cleanHtml = try SwiftSoup.clean(h, Whitelist.basic().removeEnforcedAttribute("a", "rel"))
|
||||||
|
|
||||||
XCTAssertEqual("<p><a href=\"HTTP://nice.com\">Nice</a></p><blockquote>Hello</blockquote>",
|
XCTAssertEqual("<p><a href=\"HTTP://nice.com\">Nice</a></p><blockquote>Hello</blockquote>",
|
||||||
TextUtil.stripNewlines(cleanHtml!))
|
TextUtil.stripNewlines(cleanHtml!))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRemoveProtocols()throws{
|
func testRemoveProtocols()throws {
|
||||||
let h = "<p>Contact me <a href='mailto:info@example.com'>here</a></p>"
|
let h = "<p>Contact me <a href='mailto:info@example.com'>here</a></p>"
|
||||||
let cleanHtml = try SwiftSoup.clean(h, Whitelist.basic().removeProtocols("a", "href", "ftp", "mailto"))
|
let cleanHtml = try SwiftSoup.clean(h, Whitelist.basic().removeProtocols("a", "href", "ftp", "mailto"))
|
||||||
|
|
||||||
XCTAssertEqual("<p>Contact me <a rel=\"nofollow\">here</a></p>",
|
XCTAssertEqual("<p>Contact me <a rel=\"nofollow\">here</a></p>",
|
||||||
TextUtil.stripNewlines(cleanHtml!))
|
TextUtil.stripNewlines(cleanHtml!))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDropComments()throws{
|
func testDropComments()throws {
|
||||||
let h = "<p>Hello<!-- no --></p>"
|
let h = "<p>Hello<!-- no --></p>"
|
||||||
let cleanHtml = try SwiftSoup.clean(h, Whitelist.relaxed())
|
let cleanHtml = try SwiftSoup.clean(h, Whitelist.relaxed())
|
||||||
XCTAssertEqual("<p>Hello</p>", cleanHtml)
|
XCTAssertEqual("<p>Hello</p>", cleanHtml)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDropXmlProc()throws{
|
func testDropXmlProc()throws {
|
||||||
let h = "<?import namespace=\"xss\"><p>Hello</p>"
|
let h = "<?import namespace=\"xss\"><p>Hello</p>"
|
||||||
let cleanHtml = try SwiftSoup.clean(h, Whitelist.relaxed())
|
let cleanHtml = try SwiftSoup.clean(h, Whitelist.relaxed())
|
||||||
XCTAssertEqual("<p>Hello</p>", cleanHtml)
|
XCTAssertEqual("<p>Hello</p>", cleanHtml)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDropScript()throws{
|
func testDropScript()throws {
|
||||||
let h = "<SCRIPT SRC=//ha.ckers.org/.j><SCRIPT>alert(/XSS/.source)</SCRIPT>"
|
let h = "<SCRIPT SRC=//ha.ckers.org/.j><SCRIPT>alert(/XSS/.source)</SCRIPT>"
|
||||||
let cleanHtml = try SwiftSoup.clean(h, Whitelist.relaxed())
|
let cleanHtml = try SwiftSoup.clean(h, Whitelist.relaxed())
|
||||||
XCTAssertEqual("", cleanHtml)
|
XCTAssertEqual("", cleanHtml)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDropImageScript()throws{
|
func testDropImageScript()throws {
|
||||||
let h = "<IMG SRC=\"javascript:alert('XSS')\">"
|
let h = "<IMG SRC=\"javascript:alert('XSS')\">"
|
||||||
let cleanHtml = try SwiftSoup.clean(h, Whitelist.relaxed())
|
let cleanHtml = try SwiftSoup.clean(h, Whitelist.relaxed())
|
||||||
XCTAssertEqual("<img>", cleanHtml)
|
XCTAssertEqual("<img>", cleanHtml)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCleanJavascriptHref()throws{
|
func testCleanJavascriptHref()throws {
|
||||||
let h = "<A HREF=\"javascript:document.location='http://www.google.com/'\">XSS</A>"
|
let h = "<A HREF=\"javascript:document.location='http://www.google.com/'\">XSS</A>"
|
||||||
let cleanHtml = try SwiftSoup.clean(h, Whitelist.relaxed())
|
let cleanHtml = try SwiftSoup.clean(h, Whitelist.relaxed())
|
||||||
XCTAssertEqual("<a>XSS</a>", cleanHtml)
|
XCTAssertEqual("<a>XSS</a>", cleanHtml)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCleanAnchorProtocol()throws{
|
func testCleanAnchorProtocol()throws {
|
||||||
let validAnchor = "<a href=\"#valid\">Valid anchor</a>"
|
let validAnchor = "<a href=\"#valid\">Valid anchor</a>"
|
||||||
let invalidAnchor = "<a href=\"#anchor with spaces\">Invalid anchor</a>"
|
let invalidAnchor = "<a href=\"#anchor with spaces\">Invalid anchor</a>"
|
||||||
|
|
||||||
// A Whitelist that does not allow anchors will strip them out.
|
// A Whitelist that does not allow anchors will strip them out.
|
||||||
var cleanHtml = try SwiftSoup.clean(validAnchor, Whitelist.relaxed())
|
var cleanHtml = try SwiftSoup.clean(validAnchor, Whitelist.relaxed())
|
||||||
XCTAssertEqual("<a>Valid anchor</a>", cleanHtml)
|
XCTAssertEqual("<a>Valid anchor</a>", cleanHtml)
|
||||||
|
|
||||||
cleanHtml = try SwiftSoup.clean(invalidAnchor, Whitelist.relaxed())
|
cleanHtml = try SwiftSoup.clean(invalidAnchor, Whitelist.relaxed())
|
||||||
XCTAssertEqual("<a>Invalid anchor</a>", cleanHtml)
|
XCTAssertEqual("<a>Invalid anchor</a>", cleanHtml)
|
||||||
|
|
||||||
// A Whitelist that allows them will keep them.
|
// A Whitelist that allows them will keep them.
|
||||||
let relaxedWithAnchor: Whitelist = try Whitelist.relaxed().addProtocols("a", "href", "#")
|
let relaxedWithAnchor: Whitelist = try Whitelist.relaxed().addProtocols("a", "href", "#")
|
||||||
|
|
||||||
cleanHtml = try SwiftSoup.clean(validAnchor, relaxedWithAnchor)
|
cleanHtml = try SwiftSoup.clean(validAnchor, relaxedWithAnchor)
|
||||||
XCTAssertEqual(validAnchor, cleanHtml)
|
XCTAssertEqual(validAnchor, cleanHtml)
|
||||||
|
|
||||||
// An invalid anchor is never valid.
|
// An invalid anchor is never valid.
|
||||||
cleanHtml = try SwiftSoup.clean(invalidAnchor, relaxedWithAnchor)
|
cleanHtml = try SwiftSoup.clean(invalidAnchor, relaxedWithAnchor)
|
||||||
XCTAssertEqual("<a>Invalid anchor</a>", cleanHtml)
|
XCTAssertEqual("<a>Invalid anchor</a>", cleanHtml)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDropsUnknownTags()throws{
|
func testDropsUnknownTags()throws {
|
||||||
let h = "<p><custom foo=true>Test</custom></p>"
|
let h = "<p><custom foo=true>Test</custom></p>"
|
||||||
let cleanHtml = try SwiftSoup.clean(h, Whitelist.relaxed())
|
let cleanHtml = try SwiftSoup.clean(h, Whitelist.relaxed())
|
||||||
XCTAssertEqual("<p>Test</p>", cleanHtml)
|
XCTAssertEqual("<p>Test</p>", cleanHtml)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testtestHandlesEmptyAttributes()throws{
|
func testtestHandlesEmptyAttributes()throws {
|
||||||
let h = "<img alt=\"\" src= unknown=''>"
|
let h = "<img alt=\"\" src= unknown=''>"
|
||||||
let cleanHtml = try SwiftSoup.clean(h, Whitelist.basicWithImages())
|
let cleanHtml = try SwiftSoup.clean(h, Whitelist.basicWithImages())
|
||||||
XCTAssertEqual("<img alt=\"\">", cleanHtml)
|
XCTAssertEqual("<img alt=\"\">", cleanHtml)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testIsValid()throws{
|
func testIsValid()throws {
|
||||||
let ok = "<p>Test <b><a href='http://example.com/'>OK</a></b></p>"
|
let ok = "<p>Test <b><a href='http://example.com/'>OK</a></b></p>"
|
||||||
let nok1 = "<p><script></script>Not <b>OK</b></p>"
|
let nok1 = "<p><script></script>Not <b>OK</b></p>"
|
||||||
let nok2 = "<p align=right>Test Not <b>OK</b></p>"
|
let nok2 = "<p align=right>Test Not <b>OK</b></p>"
|
||||||
|
@ -167,39 +166,37 @@ class CleanerTest: XCTestCase {
|
||||||
XCTAssertFalse(try SwiftSoup.isValid(nok2, Whitelist.basic()))
|
XCTAssertFalse(try SwiftSoup.isValid(nok2, Whitelist.basic()))
|
||||||
XCTAssertFalse(try SwiftSoup.isValid(nok3, Whitelist.basic()))
|
XCTAssertFalse(try SwiftSoup.isValid(nok3, Whitelist.basic()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testResolvesRelativeLinks()throws{
|
func testResolvesRelativeLinks()throws {
|
||||||
let html = "<a href='/foo'>Link</a><img src='/bar'>"
|
let html = "<a href='/foo'>Link</a><img src='/bar'>"
|
||||||
let clean = try SwiftSoup.clean(html, "http://example.com/", Whitelist.basicWithImages())
|
let clean = try SwiftSoup.clean(html, "http://example.com/", Whitelist.basicWithImages())
|
||||||
XCTAssertEqual("<a href=\"http://example.com/foo\" rel=\"nofollow\">Link</a>\n<img src=\"http://example.com/bar\">", clean)
|
XCTAssertEqual("<a href=\"http://example.com/foo\" rel=\"nofollow\">Link</a>\n<img src=\"http://example.com/bar\">", clean)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPreservesRelativeLinksIfConfigured()throws{
|
func testPreservesRelativeLinksIfConfigured()throws {
|
||||||
let html = "<a href='/foo'>Link</a><img src='/bar'> <img src='javascript:alert()'>"
|
let html = "<a href='/foo'>Link</a><img src='/bar'> <img src='javascript:alert()'>"
|
||||||
let clean = try SwiftSoup.clean(html, "http://example.com/", Whitelist.basicWithImages().preserveRelativeLinks(true))
|
let clean = try SwiftSoup.clean(html, "http://example.com/", Whitelist.basicWithImages().preserveRelativeLinks(true))
|
||||||
XCTAssertEqual("<a href=\"/foo\" rel=\"nofollow\">Link</a>\n<img src=\"/bar\"> \n<img>", clean)
|
XCTAssertEqual("<a href=\"/foo\" rel=\"nofollow\">Link</a>\n<img src=\"/bar\"> \n<img>", clean)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDropsUnresolvableRelativeLinks()throws{
|
func testDropsUnresolvableRelativeLinks()throws {
|
||||||
let html = "<a href='/foo'>Link</a>"
|
let html = "<a href='/foo'>Link</a>"
|
||||||
let clean = try SwiftSoup.clean(html, Whitelist.basic())
|
let clean = try SwiftSoup.clean(html, Whitelist.basic())
|
||||||
XCTAssertEqual("<a rel=\"nofollow\">Link</a>", clean)
|
XCTAssertEqual("<a rel=\"nofollow\">Link</a>", clean)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testHandlesAllPseudoTag()throws {
|
||||||
|
|
||||||
func testHandlesAllPseudoTag()throws{
|
|
||||||
let html = "<p class='foo' src='bar'><a class='qux'>link</a></p>"
|
let html = "<p class='foo' src='bar'><a class='qux'>link</a></p>"
|
||||||
let whitelist: Whitelist = try Whitelist()
|
let whitelist: Whitelist = try Whitelist()
|
||||||
.addAttributes(":all", "class")
|
.addAttributes(":all", "class")
|
||||||
.addAttributes("p", "style")
|
.addAttributes("p", "style")
|
||||||
.addTags("p", "a")
|
.addTags("p", "a")
|
||||||
|
|
||||||
let clean = try SwiftSoup.clean(html, whitelist)
|
let clean = try SwiftSoup.clean(html, whitelist)
|
||||||
XCTAssertEqual("<p class=\"foo\"><a class=\"qux\">link</a></p>", clean)
|
XCTAssertEqual("<p class=\"foo\"><a class=\"qux\">link</a></p>", clean)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAddsTagOnAttributesIfNotSet()throws{
|
func testAddsTagOnAttributesIfNotSet()throws {
|
||||||
let html = "<p class='foo' src='bar'>One</p>"
|
let html = "<p class='foo' src='bar'>One</p>"
|
||||||
let whitelist = try Whitelist()
|
let whitelist = try Whitelist()
|
||||||
.addAttributes("p", "class")
|
.addAttributes("p", "class")
|
||||||
|
@ -207,7 +204,7 @@ class CleanerTest: XCTestCase {
|
||||||
let clean = try SwiftSoup.clean(html, whitelist)
|
let clean = try SwiftSoup.clean(html, whitelist)
|
||||||
XCTAssertEqual("<p class=\"foo\">One</p>", clean)
|
XCTAssertEqual("<p class=\"foo\">One</p>", clean)
|
||||||
}
|
}
|
||||||
|
|
||||||
// func testSupplyOutputSettings()throws{
|
// func testSupplyOutputSettings()throws{
|
||||||
// // test that one can override the default document output settings
|
// // test that one can override the default document output settings
|
||||||
// let os: OutputSettings = OutputSettings()
|
// let os: OutputSettings = OutputSettings()
|
||||||
|
@ -230,29 +227,28 @@ class CleanerTest: XCTestCase {
|
||||||
// let customOut2 = try SwiftSoup.clean(html, "http://foo.com/", Whitelist.relaxed(), os)
|
// let customOut2 = try SwiftSoup.clean(html, "http://foo.com/", Whitelist.relaxed(), os)
|
||||||
// XCTAssertEqual("<div><p>ℬ</p></div>", customOut2)
|
// XCTAssertEqual("<div><p>ℬ</p></div>", customOut2)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
func testHandlesFramesets()throws{
|
func testHandlesFramesets()throws {
|
||||||
let dirty = "<html><head><script></script><noscript></noscript></head><frameset><frame src=\"foo\" /><frame src=\"foo\" /></frameset></html>"
|
let dirty = "<html><head><script></script><noscript></noscript></head><frameset><frame src=\"foo\" /><frame src=\"foo\" /></frameset></html>"
|
||||||
let clean = try SwiftSoup.clean(dirty, Whitelist.basic())
|
let clean = try SwiftSoup.clean(dirty, Whitelist.basic())
|
||||||
XCTAssertEqual("", clean) // nothing good can come out of that
|
XCTAssertEqual("", clean) // nothing good can come out of that
|
||||||
|
|
||||||
let dirtyDoc: Document = try SwiftSoup.parse(dirty)
|
let dirtyDoc: Document = try SwiftSoup.parse(dirty)
|
||||||
let cleanDoc: Document? = try Cleaner(Whitelist.basic()).clean(dirtyDoc)
|
let cleanDoc: Document? = try Cleaner(Whitelist.basic()).clean(dirtyDoc)
|
||||||
XCTAssertFalse(cleanDoc == nil)
|
XCTAssertFalse(cleanDoc == nil)
|
||||||
XCTAssertEqual(0, cleanDoc?.body()?.childNodeSize())
|
XCTAssertEqual(0, cleanDoc?.body()?.childNodeSize())
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCleansInternationalText()throws{
|
func testCleansInternationalText()throws {
|
||||||
XCTAssertEqual("привет", try SwiftSoup.clean("привет", Whitelist.none()))
|
XCTAssertEqual("привет", try SwiftSoup.clean("привет", Whitelist.none()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testScriptTagInWhiteList()throws {
|
||||||
func testScriptTagInWhiteList()throws{
|
|
||||||
let whitelist: Whitelist = try Whitelist.relaxed()
|
let whitelist: Whitelist = try Whitelist.relaxed()
|
||||||
try whitelist.addTags( "script" )
|
try whitelist.addTags( "script" )
|
||||||
XCTAssertTrue( try SwiftSoup.isValid("Hello<script>alert('Doh')</script>World !", whitelist ) )
|
XCTAssertTrue( try SwiftSoup.isValid("Hello<script>alert('Doh')</script>World !", whitelist ) )
|
||||||
}
|
}
|
||||||
|
|
||||||
// func testHandlesFramesetsw()throws{
|
// func testHandlesFramesetsw()throws{
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
@ -322,6 +318,5 @@ class CleanerTest: XCTestCase {
|
||||||
("testScriptTagInWhiteList", testScriptTagInWhiteList)
|
("testScriptTagInWhiteList", testScriptTagInWhiteList)
|
||||||
]
|
]
|
||||||
}()
|
}()
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ class CssTest: XCTestCase {
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
|
|
||||||
let sb: StringBuilder = StringBuilder(string:"<html><head></head><body>")
|
let sb: StringBuilder = StringBuilder(string: "<html><head></head><body>")
|
||||||
|
|
||||||
sb.append("<div id='pseudo'>")
|
sb.append("<div id='pseudo'>")
|
||||||
for i in 1...10 {
|
for i in 1...10 {
|
||||||
|
|
|
@ -13,7 +13,7 @@ class DocumentTest: XCTestCase {
|
||||||
|
|
||||||
private static let charsetUtf8 = String.Encoding.utf8
|
private static let charsetUtf8 = String.Encoding.utf8
|
||||||
private static let charsetIso8859 = String.Encoding.iso2022JP //"ISO-8859-1"
|
private static let charsetIso8859 = String.Encoding.iso2022JP //"ISO-8859-1"
|
||||||
|
|
||||||
// func testT()throws
|
// func testT()throws
|
||||||
// {
|
// {
|
||||||
// do{
|
// do{
|
||||||
|
@ -59,7 +59,7 @@ class DocumentTest: XCTestCase {
|
||||||
XCTAssertEqual(linuxCount, darwinCount, "\(darwinCount - linuxCount) tests are missing from allTests")
|
XCTAssertEqual(linuxCount, darwinCount, "\(darwinCount - linuxCount) tests are missing from allTests")
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSetTextPreservesDocumentStructure() {
|
func testSetTextPreservesDocumentStructure() {
|
||||||
do {
|
do {
|
||||||
let doc: Document = try SwiftSoup.parse("<p>Hello</p>")
|
let doc: Document = try SwiftSoup.parse("<p>Hello</p>")
|
||||||
|
@ -441,10 +441,8 @@ class DocumentTest: XCTestCase {
|
||||||
|
|
||||||
return doc
|
return doc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testThai() {
|
||||||
func testThai()
|
|
||||||
{
|
|
||||||
let str = "บังคับ"
|
let str = "บังคับ"
|
||||||
guard let doc = try? SwiftSoup.parse(str) else {
|
guard let doc = try? SwiftSoup.parse(str) else {
|
||||||
XCTFail()
|
XCTFail()
|
||||||
|
@ -454,7 +452,7 @@ class DocumentTest: XCTestCase {
|
||||||
return}
|
return}
|
||||||
XCTAssertEqual("<html>\n <head></head>\n <body>\n บังคับ\n </body>\n</html>", txt)
|
XCTAssertEqual("<html>\n <head></head>\n <body>\n บังคับ\n </body>\n</html>", txt)
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo:
|
//todo:
|
||||||
// func testShiftJisRoundtrip()throws {
|
// func testShiftJisRoundtrip()throws {
|
||||||
// let input =
|
// let input =
|
||||||
|
@ -477,10 +475,10 @@ class DocumentTest: XCTestCase {
|
||||||
// assertTrue("Should have contained a ' ' or a ' '.",
|
// assertTrue("Should have contained a ' ' or a ' '.",
|
||||||
// output.contains(" ") || output.contains(" "));
|
// output.contains(" ") || output.contains(" "));
|
||||||
// }
|
// }
|
||||||
|
|
||||||
func testNewLine(){
|
func testNewLine() {
|
||||||
let h = "<html><body><div>\r\n<div dir=\"ltr\">\r\n<div id=\"divtagdefaultwrapper\"><font face=\"Calibri,Helvetica,sans-serif\" size=\"3\" color=\"black\"><span style=\"font-size:12pt;\" id=\"divtagdefaultwrapper\">\r\n<div style=\"margin-top:0;margin-bottom:0;\"> TEST</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\">TEST</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\">TEST</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\">TEST</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\">TEST</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\">TEST</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\">TEST</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\">TEST</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\">TEST</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\">TEST</div>\r\n</span></font></div>\r\n</div>\r\n</div>\r\n</body></html>"
|
let h = "<html><body><div>\r\n<div dir=\"ltr\">\r\n<div id=\"divtagdefaultwrapper\"><font face=\"Calibri,Helvetica,sans-serif\" size=\"3\" color=\"black\"><span style=\"font-size:12pt;\" id=\"divtagdefaultwrapper\">\r\n<div style=\"margin-top:0;margin-bottom:0;\"> TEST</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\">TEST</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\">TEST</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\">TEST</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\">TEST</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\">TEST</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\">TEST</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\">TEST</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\">TEST</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\"><br>\r\n\r\n</div>\r\n<div style=\"margin-top:0;margin-bottom:0;\">TEST</div>\r\n</span></font></div>\r\n</div>\r\n</div>\r\n</body></html>"
|
||||||
|
|
||||||
let doc: Document = try! SwiftSoup.parse(h)
|
let doc: Document = try! SwiftSoup.parse(h)
|
||||||
let text = try! doc.text()
|
let text = try! doc.text()
|
||||||
XCTAssertEqual(text, "TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST")
|
XCTAssertEqual(text, "TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST")
|
||||||
|
@ -514,8 +512,8 @@ class DocumentTest: XCTestCase {
|
||||||
("testMetaCharsetUpdateXmlDisabled", testMetaCharsetUpdateXmlDisabled),
|
("testMetaCharsetUpdateXmlDisabled", testMetaCharsetUpdateXmlDisabled),
|
||||||
("testMetaCharsetUpdateXmlDisabledNoChanges", testMetaCharsetUpdateXmlDisabledNoChanges),
|
("testMetaCharsetUpdateXmlDisabledNoChanges", testMetaCharsetUpdateXmlDisabledNoChanges),
|
||||||
("testMetaCharsetUpdatedDisabledPerDefault", testMetaCharsetUpdatedDisabledPerDefault),
|
("testMetaCharsetUpdatedDisabledPerDefault", testMetaCharsetUpdatedDisabledPerDefault),
|
||||||
("testThai",testThai),
|
("testThai", testThai),
|
||||||
("testNewLine", testNewLine),
|
("testNewLine", testNewLine)
|
||||||
]
|
]
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ class DocumentTypeTest: XCTestCase {
|
||||||
("testConstructorValidationOkWithBlankName", testConstructorValidationOkWithBlankName),
|
("testConstructorValidationOkWithBlankName", testConstructorValidationOkWithBlankName),
|
||||||
("testConstructorValidationThrowsExceptionOnNulls", testConstructorValidationThrowsExceptionOnNulls),
|
("testConstructorValidationThrowsExceptionOnNulls", testConstructorValidationThrowsExceptionOnNulls),
|
||||||
("testConstructorValidationOkWithBlankPublicAndSystemIds", testConstructorValidationOkWithBlankPublicAndSystemIds),
|
("testConstructorValidationOkWithBlankPublicAndSystemIds", testConstructorValidationOkWithBlankPublicAndSystemIds),
|
||||||
("testOuterHtmlGeneration", testOuterHtmlGeneration),
|
("testOuterHtmlGeneration", testOuterHtmlGeneration)
|
||||||
]
|
]
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
|
@ -823,7 +823,7 @@ class ElementTest: XCTestCase {
|
||||||
|
|
||||||
// Update the class names to a fresh set
|
// Update the class names to a fresh set
|
||||||
let newSet = OrderedSet<String>()
|
let newSet = OrderedSet<String>()
|
||||||
newSet.append(contentsOf:set1)
|
newSet.append(contentsOf: set1)
|
||||||
//newSet["c3"] //todo: nabil not a set , add == append but not change exists c3
|
//newSet["c3"] //todo: nabil not a set , add == append but not change exists c3
|
||||||
|
|
||||||
try div.classNames(newSet)
|
try div.classNames(newSet)
|
||||||
|
@ -936,7 +936,7 @@ class ElementTest: XCTestCase {
|
||||||
XCTAssertEqual(1, els.size())
|
XCTAssertEqual(1, els.size())
|
||||||
XCTAssertEqual("html > body > fb|comments", try els.get(0).cssSelector())
|
XCTAssertEqual("html > body > fb|comments", try els.get(0).cssSelector())
|
||||||
}
|
}
|
||||||
|
|
||||||
func testChainedRemoveAttributes()throws {
|
func testChainedRemoveAttributes()throws {
|
||||||
let html = "<a one two three four>Text</a>"
|
let html = "<a one two three four>Text</a>"
|
||||||
let doc = try SwiftSoup.parse(html)
|
let doc = try SwiftSoup.parse(html)
|
||||||
|
@ -946,34 +946,32 @@ class ElementTest: XCTestCase {
|
||||||
.removeAttr("two")
|
.removeAttr("two")
|
||||||
.removeAttr("three")
|
.removeAttr("three")
|
||||||
.removeAttr("four")
|
.removeAttr("four")
|
||||||
.removeAttr("five");
|
.removeAttr("five")
|
||||||
XCTAssertEqual("<a>Text</a>", try a.outerHtml());
|
XCTAssertEqual("<a>Text</a>", try a.outerHtml())
|
||||||
}
|
}
|
||||||
|
|
||||||
func testIs()throws {
|
func testIs()throws {
|
||||||
let html = "<div><p>One <a class=big>Two</a> Three</p><p>Another</p>"
|
let html = "<div><p>One <a class=big>Two</a> Three</p><p>Another</p>"
|
||||||
let doc: Document = try SwiftSoup.parse(html)
|
let doc: Document = try SwiftSoup.parse(html)
|
||||||
let p: Element = try doc.select("p").first()!
|
let p: Element = try doc.select("p").first()!
|
||||||
|
|
||||||
try XCTAssertTrue(p.iS("p"));
|
try XCTAssertTrue(p.iS("p"))
|
||||||
try XCTAssertFalse(p.iS("div"));
|
try XCTAssertFalse(p.iS("div"))
|
||||||
try XCTAssertTrue(p.iS("p:has(a)"));
|
try XCTAssertTrue(p.iS("p:has(a)"))
|
||||||
try XCTAssertTrue(p.iS("p:first-child"));
|
try XCTAssertTrue(p.iS("p:first-child"))
|
||||||
try XCTAssertFalse(p.iS("p:last-child"));
|
try XCTAssertFalse(p.iS("p:last-child"))
|
||||||
try XCTAssertTrue(p.iS("*"));
|
try XCTAssertTrue(p.iS("*"))
|
||||||
try XCTAssertTrue(p.iS("div p"));
|
try XCTAssertTrue(p.iS("div p"))
|
||||||
|
|
||||||
let q: Element = try doc.select("p").last()!
|
let q: Element = try doc.select("p").last()!
|
||||||
try XCTAssertTrue(q.iS("p"));
|
try XCTAssertTrue(q.iS("p"))
|
||||||
try XCTAssertTrue(q.iS("p ~ p"));
|
try XCTAssertTrue(q.iS("p ~ p"))
|
||||||
try XCTAssertTrue(q.iS("p + p"));
|
try XCTAssertTrue(q.iS("p + p"))
|
||||||
try XCTAssertTrue(q.iS("p:last-child"));
|
try XCTAssertTrue(q.iS("p:last-child"))
|
||||||
try XCTAssertFalse(q.iS("p a"));
|
try XCTAssertFalse(q.iS("p a"))
|
||||||
try XCTAssertFalse(q.iS("a"));
|
try XCTAssertFalse(q.iS("a"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static var allTests = {
|
static var allTests = {
|
||||||
return [
|
return [
|
||||||
("testLinuxTestSuiteIncludesAllTests", testLinuxTestSuiteIncludesAllTests),
|
("testLinuxTestSuiteIncludesAllTests", testLinuxTestSuiteIncludesAllTests),
|
||||||
|
@ -1046,8 +1044,8 @@ class ElementTest: XCTestCase {
|
||||||
("testAppendMustCorrectlyMoveChildrenInsideOneParentElement", testAppendMustCorrectlyMoveChildrenInsideOneParentElement),
|
("testAppendMustCorrectlyMoveChildrenInsideOneParentElement", testAppendMustCorrectlyMoveChildrenInsideOneParentElement),
|
||||||
("testHashcodeIsStableWithContentChanges", testHashcodeIsStableWithContentChanges),
|
("testHashcodeIsStableWithContentChanges", testHashcodeIsStableWithContentChanges),
|
||||||
("testNamespacedElements", testNamespacedElements),
|
("testNamespacedElements", testNamespacedElements),
|
||||||
("testChainedRemoveAttributes",testChainedRemoveAttributes),
|
("testChainedRemoveAttributes", testChainedRemoveAttributes),
|
||||||
("testIs",testIs)
|
("testIs", testIs)
|
||||||
]
|
]
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,7 +147,6 @@ class EntitiesTest: XCTestCase {
|
||||||
doc.outputSettings().escapeMode(Entities.EscapeMode.xhtml)
|
doc.outputSettings().escapeMode(Entities.EscapeMode.xhtml)
|
||||||
XCTAssertEqual("<a title=\"<p>One</p>\">One</a>", try element.outerHtml())
|
XCTAssertEqual("<a title=\"<p>One</p>\">One</a>", try element.outerHtml())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static var allTests = {
|
static var allTests = {
|
||||||
return [
|
return [
|
||||||
|
|
|
@ -124,15 +124,14 @@ class StringUtilTest: XCTestCase {
|
||||||
return [
|
return [
|
||||||
("testLinuxTestSuiteIncludesAllTests", testLinuxTestSuiteIncludesAllTests),
|
("testLinuxTestSuiteIncludesAllTests", testLinuxTestSuiteIncludesAllTests),
|
||||||
("testJoin", testJoin),
|
("testJoin", testJoin),
|
||||||
("testPadding",testPadding),
|
("testPadding", testPadding),
|
||||||
("testIsBlank", testIsBlank),
|
("testIsBlank", testIsBlank),
|
||||||
("testIsNumeric", testIsNumeric),
|
("testIsNumeric", testIsNumeric),
|
||||||
("testIsWhitespace", testIsWhitespace),
|
("testIsWhitespace", testIsWhitespace),
|
||||||
("testNormaliseWhiteSpace", testNormaliseWhiteSpace),
|
("testNormaliseWhiteSpace", testNormaliseWhiteSpace),
|
||||||
("testNormaliseWhiteSpaceHandlesHighSurrogates", testNormaliseWhiteSpaceHandlesHighSurrogates),
|
("testNormaliseWhiteSpaceHandlesHighSurrogates", testNormaliseWhiteSpaceHandlesHighSurrogates),
|
||||||
("testResolvesRelativeUrls", testResolvesRelativeUrls),
|
("testResolvesRelativeUrls", testResolvesRelativeUrls)
|
||||||
]
|
]
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,7 +90,7 @@ class TagTest: XCTestCase {
|
||||||
("testPSemantics", testPSemantics),
|
("testPSemantics", testPSemantics),
|
||||||
("testImgSemantics", testImgSemantics),
|
("testImgSemantics", testImgSemantics),
|
||||||
("testDefaultSemantics", testDefaultSemantics),
|
("testDefaultSemantics", testDefaultSemantics),
|
||||||
("testValueOfChecksNotEmpty", testValueOfChecksNotEmpty),
|
("testValueOfChecksNotEmpty", testValueOfChecksNotEmpty)
|
||||||
]
|
]
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,7 +164,7 @@ class XmlTreeBuilderTest: XCTestCase {
|
||||||
let doc = try SwiftSoup.parse(xml, "", Parser.xmlParser().settings(ParseSettings.htmlDefault))
|
let doc = try SwiftSoup.parse(xml, "", Parser.xmlParser().settings(ParseSettings.htmlDefault))
|
||||||
try XCTAssertEqual("<test id=\"1\">Check</test>", TextUtil.stripNewlines(doc.html()))
|
try XCTAssertEqual("<test id=\"1\">Check</test>", TextUtil.stripNewlines(doc.html()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testNilReplaceInQueue()throws {
|
func testNilReplaceInQueue()throws {
|
||||||
let html: String = "<TABLE><TBODY><TR><TD></TD><TD><FONT color=#000000 size=1><I><FONT size=5><P align=center></FONT></I></FONT> </P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></DIV></DIV></DIV><BLOCKQUOTE></BLOCKQUOTE><DIV style=\"FONT: 10pt Courier New\"><BR><BR> </DIV></BODY></HTML>"
|
let html: String = "<TABLE><TBODY><TR><TD></TD><TD><FONT color=#000000 size=1><I><FONT size=5><P align=center></FONT></I></FONT> </P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></DIV></DIV></DIV><BLOCKQUOTE></BLOCKQUOTE><DIV style=\"FONT: 10pt Courier New\"><BR><BR> </DIV></BODY></HTML>"
|
||||||
_ = try SwiftSoup.parse(html)
|
_ = try SwiftSoup.parse(html)
|
||||||
|
@ -187,7 +187,7 @@ class XmlTreeBuilderTest: XCTestCase {
|
||||||
("testCreatesValidProlog", testCreatesValidProlog),
|
("testCreatesValidProlog", testCreatesValidProlog),
|
||||||
("testPreservesCaseByDefault", testPreservesCaseByDefault),
|
("testPreservesCaseByDefault", testPreservesCaseByDefault),
|
||||||
("testCanNormalizeCase", testCanNormalizeCase),
|
("testCanNormalizeCase", testCanNormalizeCase),
|
||||||
("testNilReplaceInQueue",testNilReplaceInQueue)
|
("testNilReplaceInQueue", testNilReplaceInQueue)
|
||||||
]
|
]
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue