* Removed StringBuilder from Element.cssSelector

* Lint Code
* Swift 4.1
This commit is contained in:
Nabil Chatbi 2018-03-31 19:42:13 +02:00
parent 306be6efbe
commit d98300a7ec
57 changed files with 473 additions and 504 deletions

View File

@ -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.

View File

@ -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:.
} }
} }

View File

@ -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])

View File

@ -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)
} }
} }

View File

@ -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"])
] ]
) )

View File

@ -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
} }

View File

@ -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 {

View File

@ -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

View File

@ -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
} }

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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 {}
} }

View File

@ -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
} }

View File

@ -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

View File

@ -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
} }

View File

@ -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
} }
} }

View File

@ -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)

View File

@ -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 {

View File

@ -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)
} }
} }

View File

@ -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()

View File

@ -57,8 +57,3 @@ open class ParseSettings {
} }
} }

View File

@ -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()
} }
} }

View File

@ -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

View File

@ -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) {

View File

@ -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()

View File

@ -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 {

View File

@ -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
} }
} }

View File

@ -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
} }

View File

@ -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)
} }
} }

View File

@ -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

View File

@ -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"
} }
} }

View File

@ -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>

View File

@ -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)

View File

@ -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

View File

@ -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")
} }

View File

@ -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
} }

View File

@ -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;

View File

@ -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>

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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)
]) ])

View File

@ -115,7 +115,7 @@ class AttributeParseTest: XCTestCase {
("teststrictAttributeUnescapes", teststrictAttributeUnescapes), ("teststrictAttributeUnescapes", teststrictAttributeUnescapes),
("testmoreAttributeUnescapes", testmoreAttributeUnescapes), ("testmoreAttributeUnescapes", testmoreAttributeUnescapes),
("testparsesBooleanAttributes", testparsesBooleanAttributes), ("testparsesBooleanAttributes", testparsesBooleanAttributes),
("testdropsSlashFromAttributeName", testdropsSlashFromAttributeName), ("testdropsSlashFromAttributeName", testdropsSlashFromAttributeName)
] ]
}() }()

View File

@ -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)
] ]
}() }()

View File

@ -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>&#x212c;</p></div>", customOut2) // XCTAssertEqual("<div><p>&#x212c;</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)
] ]
}() }()
}
}

View File

@ -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 {

View File

@ -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 '&#xa0;' or a '&nbsp;'.", // assertTrue("Should have contained a '&#xa0;' or a '&nbsp;'.",
// output.contains("&#xa0;") || output.contains("&nbsp;")); // output.contains("&#xa0;") || output.contains("&nbsp;"));
// } // }
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;\">&nbsp;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;\">&nbsp;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)
] ]
}() }()

View File

@ -55,7 +55,7 @@ class DocumentTypeTest: XCTestCase {
("testConstructorValidationOkWithBlankName", testConstructorValidationOkWithBlankName), ("testConstructorValidationOkWithBlankName", testConstructorValidationOkWithBlankName),
("testConstructorValidationThrowsExceptionOnNulls", testConstructorValidationThrowsExceptionOnNulls), ("testConstructorValidationThrowsExceptionOnNulls", testConstructorValidationThrowsExceptionOnNulls),
("testConstructorValidationOkWithBlankPublicAndSystemIds", testConstructorValidationOkWithBlankPublicAndSystemIds), ("testConstructorValidationOkWithBlankPublicAndSystemIds", testConstructorValidationOkWithBlankPublicAndSystemIds),
("testOuterHtmlGeneration", testOuterHtmlGeneration), ("testOuterHtmlGeneration", testOuterHtmlGeneration)
] ]
}() }()
} }

View File

@ -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)
] ]
}() }()
} }

View File

@ -147,7 +147,6 @@ class EntitiesTest: XCTestCase {
doc.outputSettings().escapeMode(Entities.EscapeMode.xhtml) doc.outputSettings().escapeMode(Entities.EscapeMode.xhtml)
XCTAssertEqual("<a title=\"&lt;p>One&lt;/p>\">One</a>", try element.outerHtml()) XCTAssertEqual("<a title=\"&lt;p>One&lt;/p>\">One</a>", try element.outerHtml())
} }
static var allTests = { static var allTests = {
return [ return [

View File

@ -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)
] ]
}() }()
} }

View File

@ -90,7 +90,7 @@ class TagTest: XCTestCase {
("testPSemantics", testPSemantics), ("testPSemantics", testPSemantics),
("testImgSemantics", testImgSemantics), ("testImgSemantics", testImgSemantics),
("testDefaultSemantics", testDefaultSemantics), ("testDefaultSemantics", testDefaultSemantics),
("testValueOfChecksNotEmpty", testValueOfChecksNotEmpty), ("testValueOfChecksNotEmpty", testValueOfChecksNotEmpty)
] ]
}() }()
} }

View File

@ -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>&nbsp;</P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></DIV></DIV></DIV><BLOCKQUOTE></BLOCKQUOTE><DIV style=\"FONT: 10pt Courier New\"><BR><BR>&nbsp;</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>&nbsp;</P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></DIV></DIV></DIV><BLOCKQUOTE></BLOCKQUOTE><DIV style=\"FONT: 10pt Courier New\"><BR><BR>&nbsp;</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)
] ]
}() }()