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