lint
This commit is contained in:
parent
6272c01cd8
commit
db2f0bafbc
|
@ -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:.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ class ViewController: UIViewController {
|
|||
// Dispose of any resources that can be recreated.
|
||||
}
|
||||
|
||||
|
||||
func parseDocument()throws->Document {
|
||||
let html = "<html><head><title>First parse</title></head>"
|
||||
+ "<body><p>Parsed HTML into a doc.</p></body></html>"
|
||||
|
@ -69,18 +68,16 @@ class ViewController: UIViewController {
|
|||
" <foo />bar\n" +
|
||||
" </body>\n" +
|
||||
"</html>"
|
||||
let doc: Document = try! SwiftSoup.parse(h);
|
||||
let doc: Document = try! SwiftSoup.parse(h)
|
||||
do {
|
||||
for _ in 0...100000{
|
||||
_ = try doc.select("div");
|
||||
for _ in 0...100000 {
|
||||
_ = try doc.select("div")
|
||||
}
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
|
||||
func testSite()
|
||||
{
|
||||
func testSite() {
|
||||
let myURLString = "http://apple.com"
|
||||
guard let myURL = URL(string: myURLString) else {
|
||||
print("Error: \(myURLString) doesn't seem to be a valid URL")
|
||||
|
@ -90,16 +87,12 @@ class ViewController: UIViewController {
|
|||
let doc: Document = try! SwiftSoup.parse(html)
|
||||
|
||||
do {
|
||||
for _ in 0...100{
|
||||
for _ in 0...100 {
|
||||
_ = try doc.text()
|
||||
}
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
print("Error")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,7 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
extension Array
|
||||
{
|
||||
extension Array {
|
||||
func binarySearch<T: Comparable>(_ collection: [T], _ target: T) -> Int {
|
||||
|
||||
let min = 0
|
||||
|
@ -19,7 +18,6 @@ extension Array
|
|||
|
||||
}
|
||||
|
||||
|
||||
func binaryMakeGuess<T: Comparable>(min: Int, max: Int, target: T, collection: [T]) -> Int {
|
||||
|
||||
let guess = (min + max) / 2
|
||||
|
@ -49,12 +47,8 @@ extension Array
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
extension Array where Element : Equatable
|
||||
{
|
||||
func lastIndexOf(_ e: Element) -> Int
|
||||
{
|
||||
extension Array where Element : Equatable {
|
||||
func lastIndexOf(_ e: Element) -> Int {
|
||||
for pos in (0..<self.count).reversed() {
|
||||
let next = self[pos]
|
||||
if (next == e) {
|
||||
|
@ -64,5 +58,3 @@ extension Array where Element : Equatable
|
|||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ open class Attribute {
|
|||
var key: String
|
||||
var value: String
|
||||
|
||||
public init(key: String,value :String) throws {
|
||||
public init(key: String, value: String) throws {
|
||||
try Validate.notEmpty(string: key)
|
||||
self.key = key.trim()
|
||||
self.value = value
|
||||
|
@ -32,7 +32,7 @@ open class Attribute {
|
|||
Get the attribute key.
|
||||
@return the attribute key
|
||||
*/
|
||||
open func getKey() -> String{
|
||||
open func getKey() -> String {
|
||||
return key
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ open class Attribute {
|
|||
Get the HTML representation of this attribute; e.g. {@code href="index.html"}.
|
||||
@return HTML
|
||||
*/
|
||||
public func html()-> String {
|
||||
public func html() -> String {
|
||||
let accum = StringBuilder()
|
||||
html(accum: accum, out: (Document("")).outputSettings())
|
||||
return accum.toString()
|
||||
|
@ -87,7 +87,7 @@ open class Attribute {
|
|||
Get the string representation of this attribute, implemented as {@link #html()}.
|
||||
@return string
|
||||
*/
|
||||
open func toString()-> String {
|
||||
open func toString() -> String {
|
||||
return html()
|
||||
}
|
||||
|
||||
|
@ -118,38 +118,31 @@ open class Attribute {
|
|||
&& isBooleanAttribute()
|
||||
}
|
||||
|
||||
public func isBooleanAttribute() -> Bool
|
||||
{
|
||||
return (Attribute.booleanAttributes.binarySearch(Attribute.booleanAttributes,key) != -1)
|
||||
public func isBooleanAttribute() -> Bool {
|
||||
return (Attribute.booleanAttributes.binarySearch(Attribute.booleanAttributes, key) != -1)
|
||||
}
|
||||
|
||||
|
||||
public func hashCode() -> Int {
|
||||
var result = key.hashValue
|
||||
result = 31 * result + value.hashValue
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
public func clone() -> Attribute
|
||||
{
|
||||
public func clone() -> Attribute {
|
||||
do {
|
||||
return try Attribute(key: key,value: value)
|
||||
} catch Exception.Error( _ , let msg){
|
||||
return try Attribute(key: key, value: value)
|
||||
} catch Exception.Error( _, let msg) {
|
||||
print(msg)
|
||||
}catch{
|
||||
} catch {
|
||||
|
||||
}
|
||||
return try! Attribute(key: "",value: "")
|
||||
return try! Attribute(key: "", value: "")
|
||||
}
|
||||
}
|
||||
|
||||
extension Attribute : Equatable
|
||||
{
|
||||
static public func == (lhs: Attribute, rhs: Attribute) -> Bool
|
||||
{
|
||||
extension Attribute : Equatable {
|
||||
static public func == (lhs: Attribute, rhs: Attribute) -> Bool {
|
||||
return lhs.value == rhs.value && lhs.key == rhs.key
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -21,15 +21,15 @@ import Foundation
|
|||
*
|
||||
* @author Jonathan Hedley, jonathan@hedley.net
|
||||
*/
|
||||
open class Attributes : NSCopying {
|
||||
open class Attributes: NSCopying {
|
||||
|
||||
open static var dataPrefix : String = "data-"
|
||||
open static var dataPrefix: String = "data-"
|
||||
|
||||
fileprivate var attributes : OrderedDictionary<String, Attribute> = OrderedDictionary<String, Attribute>()
|
||||
fileprivate var attributes: OrderedDictionary<String, Attribute> = OrderedDictionary<String, Attribute>()
|
||||
// linked hash map to preserve insertion order.
|
||||
// null be default as so many elements have no attributes -- saves a good chunk of memory
|
||||
|
||||
public init(){}
|
||||
public init() {}
|
||||
|
||||
/**
|
||||
Get an attribute value by key.
|
||||
|
@ -37,8 +37,8 @@ open class Attributes : NSCopying {
|
|||
@return the attribute value if set; or empty string if not set.
|
||||
@see #hasKey(String)
|
||||
*/
|
||||
open func get(key : String)-> String {
|
||||
let attr : Attribute? = attributes.get(key:key)
|
||||
open func get(key: String) -> String {
|
||||
let attr: Attribute? = attributes.get(key:key)
|
||||
return attr != nil ? attr!.getValue() : ""
|
||||
}
|
||||
|
||||
|
@ -47,12 +47,11 @@ open class Attributes : NSCopying {
|
|||
* @param key the attribute name
|
||||
* @return the first matching attribute value if set; or empty string if not set.
|
||||
*/
|
||||
open func getIgnoreCase(key : String )throws -> String {
|
||||
open func getIgnoreCase(key: String )throws -> String {
|
||||
try Validate.notEmpty(string: key)
|
||||
|
||||
for attrKey in (attributes.keySet())
|
||||
{
|
||||
if attrKey.equalsIgnoreCase(string: key){
|
||||
for attrKey in (attributes.keySet()) {
|
||||
if attrKey.equalsIgnoreCase(string: key) {
|
||||
return attributes.get(key: attrKey)!.getValue()
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +63,7 @@ open class Attributes : NSCopying {
|
|||
@param key attribute key
|
||||
@param value attribute value
|
||||
*/
|
||||
open func put(_ key : String , _ value : String) throws {
|
||||
open func put(_ key: String, _ value: String) throws {
|
||||
let attr = try Attribute(key: key, value: value)
|
||||
put(attribute: attr)
|
||||
}
|
||||
|
@ -74,10 +73,10 @@ open class Attributes : NSCopying {
|
|||
@param key attribute key
|
||||
@param value attribute value
|
||||
*/
|
||||
open func put(_ key : String , _ value : Bool) throws {
|
||||
if (value){
|
||||
open func put(_ key: String, _ value: Bool) throws {
|
||||
if (value) {
|
||||
try put(attribute: BooleanAttribute(key: key))
|
||||
}else{
|
||||
} else {
|
||||
try remove(key: key)
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +85,7 @@ open class Attributes : NSCopying {
|
|||
Set a new attribute, or replace an existing one by key.
|
||||
@param attribute attribute
|
||||
*/
|
||||
open func put(attribute : Attribute) {
|
||||
open func put(attribute: Attribute) {
|
||||
attributes.put(value: attribute, forKey:attribute.getKey())
|
||||
}
|
||||
|
||||
|
@ -103,10 +102,10 @@ open class Attributes : NSCopying {
|
|||
Remove an attribute by key. <b>Case insensitive.</b>
|
||||
@param key attribute key to remove
|
||||
*/
|
||||
open func removeIgnoreCase(key : String ) throws {
|
||||
open func removeIgnoreCase(key: String ) throws {
|
||||
try Validate.notEmpty(string: key)
|
||||
for attrKey in attributes.keySet(){
|
||||
if (attrKey.equalsIgnoreCase(string: key)){
|
||||
for attrKey in attributes.keySet() {
|
||||
if (attrKey.equalsIgnoreCase(string: key)) {
|
||||
attributes.remove(key: attrKey)
|
||||
}
|
||||
}
|
||||
|
@ -117,7 +116,7 @@ open class Attributes : NSCopying {
|
|||
@param key case-sensitive key to check for
|
||||
@return true if key exists, false otherwise
|
||||
*/
|
||||
open func hasKey(key : String) -> Bool {
|
||||
open func hasKey(key: String) -> Bool {
|
||||
return attributes.containsKey(key: key)
|
||||
}
|
||||
|
||||
|
@ -128,7 +127,7 @@ open class Attributes : NSCopying {
|
|||
*/
|
||||
open func hasKeyIgnoreCase(key: String) -> Bool {
|
||||
for attrKey in attributes.keySet() {
|
||||
if (attrKey.equalsIgnoreCase(string: key)){
|
||||
if (attrKey.equalsIgnoreCase(string: key)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -152,16 +151,15 @@ open class Attributes : NSCopying {
|
|||
return
|
||||
}
|
||||
|
||||
if (incoming.size() == 0){
|
||||
if (incoming.size() == 0) {
|
||||
return
|
||||
}
|
||||
attributes.putAll(all: incoming.attributes)
|
||||
}
|
||||
|
||||
open func iterator() -> IndexingIterator<Array<Attribute>> {
|
||||
if (attributes.isEmpty)
|
||||
{
|
||||
let args : [Attribute] = []
|
||||
if (attributes.isEmpty) {
|
||||
let args: [Attribute] = []
|
||||
return args.makeIterator()
|
||||
}
|
||||
return attributes.orderedValues.makeIterator()
|
||||
|
@ -173,7 +171,7 @@ open class Attributes : NSCopying {
|
|||
@return an view of the attributes as a List.
|
||||
*/
|
||||
open func asList() -> Array<Attribute> {
|
||||
var list : Array<Attribute> = Array(/*attributes.size()*/)
|
||||
var list: Array<Attribute> = Array(/*attributes.size()*/)
|
||||
for entry in attributes.orderedValues {
|
||||
list.append(entry)
|
||||
}
|
||||
|
@ -186,11 +184,11 @@ open class Attributes : NSCopying {
|
|||
* @return map of custom data attributes.
|
||||
*/
|
||||
//Map<String, String>
|
||||
open func dataset() -> Dictionary<String,String> {
|
||||
var dataset = Dictionary<String,String>()
|
||||
for attribute in attributes{
|
||||
open func dataset() -> Dictionary<String, String> {
|
||||
var dataset = Dictionary<String, String>()
|
||||
for attribute in attributes {
|
||||
let attr = attribute.1
|
||||
if(attr.isDataAttribute()){
|
||||
if(attr.isDataAttribute()) {
|
||||
let key = attr.getKey().substring(Attributes.dataPrefix.characters.count)
|
||||
dataset[key] = attribute.1.getValue()
|
||||
}
|
||||
|
@ -209,7 +207,7 @@ open class Attributes : NSCopying {
|
|||
return accum.toString()
|
||||
}
|
||||
|
||||
public func html(accum: StringBuilder,out: OutputSettings ) throws {
|
||||
public func html(accum: StringBuilder, out: OutputSettings ) throws {
|
||||
for attribute in attributes.orderedValues {
|
||||
accum.append(" ")
|
||||
attribute.html(accum: accum, out: out)
|
||||
|
@ -226,9 +224,9 @@ open class Attributes : NSCopying {
|
|||
* @return if both sets of attributes have the same content
|
||||
*/
|
||||
open func equals(o: AnyObject?) -> Bool {
|
||||
if(o == nil){return false}
|
||||
if(o == nil) {return false}
|
||||
if (self === o.self) {return true}
|
||||
guard let that : Attributes = o as? Attributes else {return false}
|
||||
guard let that: Attributes = o as? Attributes else {return false}
|
||||
return (attributes == that.attributes)
|
||||
}
|
||||
|
||||
|
@ -240,8 +238,7 @@ open class Attributes : NSCopying {
|
|||
return attributes.hashCode()
|
||||
}
|
||||
|
||||
public func copy(with zone: NSZone? = nil) -> Any
|
||||
{
|
||||
public func copy(with zone: NSZone? = nil) -> Any {
|
||||
let clone = Attributes()
|
||||
clone.attributes = attributes.clone()
|
||||
return clone
|
||||
|
@ -260,9 +257,8 @@ open class Attributes : NSCopying {
|
|||
extension Attributes : Sequence {
|
||||
public func makeIterator() -> AnyIterator<Attribute> {
|
||||
var list = attributes.orderedValues
|
||||
return AnyIterator{
|
||||
return AnyIterator {
|
||||
return list.count > 0 ? list.removeFirst() : nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,16 +11,15 @@ import Foundation
|
|||
/**
|
||||
* A boolean attribute that is written out without any value.
|
||||
*/
|
||||
open class BooleanAttribute : Attribute {
|
||||
open class BooleanAttribute: Attribute {
|
||||
/**
|
||||
* Create a new boolean attribute from unencoded (raw) key.
|
||||
* @param key attribute key
|
||||
*/
|
||||
init(key : String) throws {
|
||||
init(key: String) throws {
|
||||
try super.init(key: key, value: "")
|
||||
}
|
||||
|
||||
|
||||
override public func isBooleanAttribute() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -17,10 +17,10 @@ private let digitSet = CharacterSet.decimalDigits
|
|||
|
||||
extension Character {
|
||||
|
||||
public static let BackslashF : Character = Character(UnicodeScalar(12))
|
||||
public static let BackslashF: Character = Character(UnicodeScalar(12))
|
||||
|
||||
//http://www.unicode.org/glossary/#supplementary_code_point
|
||||
public static let MIN_SUPPLEMENTARY_CODE_POINT : UInt32 = 0x010000
|
||||
public static let MIN_SUPPLEMENTARY_CODE_POINT: UInt32 = 0x010000
|
||||
|
||||
/// The first `UnicodeScalar` of `self`.
|
||||
var unicodeScalar: UnicodeScalar {
|
||||
|
@ -48,7 +48,7 @@ extension Character {
|
|||
|
||||
switch self {
|
||||
|
||||
case " ", "\t", "\n", "\r", "\r\n",Character.BackslashF: return true
|
||||
case " ", "\t", "\n", "\r", "\r\n", Character.BackslashF: return true
|
||||
|
||||
case "\u{000C}", "\u{000B}", "\u{0085}": return true // Form Feed, vertical tab, next line (nel)
|
||||
|
||||
|
@ -170,8 +170,7 @@ extension Character {
|
|||
return Character(UnicodeScalar(value)!)
|
||||
}
|
||||
|
||||
func unicodeScalarCodePoint() -> UInt32
|
||||
{
|
||||
func unicodeScalarCodePoint() -> UInt32 {
|
||||
return unicodeScalar.value
|
||||
}
|
||||
|
||||
|
@ -179,22 +178,18 @@ extension Character {
|
|||
return codePoint >= MIN_SUPPLEMENTARY_CODE_POINT ? 2 : 1
|
||||
}
|
||||
|
||||
static func isLetter(_ char: Character)->Bool{
|
||||
static func isLetter(_ char: Character) -> Bool {
|
||||
return char.isLetter()
|
||||
}
|
||||
func isLetter()->Bool{
|
||||
func isLetter() -> Bool {
|
||||
return self.isMemberOfCharacterSet(CharacterSet.letters)
|
||||
}
|
||||
|
||||
static func isLetterOrDigit(_ char: Character)->Bool
|
||||
{
|
||||
static func isLetterOrDigit(_ char: Character) -> Bool {
|
||||
return char.isLetterOrDigit()
|
||||
}
|
||||
func isLetterOrDigit()->Bool
|
||||
{
|
||||
func isLetterOrDigit() -> Bool {
|
||||
if(self.isLetter()) {return true}
|
||||
return self.isDigit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -11,18 +11,16 @@ import Foundation
|
|||
/**
|
||||
CharacterReader consumes tokens off a string. To replace the old TokenQueue.
|
||||
*/
|
||||
public final class CharacterReader
|
||||
{
|
||||
public static let EOF : UnicodeScalar = "\u{FFFF}"//65535
|
||||
private static let maxCacheLen : Int = 12
|
||||
private let input : [UnicodeScalar]
|
||||
private let length : Int
|
||||
private var pos : Int = 0
|
||||
private var mark : Int = 0
|
||||
private let stringCache : Array<String?> // holds reused strings in this doc, to lessen garbage
|
||||
public final class CharacterReader {
|
||||
public static let EOF: UnicodeScalar = "\u{FFFF}"//65535
|
||||
private static let maxCacheLen: Int = 12
|
||||
private let input: [UnicodeScalar]
|
||||
private let length: Int
|
||||
private var pos: Int = 0
|
||||
private var mark: Int = 0
|
||||
private let stringCache: Array<String?> // holds reused strings in this doc, to lessen garbage
|
||||
|
||||
public init(_ input: String)
|
||||
{
|
||||
public init(_ input: String) {
|
||||
self.input = Array(input.unicodeScalars)
|
||||
self.length = self.input.count
|
||||
stringCache = Array(repeating:nil, count:512)
|
||||
|
@ -75,10 +73,10 @@ public final class CharacterReader
|
|||
* @param c scan target
|
||||
* @return offset between current position and next instance of target. -1 if not found.
|
||||
*/
|
||||
public func nextIndexOf(_ c : UnicodeScalar) -> Int {
|
||||
public func nextIndexOf(_ c: UnicodeScalar) -> Int {
|
||||
// doesn't handle scanning for surrogates
|
||||
for i in pos..<length {
|
||||
if (c == input[i]){
|
||||
if (c == input[i]) {
|
||||
return i - pos
|
||||
}
|
||||
}
|
||||
|
@ -93,25 +91,24 @@ public final class CharacterReader
|
|||
*/
|
||||
public func nextIndexOf(_ seq: String) -> Int {
|
||||
// doesn't handle scanning for surrogates
|
||||
if(seq.isEmpty){return -1}
|
||||
let startChar : UnicodeScalar = seq.unicodeScalar(0)
|
||||
if(seq.isEmpty) {return -1}
|
||||
let startChar: UnicodeScalar = seq.unicodeScalar(0)
|
||||
for var offset in pos..<length {
|
||||
// scan to first instance of startchar:
|
||||
if (startChar != input[offset]){
|
||||
if (startChar != input[offset]) {
|
||||
offset+=1
|
||||
while(offset < length && startChar != input[offset]) { offset+=1 }
|
||||
}
|
||||
var i = offset + 1
|
||||
let last = i + seq.unicodeScalars.count-1
|
||||
if (offset < length && last <= length)
|
||||
{
|
||||
if (offset < length && last <= length) {
|
||||
var j = 1
|
||||
while i < last && seq.unicodeScalar(j) == input[i] {
|
||||
j+=1
|
||||
i+=1
|
||||
}
|
||||
// found full sequence
|
||||
if (i == last){
|
||||
if (i == last) {
|
||||
return offset - pos
|
||||
}
|
||||
}
|
||||
|
@ -119,7 +116,7 @@ public final class CharacterReader
|
|||
return -1
|
||||
}
|
||||
|
||||
public func consumeTo(_ c : UnicodeScalar) -> String {
|
||||
public func consumeTo(_ c: UnicodeScalar) -> String {
|
||||
let offset = nextIndexOf(c)
|
||||
if (offset != -1) {
|
||||
let consumed = cacheString(pos, offset)
|
||||
|
@ -141,24 +138,23 @@ public final class CharacterReader
|
|||
}
|
||||
}
|
||||
|
||||
public func consumeToAny(_ chars: UnicodeScalar...)->String {
|
||||
public func consumeToAny(_ chars: UnicodeScalar...) -> String {
|
||||
return consumeToAny(chars)
|
||||
}
|
||||
public func consumeToAny(_ chars: [UnicodeScalar])->String {
|
||||
let start : Int = pos
|
||||
let remaining : Int = length
|
||||
public func consumeToAny(_ chars: [UnicodeScalar]) -> String {
|
||||
let start: Int = pos
|
||||
let remaining: Int = length
|
||||
let val = input
|
||||
if(start == 2528){
|
||||
if(start == 2528) {
|
||||
let d = 1
|
||||
print(d)
|
||||
}
|
||||
OUTER: while (pos < remaining)
|
||||
{
|
||||
if(pos == 41708){
|
||||
OUTER: while (pos < remaining) {
|
||||
if(pos == 41708) {
|
||||
let d = 1
|
||||
print(d)
|
||||
}
|
||||
if chars.contains(val[pos]){
|
||||
if chars.contains(val[pos]) {
|
||||
break OUTER
|
||||
}
|
||||
// for c in chars {
|
||||
|
@ -172,17 +168,16 @@ public final class CharacterReader
|
|||
return pos > start ? cacheString(start, pos-start) : ""
|
||||
}
|
||||
|
||||
|
||||
public func consumeToAnySorted(_ chars: UnicodeScalar...)->String {
|
||||
public func consumeToAnySorted(_ chars: UnicodeScalar...) -> String {
|
||||
return consumeToAnySorted(chars)
|
||||
}
|
||||
public func consumeToAnySorted(_ chars: [UnicodeScalar])->String {
|
||||
public func consumeToAnySorted(_ chars: [UnicodeScalar]) -> String {
|
||||
let start = pos
|
||||
let remaining = length
|
||||
let val = input
|
||||
|
||||
while (pos < remaining) {
|
||||
if (chars.binarySearch(chars, val[pos]) >= 0){
|
||||
if (chars.binarySearch(chars, val[pos]) >= 0) {
|
||||
break
|
||||
}
|
||||
pos += 1
|
||||
|
@ -191,8 +186,6 @@ public final class CharacterReader
|
|||
return pos > start ? cacheString(start, pos-start) : ""
|
||||
}
|
||||
|
||||
|
||||
|
||||
public func consumeData() -> String {
|
||||
// &, <, null
|
||||
let start = pos
|
||||
|
@ -200,8 +193,8 @@ public final class CharacterReader
|
|||
let val = input
|
||||
|
||||
while (pos < remaining) {
|
||||
let c : UnicodeScalar = val[pos]
|
||||
if (c == "&" || c == "<" || c == TokeniserStateVars.nullScalr){
|
||||
let c: UnicodeScalar = val[pos]
|
||||
if (c == "&" || c == "<" || c == TokeniserStateVars.nullScalr) {
|
||||
break
|
||||
}
|
||||
pos += 1
|
||||
|
@ -210,15 +203,15 @@ public final class CharacterReader
|
|||
return pos > start ? cacheString(start, pos-start) : ""
|
||||
}
|
||||
|
||||
public func consumeTagName()-> String {
|
||||
public func consumeTagName() -> String {
|
||||
// '\t', '\n', '\r', '\f', ' ', '/', '>', nullChar
|
||||
let start = pos
|
||||
let remaining = length
|
||||
let val = input
|
||||
|
||||
while (pos < remaining) {
|
||||
let c : UnicodeScalar = val[pos]
|
||||
if (c == "\t" || c == "\n" || c == "\r" || c == UnicodeScalar.BackslashF || c == " " || c == "/" || c == ">" || c == TokeniserStateVars.nullScalr){
|
||||
let c: UnicodeScalar = val[pos]
|
||||
if (c == "\t" || c == "\n" || c == "\r" || c == UnicodeScalar.BackslashF || c == " " || c == "/" || c == ">" || c == TokeniserStateVars.nullScalr) {
|
||||
break
|
||||
}
|
||||
pos += 1
|
||||
|
@ -226,41 +219,40 @@ public final class CharacterReader
|
|||
return pos > start ? cacheString(start, pos-start) : ""
|
||||
}
|
||||
|
||||
|
||||
public func consumeToEnd()-> String {
|
||||
public func consumeToEnd() -> String {
|
||||
let data = cacheString(pos, length-pos)
|
||||
pos = length
|
||||
return data
|
||||
}
|
||||
|
||||
public func consumeLetterSequence()-> String {
|
||||
public func consumeLetterSequence() -> String {
|
||||
let start = pos
|
||||
while (pos < length) {
|
||||
let c : UnicodeScalar = input[pos]
|
||||
if ((c >= "A" && c <= "Z") || (c >= "a" && c <= "z") || c.isMemberOfCharacterSet(CharacterSet.letters)){
|
||||
let c: UnicodeScalar = input[pos]
|
||||
if ((c >= "A" && c <= "Z") || (c >= "a" && c <= "z") || c.isMemberOfCharacterSet(CharacterSet.letters)) {
|
||||
pos += 1
|
||||
}else{
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return cacheString(start, pos - start)
|
||||
}
|
||||
|
||||
public func consumeLetterThenDigitSequence()-> String {
|
||||
public func consumeLetterThenDigitSequence() -> String {
|
||||
let start = pos
|
||||
while (pos < length) {
|
||||
let c = input[pos]
|
||||
if ((c >= "A" && c <= "Z") || (c >= "a" && c <= "z") || c.isMemberOfCharacterSet(CharacterSet.letters)){
|
||||
if ((c >= "A" && c <= "Z") || (c >= "a" && c <= "z") || c.isMemberOfCharacterSet(CharacterSet.letters)) {
|
||||
pos += 1
|
||||
}else{
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
while (!isEmpty()) {
|
||||
let c = input[pos]
|
||||
if (c >= "0" && c <= "9"){
|
||||
if (c >= "0" && c <= "9") {
|
||||
pos += 1
|
||||
}else{
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -268,13 +260,13 @@ public final class CharacterReader
|
|||
return cacheString(start, pos - start)
|
||||
}
|
||||
|
||||
public func consumeHexSequence()-> String {
|
||||
public func consumeHexSequence() -> String {
|
||||
let start = pos
|
||||
while (pos < length) {
|
||||
let c = input[pos]
|
||||
if ((c >= "0" && c <= "9") || (c >= "A" && c <= "F") || (c >= "a" && c <= "f")){
|
||||
if ((c >= "0" && c <= "9") || (c >= "A" && c <= "F") || (c >= "a" && c <= "f")) {
|
||||
pos+=1
|
||||
}else{
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -285,9 +277,9 @@ public final class CharacterReader
|
|||
let start = pos
|
||||
while (pos < length) {
|
||||
let c = input[pos]
|
||||
if (c >= "0" && c <= "9"){
|
||||
if (c >= "0" && c <= "9") {
|
||||
pos+=1
|
||||
}else{
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -299,68 +291,68 @@ public final class CharacterReader
|
|||
|
||||
}
|
||||
|
||||
public func matches(_ seq: String)-> Bool {
|
||||
public func matches(_ seq: String) -> Bool {
|
||||
let scanLength = seq.unicodeScalars.count
|
||||
if (scanLength > length - pos){
|
||||
if (scanLength > length - pos) {
|
||||
return false
|
||||
}
|
||||
|
||||
for offset in 0..<scanLength{
|
||||
if (seq.unicodeScalar(offset) != input[pos+offset]){
|
||||
for offset in 0..<scanLength {
|
||||
if (seq.unicodeScalar(offset) != input[pos+offset]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public func matchesIgnoreCase(_ seq: String )->Bool {
|
||||
public func matchesIgnoreCase(_ seq: String ) -> Bool {
|
||||
|
||||
let scanLength = seq.unicodeScalars.count
|
||||
if(scanLength == 0){
|
||||
if(scanLength == 0) {
|
||||
return false
|
||||
}
|
||||
if (scanLength > length - pos){
|
||||
if (scanLength > length - pos) {
|
||||
return false
|
||||
}
|
||||
|
||||
for offset in 0..<scanLength{
|
||||
let upScan : UnicodeScalar = seq.unicodeScalar(offset).uppercase
|
||||
let upTarget : UnicodeScalar = input[pos+offset].uppercase
|
||||
if (upScan != upTarget){
|
||||
for offset in 0..<scanLength {
|
||||
let upScan: UnicodeScalar = seq.unicodeScalar(offset).uppercase
|
||||
let upTarget: UnicodeScalar = input[pos+offset].uppercase
|
||||
if (upScan != upTarget) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public func matchesAny(_ seq: UnicodeScalar...)->Bool {
|
||||
if (isEmpty()){
|
||||
public func matchesAny(_ seq: UnicodeScalar...) -> Bool {
|
||||
if (isEmpty()) {
|
||||
return false
|
||||
}
|
||||
|
||||
let c : UnicodeScalar = input[pos]
|
||||
let c: UnicodeScalar = input[pos]
|
||||
for seek in seq {
|
||||
if (seek == c){
|
||||
if (seek == c) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public func matchesAnySorted(_ seq : [UnicodeScalar]) -> Bool {
|
||||
public func matchesAnySorted(_ seq: [UnicodeScalar]) -> Bool {
|
||||
return !isEmpty() && seq.binarySearch(seq, input[pos]) >= 0
|
||||
}
|
||||
|
||||
public func matchesLetter()-> Bool {
|
||||
if (isEmpty()){
|
||||
public func matchesLetter() -> Bool {
|
||||
if (isEmpty()) {
|
||||
return false
|
||||
}
|
||||
let c = input[pos]
|
||||
return (c >= "A" && c <= "Z") || (c >= "a" && c <= "z") || c.isMemberOfCharacterSet(CharacterSet.letters)
|
||||
}
|
||||
|
||||
public func matchesDigit()->Bool {
|
||||
if (isEmpty()){
|
||||
public func matchesDigit() -> Bool {
|
||||
if (isEmpty()) {
|
||||
return false
|
||||
}
|
||||
let c = input[pos]
|
||||
|
@ -368,7 +360,7 @@ public final class CharacterReader
|
|||
}
|
||||
|
||||
@discardableResult
|
||||
public func matchConsume(_ seq: String)->Bool {
|
||||
public func matchConsume(_ seq: String) -> Bool {
|
||||
if (matches(seq)) {
|
||||
pos += seq.unicodeScalars.count
|
||||
return true
|
||||
|
@ -378,7 +370,7 @@ public final class CharacterReader
|
|||
}
|
||||
|
||||
@discardableResult
|
||||
public func matchConsumeIgnoreCase(_ seq: String)->Bool {
|
||||
public func matchConsumeIgnoreCase(_ seq: String) -> Bool {
|
||||
if (matchesIgnoreCase(seq)) {
|
||||
pos += seq.unicodeScalars.count
|
||||
return true
|
||||
|
@ -387,20 +379,18 @@ public final class CharacterReader
|
|||
}
|
||||
}
|
||||
|
||||
public func containsIgnoreCase(_ seq: String )->Bool {
|
||||
public func containsIgnoreCase(_ seq: String ) -> Bool {
|
||||
// used to check presence of </title>, </style>. only finds consistent case.
|
||||
let loScan = seq.lowercased(with: Locale(identifier: "en"))
|
||||
let hiScan = seq.uppercased(with: Locale(identifier: "eng"))
|
||||
return (nextIndexOf(loScan) > -1) || (nextIndexOf(hiScan) > -1)
|
||||
}
|
||||
|
||||
|
||||
public func toString()->String {
|
||||
public func toString() -> String {
|
||||
return String.unicodescalars(Array(input[pos..<length]))
|
||||
//return input.string(pos, length - pos)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Caches short strings, as a flywheel pattern, to reduce GC load. Just for this doc, to prevent leaks.
|
||||
* <p />
|
||||
|
@ -410,17 +400,17 @@ public final class CharacterReader
|
|||
*/
|
||||
private func cacheString(_ start: Int, _ count: Int) -> String {
|
||||
let val = input
|
||||
var cache : [String?] = stringCache
|
||||
var cache: [String?] = stringCache
|
||||
|
||||
// limit (no cache):
|
||||
if (count > CharacterReader.maxCacheLen){
|
||||
if (count > CharacterReader.maxCacheLen) {
|
||||
return String.unicodescalars(Array(val[start..<start+count]))
|
||||
}
|
||||
|
||||
// calculate hash:
|
||||
var hash : Int = 0
|
||||
var hash: Int = 0
|
||||
var offset = start
|
||||
for _ in 0..<count{
|
||||
for _ in 0..<count {
|
||||
let ch = val[offset].value
|
||||
hash = Int.addWithOverflow(Int.multiplyWithOverflow(31, hash).0, Int(ch)).0
|
||||
offset+=1
|
||||
|
@ -429,11 +419,10 @@ public final class CharacterReader
|
|||
// get from cache
|
||||
hash = abs(hash)
|
||||
let i = hash % cache.count
|
||||
let index : Int = abs(i) //Int(hash & Int(cache.count) - 1)
|
||||
let index: Int = abs(i) //Int(hash & Int(cache.count) - 1)
|
||||
var cached = cache[index]
|
||||
|
||||
if (cached == nil)
|
||||
{ // miss, add
|
||||
if (cached == nil) { // miss, add
|
||||
cached = String.unicodescalars(Array(val[start..<start+count]))
|
||||
//cached = val.string(start, count)
|
||||
cache[Int(index)] = cached
|
||||
|
@ -453,16 +442,14 @@ public final class CharacterReader
|
|||
* Check if the value of the provided range equals the string.
|
||||
*/
|
||||
public func rangeEquals(_ start: Int, _ count: Int, _ cached: String) -> Bool {
|
||||
if (count == cached.unicodeScalars.count)
|
||||
{
|
||||
if (count == cached.unicodeScalars.count) {
|
||||
var count = count
|
||||
let one = input
|
||||
var i = start
|
||||
var j = 0
|
||||
while (count != 0) {
|
||||
count -= 1
|
||||
if (one[i] != cached.unicodeScalar(j) )
|
||||
{
|
||||
if (one[i] != cached.unicodeScalar(j) ) {
|
||||
return false
|
||||
}
|
||||
j += 1
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
open class Cleaner
|
||||
{
|
||||
fileprivate let whitelist : Whitelist
|
||||
open class Cleaner {
|
||||
fileprivate let whitelist: Whitelist
|
||||
|
||||
/**
|
||||
Create a new cleaner, that sanitizes documents using the supplied whitelist.
|
||||
|
@ -56,7 +55,7 @@ open class Cleaner
|
|||
|
||||
@discardableResult
|
||||
fileprivate func copySafeNodes(_ source: Element, _ dest: Element)throws->Int {
|
||||
let cleaningVisitor: Cleaner.CleaningVisitor = Cleaner.CleaningVisitor(source, dest,self)
|
||||
let cleaningVisitor: Cleaner.CleaningVisitor = Cleaner.CleaningVisitor(source, dest, self)
|
||||
let traversor: NodeTraversor = NodeTraversor(cleaningVisitor)
|
||||
try traversor.traverse(source)
|
||||
return cleaningVisitor.numDiscarded
|
||||
|
@ -68,13 +67,11 @@ open class Cleaner
|
|||
let dest: Element = try Element(Tag.valueOf(sourceTag), sourceEl.getBaseUri(), destAttrs)
|
||||
var numDiscarded: Int = 0
|
||||
|
||||
if let sourceAttrs = sourceEl.getAttributes()
|
||||
{
|
||||
for sourceAttr: Attribute in sourceAttrs
|
||||
{
|
||||
if (whitelist.isSafeAttribute(sourceTag, sourceEl, sourceAttr)){
|
||||
if let sourceAttrs = sourceEl.getAttributes() {
|
||||
for sourceAttr: Attribute in sourceAttrs {
|
||||
if (whitelist.isSafeAttribute(sourceTag, sourceEl, sourceAttr)) {
|
||||
destAttrs.put(attribute: sourceAttr)
|
||||
}else{
|
||||
} else {
|
||||
numDiscarded+=1
|
||||
}
|
||||
}
|
||||
|
@ -87,18 +84,15 @@ open class Cleaner
|
|||
|
||||
}
|
||||
|
||||
|
||||
extension Cleaner
|
||||
{
|
||||
fileprivate final class CleaningVisitor : NodeVisitor
|
||||
{
|
||||
extension Cleaner {
|
||||
fileprivate final class CleaningVisitor: NodeVisitor {
|
||||
var numDiscarded: Int = 0
|
||||
let root: Element
|
||||
var destination: Element? // current element to append nodes to
|
||||
|
||||
private weak var cleaner : Cleaner?
|
||||
private weak var cleaner: Cleaner?
|
||||
|
||||
public init(_ root: Element, _ destination: Element, _ cleaner : Cleaner) {
|
||||
public init(_ root: Element, _ destination: Element, _ cleaner: Cleaner) {
|
||||
self.root = root
|
||||
self.destination = destination
|
||||
}
|
||||
|
@ -118,11 +112,8 @@ extension Cleaner
|
|||
} else if let sourceText = (source as? TextNode) {
|
||||
let destText: TextNode = TextNode(sourceText.getWholeText(), source.getBaseUri())
|
||||
try destination?.appendChild(destText)
|
||||
}
|
||||
else if let sourceData = (source as? DataNode)
|
||||
{
|
||||
if sourceData.parent() != nil && cleaner!.whitelist.isSafeTag(sourceData.parent()!.nodeName())
|
||||
{
|
||||
} else if let sourceData = (source as? DataNode) {
|
||||
if sourceData.parent() != nil && cleaner!.whitelist.isSafeTag(sourceData.parent()!.nodeName()) {
|
||||
//let sourceData: DataNode = (DataNode) source
|
||||
let destData: DataNode = DataNode(sourceData.getWholeData(), source.getBaseUri())
|
||||
try destination?.appendChild(destData)
|
||||
|
@ -132,12 +123,9 @@ extension Cleaner
|
|||
}
|
||||
}
|
||||
|
||||
public func tail(_ source: Node, _ depth: Int)throws
|
||||
{
|
||||
if let x = (source as? Element)
|
||||
{
|
||||
if cleaner!.whitelist.isSafeTag(x.nodeName())
|
||||
{
|
||||
public func tail(_ source: Node, _ depth: Int)throws {
|
||||
if let x = (source as? Element) {
|
||||
if cleaner!.whitelist.isSafeTag(x.nodeName()) {
|
||||
// would have descended, so pop destination stack
|
||||
destination = destination?.parent()
|
||||
}
|
||||
|
@ -146,8 +134,7 @@ extension Cleaner
|
|||
}
|
||||
}
|
||||
|
||||
extension Cleaner
|
||||
{
|
||||
extension Cleaner {
|
||||
fileprivate struct ElementMeta {
|
||||
let el: Element
|
||||
let numAttribsDiscarded: Int
|
||||
|
|
|
@ -25,14 +25,14 @@ open class Collector {
|
|||
@return list of matches; empty if none
|
||||
*/
|
||||
open static func collect (_ eval: Evaluator, _ root: Element)throws->Elements {
|
||||
let elements : Elements = Elements()
|
||||
let elements: Elements = Elements()
|
||||
try NodeTraversor(Accumulator(root, elements, eval)).traverse(root)
|
||||
return elements
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class Accumulator : NodeVisitor {
|
||||
private final class Accumulator: NodeVisitor {
|
||||
private let root: Element
|
||||
private let elements: Elements
|
||||
private let eval: Evaluator
|
||||
|
@ -44,13 +44,12 @@ private final class Accumulator : NodeVisitor {
|
|||
}
|
||||
|
||||
open func head(_ node: Node, _ depth: Int) {
|
||||
if let el = node as? Element
|
||||
{
|
||||
do{
|
||||
if (try eval.matches(root, el)){
|
||||
if let el = node as? Element {
|
||||
do {
|
||||
if (try eval.matches(root, el)) {
|
||||
elements.add(el)
|
||||
}
|
||||
}catch{}
|
||||
} catch {}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@ import Foundation
|
|||
/**
|
||||
* Base combining (and, or) evaluator.
|
||||
*/
|
||||
public class CombiningEvaluator : Evaluator {
|
||||
public class CombiningEvaluator: Evaluator {
|
||||
|
||||
public private(set) var evaluators: Array<Evaluator>
|
||||
var num : Int = 0
|
||||
var num: Int = 0
|
||||
|
||||
public override init() {
|
||||
evaluators = Array<Evaluator>()
|
||||
|
@ -27,7 +27,7 @@ public class CombiningEvaluator : Evaluator {
|
|||
updateNumEvaluators()
|
||||
}
|
||||
|
||||
func rightMostEvaluator()->Evaluator? {
|
||||
func rightMostEvaluator() -> Evaluator? {
|
||||
return num > 0 && evaluators.count > 0 ? evaluators[num - 1] : nil
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ public class CombiningEvaluator : Evaluator {
|
|||
num = evaluators.count
|
||||
}
|
||||
|
||||
public final class And : CombiningEvaluator {
|
||||
public final class And: CombiningEvaluator {
|
||||
public override init(_ evaluators: Array<Evaluator>) {
|
||||
super.init(evaluators)
|
||||
}
|
||||
|
@ -49,36 +49,35 @@ public class CombiningEvaluator : Evaluator {
|
|||
super.init(evaluators)
|
||||
}
|
||||
|
||||
public override func matches(_ root: Element, _ node: Element)->Bool {
|
||||
for i in 0..<num
|
||||
{
|
||||
public override func matches(_ root: Element, _ node: Element) -> Bool {
|
||||
for i in 0..<num {
|
||||
let s = evaluators[i]
|
||||
do{
|
||||
if (try !s.matches(root, node)){
|
||||
do {
|
||||
if (try !s.matches(root, node)) {
|
||||
return false
|
||||
}
|
||||
}catch{}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
public override func toString()->String {
|
||||
let ar : [String] = evaluators.map { String($0.toString()) }
|
||||
public override func toString() -> String {
|
||||
let ar: [String] = evaluators.map { String($0.toString()) }
|
||||
return StringUtil.join(ar, sep: " ")
|
||||
}
|
||||
}
|
||||
|
||||
public final class Or : CombiningEvaluator {
|
||||
public final class Or: CombiningEvaluator {
|
||||
/**
|
||||
* Create a new Or evaluator. The initial evaluators are ANDed together and used as the first clause of the OR.
|
||||
* @param evaluators initial OR clause (these are wrapped into an AND evaluator).
|
||||
*/
|
||||
public override init(_ evaluators: Array<Evaluator>) {
|
||||
super.init()
|
||||
if (num > 1){
|
||||
if (num > 1) {
|
||||
self.evaluators.append(And(evaluators))
|
||||
}else{ // 0 or 1
|
||||
} else { // 0 or 1
|
||||
self.evaluators.append(contentsOf: evaluators)
|
||||
}
|
||||
updateNumEvaluators()
|
||||
|
@ -86,9 +85,9 @@ public class CombiningEvaluator : Evaluator {
|
|||
|
||||
override init(_ evaluators: Evaluator...) {
|
||||
super.init()
|
||||
if (num > 1){
|
||||
if (num > 1) {
|
||||
self.evaluators.append(And(evaluators))
|
||||
}else{ // 0 or 1
|
||||
} else { // 0 or 1
|
||||
self.evaluators.append(contentsOf: evaluators)
|
||||
}
|
||||
updateNumEvaluators()
|
||||
|
@ -103,21 +102,20 @@ public class CombiningEvaluator : Evaluator {
|
|||
updateNumEvaluators()
|
||||
}
|
||||
|
||||
public override func matches(_ root: Element, _ node: Element)->Bool {
|
||||
for i in 0..<num
|
||||
{
|
||||
let s : Evaluator = evaluators[i]
|
||||
do{
|
||||
if (try s.matches(root, node)){
|
||||
public override func matches(_ root: Element, _ node: Element) -> Bool {
|
||||
for i in 0..<num {
|
||||
let s: Evaluator = evaluators[i]
|
||||
do {
|
||||
if (try s.matches(root, node)) {
|
||||
return true
|
||||
}
|
||||
}catch{}
|
||||
} catch {}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public override func toString()->String {
|
||||
return ":or\(evaluators.map{String($0.toString())})"
|
||||
public override func toString() -> String {
|
||||
return ":or\(evaluators.map {String($0.toString())})"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@ import Foundation
|
|||
/**
|
||||
A comment node.
|
||||
*/
|
||||
public class Comment : Node {
|
||||
private static let COMMENT_KEY: String = "comment";
|
||||
public class Comment: Node {
|
||||
private static let COMMENT_KEY: String = "comment"
|
||||
|
||||
/**
|
||||
Create a new comment node.
|
||||
|
@ -20,59 +20,56 @@ public class Comment : Node {
|
|||
@param baseUri base URI
|
||||
*/
|
||||
public init(_ data: String, _ baseUri: String) {
|
||||
super.init(baseUri);
|
||||
do{
|
||||
try attributes?.put(Comment.COMMENT_KEY, data);
|
||||
}catch{}
|
||||
super.init(baseUri)
|
||||
do {
|
||||
try attributes?.put(Comment.COMMENT_KEY, data)
|
||||
} catch {}
|
||||
}
|
||||
|
||||
public override func nodeName()->String {
|
||||
return "#comment";
|
||||
public override func nodeName() -> String {
|
||||
return "#comment"
|
||||
}
|
||||
|
||||
/**
|
||||
Get the contents of the comment.
|
||||
@return comment content
|
||||
*/
|
||||
public func getData()->String {
|
||||
return attributes!.get(key: Comment.COMMENT_KEY);
|
||||
public func getData() -> String {
|
||||
return attributes!.get(key: Comment.COMMENT_KEY)
|
||||
}
|
||||
|
||||
override func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {
|
||||
if (out.prettyPrint()){
|
||||
indent(accum, depth, out);
|
||||
if (out.prettyPrint()) {
|
||||
indent(accum, depth, out)
|
||||
}
|
||||
accum
|
||||
.append("<!--")
|
||||
.append(getData())
|
||||
.append("-->");
|
||||
.append("-->")
|
||||
}
|
||||
|
||||
override func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {}
|
||||
|
||||
public override func toString()->String {
|
||||
do{
|
||||
public override func toString() -> String {
|
||||
do {
|
||||
return try
|
||||
outerHtml();
|
||||
}catch{
|
||||
outerHtml()
|
||||
} catch {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
public override func copy(with zone: NSZone? = nil) -> Any
|
||||
{
|
||||
let clone = Comment(attributes!.get(key: Comment.COMMENT_KEY),baseUri!)
|
||||
public override func copy(with zone: NSZone? = nil) -> Any {
|
||||
let clone = Comment(attributes!.get(key: Comment.COMMENT_KEY), baseUri!)
|
||||
return copy(clone: clone)
|
||||
}
|
||||
|
||||
public override func copy(parent: Node?)->Node
|
||||
{
|
||||
let clone = Comment(attributes!.get(key: Comment.COMMENT_KEY),baseUri!)
|
||||
return copy(clone: clone,parent: parent)
|
||||
public override func copy(parent: Node?) -> Node {
|
||||
let clone = Comment(attributes!.get(key: Comment.COMMENT_KEY), baseUri!)
|
||||
return copy(clone: clone, parent: parent)
|
||||
}
|
||||
|
||||
public override func copy(clone: Node, parent: Node?)->Node
|
||||
{
|
||||
return super.copy(clone: clone,parent: parent)
|
||||
public override func copy(clone: Node, parent: Node?) -> Node {
|
||||
return super.copy(clone: clone, parent: parent)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,3 @@
|
|||
|
||||
import Foundation
|
||||
//TODO:
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@ import Foundation
|
|||
/**
|
||||
A data node, for contents of style, script tags etc, where contents should not show in text().
|
||||
*/
|
||||
open class DataNode : Node{
|
||||
private static let DATA_KEY : String = "data"
|
||||
open class DataNode: Node {
|
||||
private static let DATA_KEY: String = "data"
|
||||
|
||||
/**
|
||||
Create a new DataNode.
|
||||
|
@ -21,13 +21,13 @@ open class DataNode : Node{
|
|||
*/
|
||||
public init(_ data: String, _ baseUri: String) {
|
||||
super.init(baseUri)
|
||||
do{
|
||||
do {
|
||||
try attributes?.put(DataNode.DATA_KEY, data)
|
||||
}catch{}
|
||||
} catch {}
|
||||
|
||||
}
|
||||
|
||||
open override func nodeName()->String {
|
||||
open override func nodeName() -> String {
|
||||
return "#data"
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ open class DataNode : Node{
|
|||
Get the data contents of this node. Will be unescaped and with original new lines, space etc.
|
||||
@return data
|
||||
*/
|
||||
open func getWholeData()->String {
|
||||
open func getWholeData() -> String {
|
||||
return attributes!.get(key: DataNode.DATA_KEY)
|
||||
}
|
||||
|
||||
|
@ -45,10 +45,10 @@ open class DataNode : Node{
|
|||
* @return this node, for chaining
|
||||
*/
|
||||
@discardableResult
|
||||
open func setWholeData(_ data: String)->DataNode {
|
||||
do{
|
||||
open func setWholeData(_ data: String) -> DataNode {
|
||||
do {
|
||||
try attributes?.put(DataNode.DATA_KEY, data)
|
||||
}catch{}
|
||||
} catch {}
|
||||
return self
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,6 @@ open class DataNode : Node{
|
|||
|
||||
override func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {}
|
||||
|
||||
|
||||
open override func toString()throws->String {
|
||||
return try outerHtml()
|
||||
}
|
||||
|
@ -74,20 +73,17 @@ open class DataNode : Node{
|
|||
return DataNode(data, baseUri)
|
||||
}
|
||||
|
||||
public override func copy(with zone: NSZone? = nil) -> Any
|
||||
{
|
||||
let clone = DataNode(attributes!.get(key: DataNode.DATA_KEY),baseUri!)
|
||||
public override func copy(with zone: NSZone? = nil) -> Any {
|
||||
let clone = DataNode(attributes!.get(key: DataNode.DATA_KEY), baseUri!)
|
||||
return copy(clone: clone)
|
||||
}
|
||||
|
||||
public override func copy(parent: Node?)->Node
|
||||
{
|
||||
let clone = DataNode(attributes!.get(key: DataNode.DATA_KEY),baseUri!)
|
||||
return copy(clone: clone,parent: parent)
|
||||
public override func copy(parent: Node?) -> Node {
|
||||
let clone = DataNode(attributes!.get(key: DataNode.DATA_KEY), baseUri!)
|
||||
return copy(clone: clone, parent: parent)
|
||||
}
|
||||
|
||||
public override func copy(clone: Node, parent: Node?)->Node
|
||||
{
|
||||
return super.copy(clone: clone,parent: parent)
|
||||
public override func copy(clone: Node, parent: Node?) -> Node {
|
||||
return super.copy(clone: clone, parent: parent)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,5 +21,4 @@ class DataUtil {
|
|||
static let mimeBoundaryChars = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".characters
|
||||
static let boundaryLength = 32
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -8,8 +8,7 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
open class Document : Element
|
||||
{
|
||||
open class Document: Element {
|
||||
public enum QuirksMode {
|
||||
case noQuirks, quirks, limitedQuirks
|
||||
}
|
||||
|
@ -25,7 +24,7 @@ open class Document : Element
|
|||
@see org.jsoup.Jsoup#parse
|
||||
@see #createShell
|
||||
*/
|
||||
public init(_ baseUri: String){
|
||||
public init(_ baseUri: String) {
|
||||
self._location = baseUri
|
||||
super.init(try! Tag.valueOf("#root", ParseSettings.htmlDefault), baseUri)
|
||||
}
|
||||
|
@ -35,7 +34,7 @@ open class Document : Element
|
|||
@param baseUri baseUri of document
|
||||
@return document with html, head, and body elements.
|
||||
*/
|
||||
static open func createShell(_ baseUri: String)->Document {
|
||||
static open func createShell(_ baseUri: String) -> Document {
|
||||
let doc: Document = Document(baseUri)
|
||||
let html: Element = try! doc.appendElement("html")
|
||||
try! html.appendElement("head")
|
||||
|
@ -49,7 +48,7 @@ open class Document : Element
|
|||
* this will return the final URL from which the document was served from.
|
||||
* @return location
|
||||
*/
|
||||
public func location()->String {
|
||||
public func location() -> String {
|
||||
return _location
|
||||
}
|
||||
|
||||
|
@ -57,7 +56,7 @@ open class Document : Element
|
|||
Accessor to the document's {@code head} element.
|
||||
@return {@code head}
|
||||
*/
|
||||
public func head()->Element? {
|
||||
public func head() -> Element? {
|
||||
return findFirstElementByTagName("head", self)
|
||||
}
|
||||
|
||||
|
@ -65,7 +64,7 @@ open class Document : Element
|
|||
Accessor to the document's {@code body} element.
|
||||
@return {@code body}
|
||||
*/
|
||||
public func body()->Element? {
|
||||
public func body() -> Element? {
|
||||
return findFirstElementByTagName("body", self)
|
||||
}
|
||||
|
||||
|
@ -110,15 +109,15 @@ open class Document : Element
|
|||
@discardableResult
|
||||
public func normalise()throws->Document {
|
||||
var htmlE: Element? = findFirstElementByTagName("html", self)
|
||||
if (htmlE == nil){
|
||||
if (htmlE == nil) {
|
||||
htmlE = try appendElement("html")
|
||||
}
|
||||
let htmlEl: Element = htmlE!
|
||||
|
||||
if (head() == nil){
|
||||
if (head() == nil) {
|
||||
try htmlEl.prependElement("head")
|
||||
}
|
||||
if (body() == nil){
|
||||
if (body() == nil) {
|
||||
try htmlEl.appendElement("body")
|
||||
}
|
||||
|
||||
|
@ -139,17 +138,15 @@ open class Document : Element
|
|||
// does not recurse.
|
||||
private func normaliseTextNodes(_ element: Element)throws {
|
||||
var toMove: Array<Node> = Array<Node>()
|
||||
for node:Node in element.childNodes
|
||||
{
|
||||
for node: Node in element.childNodes {
|
||||
if let tn = (node as? TextNode) {
|
||||
if (!tn.isBlank()){
|
||||
if (!tn.isBlank()) {
|
||||
toMove.append(tn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i in toMove.count-1...0
|
||||
{
|
||||
for i in toMove.count-1...0 {
|
||||
let node: Node = toMove[i]
|
||||
try element.removeChild(node)
|
||||
try body()?.prependChild(TextNode(" ", ""))
|
||||
|
@ -162,18 +159,16 @@ open class Document : Element
|
|||
let elements: Elements = try self.getElementsByTag(tag)
|
||||
let master: Element? = elements.first() // will always be available as created above if not existent
|
||||
if (elements.size() > 1) { // dupes, move contents to master
|
||||
var toMove:Array<Node> = Array<Node>()
|
||||
for i in 1..<elements.size()
|
||||
{
|
||||
var toMove: Array<Node> = Array<Node>()
|
||||
for i in 1..<elements.size() {
|
||||
let dupe: Node = elements.get(i)
|
||||
for node:Node in dupe.childNodes
|
||||
{
|
||||
for node: Node in dupe.childNodes {
|
||||
toMove.append(node)
|
||||
}
|
||||
try dupe.remove()
|
||||
}
|
||||
|
||||
for dupe:Node in toMove{
|
||||
for dupe: Node in toMove {
|
||||
try master?.appendChild(dupe)
|
||||
}
|
||||
}
|
||||
|
@ -183,15 +178,14 @@ open class Document : Element
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// fast method to get first by tag name, used for html, head, body finders
|
||||
private func findFirstElementByTagName(_ tag: String, _ node: Node)->Element? {
|
||||
if (node.nodeName()==tag){
|
||||
private func findFirstElementByTagName(_ tag: String, _ node: Node) -> Element? {
|
||||
if (node.nodeName()==tag) {
|
||||
return node as? Element
|
||||
}else {
|
||||
for child:Node in node.childNodes {
|
||||
} else {
|
||||
for child: Node in node.childNodes {
|
||||
let found: Element? = findFirstElementByTagName(tag, child)
|
||||
if (found != nil){
|
||||
if (found != nil) {
|
||||
return found
|
||||
}
|
||||
}
|
||||
|
@ -214,7 +208,7 @@ open class Document : Element
|
|||
return self
|
||||
}
|
||||
|
||||
open override func nodeName()->String {
|
||||
open override func nodeName() -> String {
|
||||
return "#document"
|
||||
}
|
||||
|
||||
|
@ -285,7 +279,7 @@ open class Document : Element
|
|||
* @return Returns <tt>true</tt> if the element is updated on charset
|
||||
* changes, <tt>false</tt> if not
|
||||
*/
|
||||
public func updateMetaCharsetElement()->Bool {
|
||||
public func updateMetaCharsetElement() -> Bool {
|
||||
return updateMetaCharset
|
||||
}
|
||||
|
||||
|
@ -363,7 +357,7 @@ open class Document : Element
|
|||
* Get the document's current output settings.
|
||||
* @return the document's current output settings.
|
||||
*/
|
||||
public func outputSettings()->OutputSettings {
|
||||
public func outputSettings() -> OutputSettings {
|
||||
return _outputSettings
|
||||
}
|
||||
|
||||
|
@ -373,7 +367,7 @@ open class Document : Element
|
|||
* @return this document, for chaining.
|
||||
*/
|
||||
@discardableResult
|
||||
public func outputSettings(_ outputSettings: OutputSettings)->Document {
|
||||
public func outputSettings(_ outputSettings: OutputSettings) -> Document {
|
||||
self._outputSettings = outputSettings
|
||||
return self
|
||||
}
|
||||
|
@ -383,33 +377,29 @@ open class Document : Element
|
|||
}
|
||||
|
||||
@discardableResult
|
||||
public func quirksMode(_ quirksMode: Document.QuirksMode)->Document {
|
||||
public func quirksMode(_ quirksMode: Document.QuirksMode) -> Document {
|
||||
self._quirksMode = quirksMode
|
||||
return self
|
||||
}
|
||||
|
||||
public override func copy(with zone: NSZone? = nil) -> Any
|
||||
{
|
||||
public override func copy(with zone: NSZone? = nil) -> Any {
|
||||
let clone = Document(_location)
|
||||
return copy(clone: clone)
|
||||
}
|
||||
|
||||
public override func copy(parent: Node?)->Node
|
||||
{
|
||||
public override func copy(parent: Node?) -> Node {
|
||||
let clone = Document(_location)
|
||||
return copy(clone: clone,parent: parent)
|
||||
return copy(clone: clone, parent: parent)
|
||||
}
|
||||
|
||||
public override func copy(clone: Node, parent: Node?)->Node
|
||||
{
|
||||
public override func copy(clone: Node, parent: Node?) -> Node {
|
||||
let clone = clone as! Document
|
||||
clone._outputSettings = _outputSettings.copy() as! OutputSettings
|
||||
clone._quirksMode = _quirksMode
|
||||
clone.updateMetaCharset = updateMetaCharset
|
||||
return super.copy(clone: clone,parent: parent)
|
||||
return super.copy(clone: clone, parent: parent)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class OutputSettings: NSCopying {
|
||||
|
@ -418,11 +408,11 @@ public class OutputSettings: NSCopying {
|
|||
*/
|
||||
public enum Syntax {case html, xml}
|
||||
|
||||
private var _escapeMode : Entities.EscapeMode = Entities.EscapeMode.base
|
||||
private var _encoder : String.Encoding = String.Encoding.utf8 // Charset.forName("UTF-8")
|
||||
private var _prettyPrint : Bool = true
|
||||
private var _outline : Bool = false
|
||||
private var _indentAmount : UInt = 1
|
||||
private var _escapeMode: Entities.EscapeMode = Entities.EscapeMode.base
|
||||
private var _encoder: String.Encoding = String.Encoding.utf8 // Charset.forName("UTF-8")
|
||||
private var _prettyPrint: Bool = true
|
||||
private var _outline: Bool = false
|
||||
private var _indentAmount: UInt = 1
|
||||
private var _syntax = Syntax.html
|
||||
|
||||
public init() {}
|
||||
|
@ -482,13 +472,11 @@ public class OutputSettings: NSCopying {
|
|||
return encoder(e)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the document's current output syntax.
|
||||
* @return current syntax
|
||||
*/
|
||||
public func syntax()-> Syntax {
|
||||
public func syntax() -> Syntax {
|
||||
return _syntax
|
||||
}
|
||||
|
||||
|
@ -499,7 +487,7 @@ public class OutputSettings: NSCopying {
|
|||
* @return the document's output settings, for chaining
|
||||
*/
|
||||
@discardableResult
|
||||
public func syntax(syntax: Syntax)->OutputSettings {
|
||||
public func syntax(syntax: Syntax) -> OutputSettings {
|
||||
_syntax = syntax
|
||||
return self
|
||||
}
|
||||
|
@ -509,7 +497,7 @@ public class OutputSettings: NSCopying {
|
|||
* the output, and the output will generally look like the input.
|
||||
* @return if pretty printing is enabled.
|
||||
*/
|
||||
public func prettyPrint()->Bool {
|
||||
public func prettyPrint() -> Bool {
|
||||
return _prettyPrint
|
||||
}
|
||||
|
||||
|
@ -519,7 +507,7 @@ public class OutputSettings: NSCopying {
|
|||
* @return this, for chaining
|
||||
*/
|
||||
@discardableResult
|
||||
public func prettyPrint(pretty: Bool)->OutputSettings {
|
||||
public func prettyPrint(pretty: Bool) -> OutputSettings {
|
||||
_prettyPrint = pretty
|
||||
return self
|
||||
}
|
||||
|
@ -529,7 +517,7 @@ public class OutputSettings: NSCopying {
|
|||
* all tags as block.
|
||||
* @return if outline mode is enabled.
|
||||
*/
|
||||
public func outline()->Bool {
|
||||
public func outline() -> Bool {
|
||||
return _outline
|
||||
}
|
||||
|
||||
|
@ -539,7 +527,7 @@ public class OutputSettings: NSCopying {
|
|||
* @return this, for chaining
|
||||
*/
|
||||
@discardableResult
|
||||
public func outline(outlineMode: Bool)->OutputSettings {
|
||||
public func outline(outlineMode: Bool) -> OutputSettings {
|
||||
_outline = outlineMode
|
||||
return self
|
||||
}
|
||||
|
@ -548,7 +536,7 @@ public class OutputSettings: NSCopying {
|
|||
* Get the current tag indent amount, used when pretty printing.
|
||||
* @return the current indent amount
|
||||
*/
|
||||
public func indentAmount()-> UInt {
|
||||
public func indentAmount() -> UInt {
|
||||
return _indentAmount
|
||||
}
|
||||
|
||||
|
@ -558,13 +546,12 @@ public class OutputSettings: NSCopying {
|
|||
* @return this, for chaining
|
||||
*/
|
||||
@discardableResult
|
||||
public func indentAmount(indentAmount: UInt)-> OutputSettings {
|
||||
public func indentAmount(indentAmount: UInt) -> OutputSettings {
|
||||
_indentAmount = indentAmount
|
||||
return self
|
||||
}
|
||||
|
||||
|
||||
public func copy(with zone: NSZone? = nil) -> Any{
|
||||
public func copy(with zone: NSZone? = nil) -> Any {
|
||||
let clone: OutputSettings = OutputSettings()
|
||||
clone.charset(_encoder) // new charset and charset encoder
|
||||
clone._escapeMode = _escapeMode//Entities.EscapeMode.valueOf(escapeMode.name())
|
||||
|
@ -572,7 +559,4 @@ public class OutputSettings: NSCopying {
|
|||
return clone
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@ import Foundation
|
|||
/**
|
||||
* A {@code <!DOCTYPE>} node.
|
||||
*/
|
||||
public class DocumentType : Node {
|
||||
private static let NAME: String = "name";
|
||||
private static let PUBLIC_ID: String = "publicId";
|
||||
private static let SYSTEM_ID: String = "systemId";
|
||||
public class DocumentType: Node {
|
||||
private static let NAME: String = "name"
|
||||
private static let PUBLIC_ID: String = "publicId"
|
||||
private static let SYSTEM_ID: String = "systemId"
|
||||
// todo: quirk mode from publicId and systemId
|
||||
|
||||
/**
|
||||
|
@ -25,57 +25,56 @@ public class DocumentType : Node {
|
|||
* @param baseUri the doctype's base URI
|
||||
*/
|
||||
public init(_ name: String, _ publicId: String, _ systemId: String, _ baseUri: String) {
|
||||
super.init(baseUri);
|
||||
do{
|
||||
try attr(DocumentType.NAME, name);
|
||||
try attr(DocumentType.PUBLIC_ID, publicId);
|
||||
try attr(DocumentType.SYSTEM_ID, systemId);
|
||||
}catch{}
|
||||
super.init(baseUri)
|
||||
do {
|
||||
try attr(DocumentType.NAME, name)
|
||||
try attr(DocumentType.PUBLIC_ID, publicId)
|
||||
try attr(DocumentType.SYSTEM_ID, systemId)
|
||||
} catch {}
|
||||
}
|
||||
|
||||
public override func nodeName()->String {
|
||||
return "#doctype";
|
||||
public override func nodeName() -> String {
|
||||
return "#doctype"
|
||||
}
|
||||
|
||||
override func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {
|
||||
if (out.syntax() == OutputSettings.Syntax.html && !has(DocumentType.PUBLIC_ID) && !has(DocumentType.SYSTEM_ID)) {
|
||||
// looks like a html5 doctype, go lowercase for aesthetics
|
||||
accum.append("<!doctype");
|
||||
accum.append("<!doctype")
|
||||
} else {
|
||||
accum.append("<!DOCTYPE");
|
||||
accum.append("<!DOCTYPE")
|
||||
}
|
||||
if (has(DocumentType.NAME)){
|
||||
do{
|
||||
accum.append(" ").append(try attr(DocumentType.NAME));
|
||||
}catch{}
|
||||
if (has(DocumentType.NAME)) {
|
||||
do {
|
||||
accum.append(" ").append(try attr(DocumentType.NAME))
|
||||
} catch {}
|
||||
|
||||
}
|
||||
if (has(DocumentType.PUBLIC_ID)){
|
||||
do{
|
||||
accum.append(" PUBLIC \"").append(try attr(DocumentType.PUBLIC_ID)).append("\"");
|
||||
}catch{}
|
||||
if (has(DocumentType.PUBLIC_ID)) {
|
||||
do {
|
||||
accum.append(" PUBLIC \"").append(try attr(DocumentType.PUBLIC_ID)).append("\"")
|
||||
} catch {}
|
||||
|
||||
}
|
||||
if (has(DocumentType.SYSTEM_ID)){
|
||||
do{
|
||||
accum.append(" \"").append(try attr(DocumentType.SYSTEM_ID)).append("\"");
|
||||
}catch{}
|
||||
if (has(DocumentType.SYSTEM_ID)) {
|
||||
do {
|
||||
accum.append(" \"").append(try attr(DocumentType.SYSTEM_ID)).append("\"")
|
||||
} catch {}
|
||||
|
||||
}
|
||||
accum.append(">");
|
||||
accum.append(">")
|
||||
}
|
||||
|
||||
override func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {
|
||||
}
|
||||
|
||||
private func has(_ attribute: String)->Bool {
|
||||
do{
|
||||
return !StringUtil.isBlank(try attr(attribute));
|
||||
}catch{return false}
|
||||
private func has(_ attribute: String) -> Bool {
|
||||
do {
|
||||
return !StringUtil.isBlank(try attr(attribute))
|
||||
} catch {return false}
|
||||
}
|
||||
|
||||
public override func copy(with zone: NSZone? = nil) -> Any
|
||||
{
|
||||
public override func copy(with zone: NSZone? = nil) -> Any {
|
||||
let clone = DocumentType(attributes!.get(key: DocumentType.NAME),
|
||||
attributes!.get(key: DocumentType.PUBLIC_ID),
|
||||
attributes!.get(key: DocumentType.SYSTEM_ID),
|
||||
|
@ -83,18 +82,16 @@ public class DocumentType : Node {
|
|||
return copy(clone: clone)
|
||||
}
|
||||
|
||||
public override func copy(parent: Node?)->Node
|
||||
{
|
||||
public override func copy(parent: Node?) -> Node {
|
||||
let clone = DocumentType(attributes!.get(key: DocumentType.NAME),
|
||||
attributes!.get(key: DocumentType.PUBLIC_ID),
|
||||
attributes!.get(key: DocumentType.SYSTEM_ID),
|
||||
baseUri!)
|
||||
return copy(clone: clone,parent: parent)
|
||||
return copy(clone: clone, parent: parent)
|
||||
}
|
||||
|
||||
public override func copy(clone: Node, parent: Node?)->Node
|
||||
{
|
||||
return super.copy(clone: clone,parent: parent)
|
||||
public override func copy(clone: Node, parent: Node?) -> Node {
|
||||
return super.copy(clone: clone, parent: parent)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
open class Element : Node
|
||||
{
|
||||
var _tag : Tag
|
||||
open class Element: Node {
|
||||
var _tag: Tag
|
||||
|
||||
//private static let classSplit : Pattern = Pattern("\\s+")
|
||||
private static let classSplit = "\\s+"
|
||||
|
@ -41,7 +40,7 @@ open class Element : Node
|
|||
super.init(baseUri, Attributes())
|
||||
}
|
||||
|
||||
open override func nodeName()->String {
|
||||
open override func nodeName() -> String {
|
||||
return _tag.getName()
|
||||
}
|
||||
/**
|
||||
|
@ -49,7 +48,7 @@ open class Element : Node
|
|||
*
|
||||
* @return the tag name
|
||||
*/
|
||||
open func tagName()->String {
|
||||
open func tagName() -> String {
|
||||
return _tag.getName()
|
||||
}
|
||||
|
||||
|
@ -72,7 +71,7 @@ open class Element : Node
|
|||
*
|
||||
* @return the tag object
|
||||
*/
|
||||
open func tag()->Tag {
|
||||
open func tag() -> Tag {
|
||||
return _tag
|
||||
}
|
||||
|
||||
|
@ -82,7 +81,7 @@ open class Element : Node
|
|||
*
|
||||
* @return true if block, false if not (and thus inline)
|
||||
*/
|
||||
open func isBlock()->Bool {
|
||||
open func isBlock() -> Bool {
|
||||
return _tag.isBlock()
|
||||
}
|
||||
|
||||
|
@ -91,11 +90,11 @@ open class Element : Node
|
|||
*
|
||||
* @return The id attribute, if present, or an empty string if not.
|
||||
*/
|
||||
open func id()->String {
|
||||
open func id() -> String {
|
||||
guard let attributes = attributes else {return ""}
|
||||
do{
|
||||
do {
|
||||
return try attributes.getIgnoreCase(key: "id")
|
||||
}catch{}
|
||||
} catch {}
|
||||
return ""
|
||||
}
|
||||
|
||||
|
@ -140,11 +139,11 @@ open class Element : Node
|
|||
* You can find elements that have data attributes using the {@code [^data-]} attribute key prefix selector.
|
||||
* @return a map of {@code key=value} custom data attributes.
|
||||
*/
|
||||
open func dataset()->Dictionary<String,String> {
|
||||
open func dataset()->Dictionary<String, String> {
|
||||
return attributes!.dataset()
|
||||
}
|
||||
|
||||
open override func parent()->Element? {
|
||||
open override func parent() -> Element? {
|
||||
return parentNode as? Element
|
||||
}
|
||||
|
||||
|
@ -152,14 +151,14 @@ open class Element : Node
|
|||
* Get this element's parent and ancestors, up to the document root.
|
||||
* @return this element's stack of parents, closest first.
|
||||
*/
|
||||
open func parents()->Elements {
|
||||
open func parents() -> Elements {
|
||||
let parents: Elements = Elements()
|
||||
Element.accumulateParents(self, parents)
|
||||
return parents
|
||||
}
|
||||
|
||||
private static func accumulateParents(_ el: Element, _ parents: Elements) {
|
||||
let parent : Element? = el.parent()
|
||||
let parent: Element? = el.parent()
|
||||
if (parent != nil && !(parent!.tagName() == "#root")) {
|
||||
parents.add(parent!)
|
||||
accumulateParents(parent!, parents)
|
||||
|
@ -177,7 +176,7 @@ open class Element : Node
|
|||
* @return the child element, if it exists, otherwise throws an {@code IndexOutOfBoundsException}
|
||||
* @see #childNode(int)
|
||||
*/
|
||||
open func child(_ index: Int)->Element {
|
||||
open func child(_ index: Int) -> Element {
|
||||
return children().get(index)
|
||||
}
|
||||
|
||||
|
@ -190,13 +189,11 @@ open class Element : Node
|
|||
* empty list.
|
||||
* @see #childNodes()
|
||||
*/
|
||||
open func children()->Elements {
|
||||
open func children() -> Elements {
|
||||
// create on the fly rather than maintaining two lists. if gets slow, memoize, and mark dirty on change
|
||||
var elements = Array<Element>()
|
||||
for node in childNodes
|
||||
{
|
||||
if let n = node as? Element
|
||||
{
|
||||
for node in childNodes {
|
||||
if let n = node as? Element {
|
||||
elements.append(n)
|
||||
}
|
||||
}
|
||||
|
@ -222,7 +219,7 @@ open class Element : Node
|
|||
open func textNodes()->Array<TextNode> {
|
||||
var textNodes = Array<TextNode>()
|
||||
for node in childNodes {
|
||||
if let n = node as? TextNode{
|
||||
if let n = node as? TextNode {
|
||||
textNodes.append(n)
|
||||
}
|
||||
}
|
||||
|
@ -240,9 +237,8 @@ open class Element : Node
|
|||
*/
|
||||
open func dataNodes()->Array<DataNode> {
|
||||
var dataNodes = Array<DataNode>()
|
||||
for node in childNodes
|
||||
{
|
||||
if let n = node as? DataNode{
|
||||
for node in childNodes {
|
||||
if let n = node as? DataNode {
|
||||
dataNodes.append(n)
|
||||
}
|
||||
}
|
||||
|
@ -315,7 +311,7 @@ open class Element : Node
|
|||
//Validate.notNull(children, "Children collection to be inserted must not be null.")
|
||||
var index = index
|
||||
let currentSize: Int = childNodeSize()
|
||||
if (index < 0){ index += currentSize + 1} // roll around
|
||||
if (index < 0) { index += currentSize + 1} // roll around
|
||||
try Validate.isTrue(val: index >= 0 && index <= currentSize, msg: "Insert position out of bounds.")
|
||||
|
||||
try addChildren(index, children)
|
||||
|
@ -358,7 +354,7 @@ open class Element : Node
|
|||
*/
|
||||
@discardableResult
|
||||
public func appendText(_ text: String)throws->Element {
|
||||
let node:TextNode = TextNode(text, getBaseUri())
|
||||
let node: TextNode = TextNode(text, getBaseUri())
|
||||
try appendChild(node)
|
||||
return self
|
||||
}
|
||||
|
@ -398,7 +394,7 @@ open class Element : Node
|
|||
@discardableResult
|
||||
public func prepend(_ html: String)throws->Element {
|
||||
let nodes: Array<Node> = try Parser.parseFragment(html, self, getBaseUri())
|
||||
try addChildren(0,nodes)
|
||||
try addChildren(0, nodes)
|
||||
return self
|
||||
}
|
||||
|
||||
|
@ -452,7 +448,7 @@ open class Element : Node
|
|||
* @return this element
|
||||
*/
|
||||
@discardableResult
|
||||
public func empty()->Element {
|
||||
public func empty() -> Element {
|
||||
childNodes.removeAll()
|
||||
return self
|
||||
}
|
||||
|
@ -479,7 +475,7 @@ open class Element : Node
|
|||
* @return the CSS Path that can be used to retrieve the element in a selector.
|
||||
*/
|
||||
public func cssSelector()throws->String {
|
||||
if (id().characters.count > 0){
|
||||
if (id().characters.count > 0) {
|
||||
return "#" + id()
|
||||
}
|
||||
|
||||
|
@ -488,7 +484,7 @@ open class Element : Node
|
|||
let selector: StringBuilder = StringBuilder(string: tagName)
|
||||
let cl = try classNames()
|
||||
let classes: String = cl.joined(separator: ".")
|
||||
if (classes.characters.count > 0){
|
||||
if (classes.characters.count > 0) {
|
||||
selector.append(".").append(classes)
|
||||
}
|
||||
|
||||
|
@ -498,7 +494,7 @@ open class Element : Node
|
|||
}
|
||||
|
||||
selector.insert(0, " > ")
|
||||
if (try parent()!.select(selector.toString()).array().count > 1){
|
||||
if (try parent()!.select(selector.toString()).array().count > 1) {
|
||||
selector.append(":nth-child(\(try elementSiblingIndex() + 1))")
|
||||
}
|
||||
|
||||
|
@ -510,14 +506,14 @@ open class Element : Node
|
|||
* of itself, so will not be included in the returned list.
|
||||
* @return sibling elements
|
||||
*/
|
||||
public func siblingElements()->Elements {
|
||||
if (parentNode == nil){return Elements()}
|
||||
public func siblingElements() -> Elements {
|
||||
if (parentNode == nil) {return Elements()}
|
||||
|
||||
let elements: Array<Element>? = parent()?.children().array()
|
||||
let siblings: Elements = Elements()
|
||||
if let elements = elements{
|
||||
for el:Element in elements{
|
||||
if (el != self){
|
||||
if let elements = elements {
|
||||
for el: Element in elements {
|
||||
if (el != self) {
|
||||
siblings.add(el)
|
||||
}
|
||||
}
|
||||
|
@ -539,10 +535,10 @@ open class Element : Node
|
|||
let siblings: Array<Element>? = parent()?.children().array()
|
||||
let index: Int? = try Element.indexInList(self, siblings)
|
||||
try Validate.notNull(obj: index)
|
||||
if let siblings = siblings{
|
||||
if (siblings.count > index!+1){
|
||||
if let siblings = siblings {
|
||||
if (siblings.count > index!+1) {
|
||||
return siblings[index!+1]
|
||||
}else{
|
||||
} else {
|
||||
return nil}
|
||||
}
|
||||
return nil
|
||||
|
@ -558,9 +554,9 @@ open class Element : Node
|
|||
let siblings: Array<Element>? = parent()?.children().array()
|
||||
let index: Int? = try Element.indexInList(self, siblings)
|
||||
try Validate.notNull(obj: index)
|
||||
if (index! > 0){
|
||||
if (index! > 0) {
|
||||
return siblings?[index!-1]
|
||||
}else{
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -569,7 +565,7 @@ open class Element : Node
|
|||
* Gets the first element sibling of this element.
|
||||
* @return the first sibling that is an element (aka the parent's first element child)
|
||||
*/
|
||||
public func firstElementSibling()->Element? {
|
||||
public func firstElementSibling() -> Element? {
|
||||
// todo: should firstSibling() exclude this?
|
||||
let siblings: Array<Element>? = parent()?.children().array()
|
||||
return (siblings != nil && siblings!.count > 1) ? siblings![0] : nil
|
||||
|
@ -590,18 +586,17 @@ open class Element : Node
|
|||
* Gets the last element sibling of this element
|
||||
* @return the last sibling that is an element (aka the parent's last element child)
|
||||
*/
|
||||
public func lastElementSibling()->Element? {
|
||||
public func lastElementSibling() -> Element? {
|
||||
let siblings: Array<Element>? = parent()?.children().array()
|
||||
return (siblings != nil && siblings!.count > 1) ? siblings![siblings!.count - 1] : nil
|
||||
}
|
||||
|
||||
private static func indexInList(_ search: Element, _ elements: Array<Element>?)throws->Int? {
|
||||
try Validate.notNull(obj: elements)
|
||||
if let elements = elements
|
||||
{
|
||||
if let elements = elements {
|
||||
for i in 0..<elements.count {
|
||||
let element: Element = elements[i]
|
||||
if (element == search){
|
||||
if (element == search) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
@ -636,9 +631,9 @@ open class Element : Node
|
|||
try Validate.notEmpty(string: id)
|
||||
|
||||
let elements: Elements = try Collector.collect(Evaluator.Id(id), self)
|
||||
if (elements.array().count > 0){
|
||||
if (elements.array().count > 0) {
|
||||
return elements.get(0)
|
||||
}else{
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -660,7 +655,6 @@ open class Element : Node
|
|||
return try Collector.collect(Evaluator.Class(className), self)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find elements that have a named attribute set. Case insensitive.
|
||||
*
|
||||
|
@ -760,11 +754,11 @@ open class Element : Node
|
|||
* @return elements that have attributes matching this regular expression
|
||||
*/
|
||||
public func getElementsByAttributeValueMatching(_ key: String, _ regex: String)throws->Elements {
|
||||
var pattern : Pattern
|
||||
var pattern: Pattern
|
||||
do {
|
||||
pattern = Pattern.compile(regex)
|
||||
try pattern.validate()
|
||||
} catch{
|
||||
} catch {
|
||||
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Pattern syntax error: \(regex)")
|
||||
}
|
||||
return try getElementsByAttributeValueMatching(key, pattern)
|
||||
|
@ -840,13 +834,12 @@ open class Element : Node
|
|||
do {
|
||||
pattern = Pattern.compile(regex)
|
||||
try pattern.validate()
|
||||
} catch{
|
||||
} catch {
|
||||
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Pattern syntax error: \(regex)")
|
||||
}
|
||||
return try getElementsMatchingText(pattern)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find elements whose own text matches the supplied regular expression.
|
||||
* @param pattern regular expression to match text against
|
||||
|
@ -868,7 +861,7 @@ open class Element : Node
|
|||
do {
|
||||
pattern = Pattern.compile(regex)
|
||||
try pattern.validate()
|
||||
} catch{
|
||||
} catch {
|
||||
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Pattern syntax error: \(regex)")
|
||||
}
|
||||
return try getElementsMatchingOwnText(pattern)
|
||||
|
@ -883,7 +876,6 @@ open class Element : Node
|
|||
return try Collector.collect(Evaluator.AllElements(), self)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the combined text of this element and all its children. Whitespace is normalized and trimmed.
|
||||
* <p>
|
||||
|
@ -893,20 +885,18 @@ open class Element : Node
|
|||
* @see #ownText()
|
||||
* @see #textNodes()
|
||||
*/
|
||||
class textNodeVisitor: NodeVisitor
|
||||
{
|
||||
class textNodeVisitor: NodeVisitor {
|
||||
let accum: StringBuilder
|
||||
init(_ accum: StringBuilder) {
|
||||
self.accum = accum
|
||||
}
|
||||
public func head(_ node: Node, _ depth: Int) {
|
||||
if let textNode = (node as? TextNode)
|
||||
{
|
||||
if let textNode = (node as? TextNode) {
|
||||
Element.appendNormalisedText(accum, textNode)
|
||||
} else if let element = (node as? Element) {
|
||||
if (accum.length > 0 &&
|
||||
(element.isBlock() || element._tag.getName() == "br") &&
|
||||
!TextNode.lastCharIsWhitespace(accum)){
|
||||
!TextNode.lastCharIsWhitespace(accum)) {
|
||||
accum.append(" ")
|
||||
}
|
||||
}
|
||||
|
@ -932,7 +922,7 @@ open class Element : Node
|
|||
* @see #text()
|
||||
* @see #textNodes()
|
||||
*/
|
||||
public func ownText()->String {
|
||||
public func ownText() -> String {
|
||||
let sb: StringBuilder = StringBuilder()
|
||||
ownText(sb)
|
||||
return sb.toString().trim()
|
||||
|
@ -951,20 +941,20 @@ open class Element : Node
|
|||
private static func appendNormalisedText(_ accum: StringBuilder, _ textNode: TextNode) {
|
||||
let text: String = textNode.getWholeText()
|
||||
|
||||
if (Element.preserveWhitespace(textNode.parentNode)){
|
||||
if (Element.preserveWhitespace(textNode.parentNode)) {
|
||||
accum.append(text)
|
||||
}else{
|
||||
} else {
|
||||
StringUtil.appendNormalisedWhitespace(accum, string: text, stripLeading: TextNode.lastCharIsWhitespace(accum))
|
||||
}
|
||||
}
|
||||
|
||||
private static func appendWhitespaceIfBr(_ element: Element, _ accum: StringBuilder) {
|
||||
if (element._tag.getName() == "br" && !TextNode.lastCharIsWhitespace(accum)){
|
||||
if (element._tag.getName() == "br" && !TextNode.lastCharIsWhitespace(accum)) {
|
||||
accum.append(" ")
|
||||
}
|
||||
}
|
||||
|
||||
static func preserveWhitespace(_ node: Node?)->Bool {
|
||||
static func preserveWhitespace(_ node: Node?) -> Bool {
|
||||
// looks only at this element and one level up, to prevent recursion & needless stack searches
|
||||
if let element = (node as? Element) {
|
||||
return element._tag.preserveWhitespace() || element.parent() != nil && element.parent()!._tag.preserveWhitespace()
|
||||
|
@ -972,7 +962,6 @@ open class Element : Node
|
|||
return false
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the text of this element. Any existing contents (text or elements) will be cleared
|
||||
* @param text unencoded text
|
||||
|
@ -990,14 +979,14 @@ open class Element : Node
|
|||
Test if this element has any text content (that is not just whitespace).
|
||||
@return true if element has non-blank text content.
|
||||
*/
|
||||
public func hasText()->Bool {
|
||||
for child:Node in childNodes {
|
||||
public func hasText() -> Bool {
|
||||
for child: Node in childNodes {
|
||||
if let textNode = (child as? TextNode) {
|
||||
if (!textNode.isBlank()){
|
||||
if (!textNode.isBlank()) {
|
||||
return true
|
||||
}
|
||||
} else if let el = (child as? Element) {
|
||||
if (el.hasText()){
|
||||
if (el.hasText()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -1011,7 +1000,7 @@ open class Element : Node
|
|||
*
|
||||
* @see #dataNodes()
|
||||
*/
|
||||
public func data()->String {
|
||||
public func data() -> String {
|
||||
let sb: StringBuilder = StringBuilder()
|
||||
|
||||
for childNode: Node in childNodes {
|
||||
|
@ -1042,7 +1031,7 @@ open class Element : Node
|
|||
*/
|
||||
public func classNames()throws->OrderedSet<String> {
|
||||
let fitted = try className().replaceAll(of: Element.classSplit, with: " ", options:.caseInsensitive)
|
||||
let names:[String] = fitted.components(separatedBy: " ")
|
||||
let names: [String] = fitted.components(separatedBy: " ")
|
||||
let classNames: OrderedSet<String> = OrderedSet(sequence:names)
|
||||
classNames.remove("") // if classNames() was empty, would include an empty class
|
||||
return classNames
|
||||
|
@ -1065,7 +1054,7 @@ open class Element : Node
|
|||
* @return true if it does, false if not
|
||||
*/
|
||||
// performance sensitive
|
||||
public func hasClass(_ className: String)->Bool {
|
||||
public func hasClass(_ className: String) -> Bool {
|
||||
let classAtt: String? = attributes?.get(key: "class")
|
||||
let len: Int = (classAtt != nil) ? classAtt!.characters.count : 0
|
||||
let wantLen: Int = className.characters.count
|
||||
|
@ -1143,8 +1132,8 @@ open class Element : Node
|
|||
@discardableResult
|
||||
public func toggleClass(_ className: String)throws->Element {
|
||||
let classes: OrderedSet<String> = try classNames()
|
||||
if (classes.contains(className)){classes.remove(className)
|
||||
}else{
|
||||
if (classes.contains(className)) {classes.remove(className)
|
||||
} else {
|
||||
classes.append(className)
|
||||
}
|
||||
try classNames(classes)
|
||||
|
@ -1157,9 +1146,9 @@ open class Element : Node
|
|||
* @return the value of the form element, or empty string if not set.
|
||||
*/
|
||||
public func val()throws->String {
|
||||
if (tagName()=="textarea"){
|
||||
if (tagName()=="textarea") {
|
||||
return try text()
|
||||
}else{
|
||||
} else {
|
||||
return try attr("value")
|
||||
}
|
||||
}
|
||||
|
@ -1170,19 +1159,17 @@ open class Element : Node
|
|||
* @return this element (for chaining)
|
||||
*/
|
||||
@discardableResult
|
||||
public func val(_ value:String)throws->Element {
|
||||
if (tagName() == "textarea"){
|
||||
public func val(_ value: String)throws->Element {
|
||||
if (tagName() == "textarea") {
|
||||
try text(value)
|
||||
}else{
|
||||
} else {
|
||||
try attr("value", value)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
override func outerHtmlHead(_ accum:StringBuilder, _ depth: Int, _ out: OutputSettings)throws
|
||||
{
|
||||
if (out.prettyPrint() && (_tag.formatAsBlock() || (parent() != nil && parent()!.tag().formatAsBlock()) || out.outline()))
|
||||
{
|
||||
override func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings)throws {
|
||||
if (out.prettyPrint() && (_tag.formatAsBlock() || (parent() != nil && parent()!.tag().formatAsBlock()) || out.outline())) {
|
||||
if (accum.length > 0) {
|
||||
indent(accum, depth, out)
|
||||
}
|
||||
|
@ -1194,22 +1181,21 @@ open class Element : Node
|
|||
|
||||
// selfclosing includes unknown tags, isEmpty defines tags that are always empty
|
||||
if (childNodes.isEmpty && _tag.isSelfClosing()) {
|
||||
if (out.syntax() == OutputSettings.Syntax.html && _tag.isEmpty()){
|
||||
if (out.syntax() == OutputSettings.Syntax.html && _tag.isEmpty()) {
|
||||
accum.append(">")
|
||||
}else{
|
||||
} else {
|
||||
accum.append(" />") // <img> in html, <img /> in xml
|
||||
}
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
accum.append(">")
|
||||
}
|
||||
}
|
||||
|
||||
override func outerHtmlTail(_ accum:StringBuilder, _ depth: Int, _ out: OutputSettings){
|
||||
override func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {
|
||||
if (!(childNodes.isEmpty && _tag.isSelfClosing())) {
|
||||
if (out.prettyPrint() && (!childNodes.isEmpty && (
|
||||
_tag.formatAsBlock() || (out.outline() && (childNodes.count>1 || (childNodes.count==1 && !(((childNodes[0] as? TextNode) != nil)))))
|
||||
))){
|
||||
))) {
|
||||
indent(accum, depth, out)
|
||||
}
|
||||
accum.append("</").append(tagName()).append(">")
|
||||
|
@ -1230,7 +1216,7 @@ open class Element : Node
|
|||
}
|
||||
|
||||
private func html2(_ accum: StringBuilder)throws {
|
||||
for node in childNodes{
|
||||
for node in childNodes {
|
||||
try node.outerHtml(accum)
|
||||
}
|
||||
}
|
||||
|
@ -1239,7 +1225,7 @@ open class Element : Node
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
open override func html(_ appendable: StringBuilder)throws->StringBuilder {
|
||||
for node in childNodes{
|
||||
for node in childNodes {
|
||||
try node.outerHtml(appendable)
|
||||
}
|
||||
return appendable
|
||||
|
@ -1258,32 +1244,26 @@ open class Element : Node
|
|||
return self
|
||||
}
|
||||
|
||||
|
||||
|
||||
open override func toString()throws->String {
|
||||
return try outerHtml()
|
||||
}
|
||||
|
||||
public override func copy(with zone: NSZone? = nil) -> Any
|
||||
{
|
||||
let clone = Element(_tag,baseUri!,attributes!)
|
||||
public override func copy(with zone: NSZone? = nil) -> Any {
|
||||
let clone = Element(_tag, baseUri!, attributes!)
|
||||
return copy(clone: clone)
|
||||
}
|
||||
|
||||
public override func copy(parent: Node?)->Node
|
||||
{
|
||||
let clone = Element(_tag,baseUri!,attributes!)
|
||||
return copy(clone: clone,parent: parent)
|
||||
public override func copy(parent: Node?) -> Node {
|
||||
let clone = Element(_tag, baseUri!, attributes!)
|
||||
return copy(clone: clone, parent: parent)
|
||||
}
|
||||
public override func copy(clone: Node, parent: Node?)->Node
|
||||
{
|
||||
return super.copy(clone: clone,parent: parent)
|
||||
public override func copy(clone: Node, parent: Node?) -> Node {
|
||||
return super.copy(clone: clone, parent: parent)
|
||||
}
|
||||
|
||||
override public var hashValue: Int
|
||||
{
|
||||
override public var hashValue: Int {
|
||||
var h = super.hashValue
|
||||
h = Int.addWithOverflow(Int.multiplyWithOverflow(31, h).0,_tag.hashValue).0
|
||||
h = Int.addWithOverflow(Int.multiplyWithOverflow(31, h).0, _tag.hashValue).0
|
||||
return h
|
||||
}
|
||||
|
||||
|
|
|
@ -16,16 +16,15 @@ import Foundation
|
|||
|
||||
//open typealias Elements = Array<Element>
|
||||
//typealias E = Element
|
||||
open class Elements: NSCopying
|
||||
{
|
||||
fileprivate var this : Array<Element> = Array<Element>()
|
||||
open class Elements: NSCopying {
|
||||
fileprivate var this: Array<Element> = Array<Element>()
|
||||
|
||||
public init() {
|
||||
}
|
||||
public init(_ a: Array<Element>){
|
||||
public init(_ a: Array<Element>) {
|
||||
this = a
|
||||
}
|
||||
public init(_ a: OrderedSet<Element>){
|
||||
public init(_ a: OrderedSet<Element>) {
|
||||
this.append(contentsOf: a)
|
||||
}
|
||||
|
||||
|
@ -33,13 +32,12 @@ open class Elements: NSCopying
|
|||
* Creates a deep copy of these elements.
|
||||
* @return a deep copy
|
||||
*/
|
||||
public func copy(with zone: NSZone? = nil) -> Any
|
||||
{
|
||||
public func copy(with zone: NSZone? = nil) -> Any {
|
||||
let clone: Elements = Elements()
|
||||
for e: Element in this{
|
||||
for e: Element in this {
|
||||
clone.add(e.copy() as! Element)
|
||||
}
|
||||
return clone;
|
||||
return clone
|
||||
}
|
||||
|
||||
// attribute methods
|
||||
|
@ -52,11 +50,11 @@ open class Elements: NSCopying
|
|||
*/
|
||||
open func attr(_ attributeKey: String)throws->String {
|
||||
for element in this {
|
||||
if (element.hasAttr(attributeKey)){
|
||||
if (element.hasAttr(attributeKey)) {
|
||||
return try element.attr(attributeKey)
|
||||
}
|
||||
}
|
||||
return "";
|
||||
return ""
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -64,11 +62,11 @@ open class Elements: NSCopying
|
|||
@param attributeKey attribute key
|
||||
@return true if any of the elements have the attribute; false if none do.
|
||||
*/
|
||||
open func hasAttr(_ attributeKey: String)->Bool {
|
||||
open func hasAttr(_ attributeKey: String) -> Bool {
|
||||
for element in this {
|
||||
if element.hasAttr(attributeKey) {return true}
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,9 +78,9 @@ open class Elements: NSCopying
|
|||
@discardableResult
|
||||
open func attr(_ attributeKey: String, _ attributeValue: String)throws->Elements {
|
||||
for element in this {
|
||||
try element.attr(attributeKey, attributeValue);
|
||||
try element.attr(attributeKey, attributeValue)
|
||||
}
|
||||
return self;
|
||||
return self
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -93,9 +91,9 @@ open class Elements: NSCopying
|
|||
@discardableResult
|
||||
open func removeAttr(_ attributeKey: String)throws->Elements {
|
||||
for element in this {
|
||||
try element.removeAttr(attributeKey);
|
||||
try element.removeAttr(attributeKey)
|
||||
}
|
||||
return self;
|
||||
return self
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -106,9 +104,9 @@ open class Elements: NSCopying
|
|||
@discardableResult
|
||||
open func addClass(_ className: String)throws->Elements {
|
||||
for element in this {
|
||||
try element.addClass(className);
|
||||
try element.addClass(className)
|
||||
}
|
||||
return self;
|
||||
return self
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,9 +117,9 @@ open class Elements: NSCopying
|
|||
@discardableResult
|
||||
open func removeClass(_ className: String)throws->Elements {
|
||||
for element: Element in this {
|
||||
try element.removeClass(className);
|
||||
try element.removeClass(className)
|
||||
}
|
||||
return self;
|
||||
return self
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -132,9 +130,9 @@ open class Elements: NSCopying
|
|||
@discardableResult
|
||||
open func toggleClass(_ className: String)throws->Elements {
|
||||
for element: Element in this {
|
||||
try element.toggleClass(className);
|
||||
try element.toggleClass(className)
|
||||
}
|
||||
return self;
|
||||
return self
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -143,13 +141,13 @@ open class Elements: NSCopying
|
|||
@return true if any do, false if none do
|
||||
*/
|
||||
|
||||
open func hasClass(_ className: String)->Bool {
|
||||
open func hasClass(_ className: String) -> Bool {
|
||||
for element: Element in this {
|
||||
if (element.hasClass(className)){
|
||||
return true;
|
||||
if (element.hasClass(className)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -158,10 +156,10 @@ open class Elements: NSCopying
|
|||
* @see Element#val()
|
||||
*/
|
||||
open func val()throws->String {
|
||||
if (size() > 0){
|
||||
return try first()!.val();
|
||||
if (size() > 0) {
|
||||
return try first()!.val()
|
||||
}
|
||||
return "";
|
||||
return ""
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -171,10 +169,10 @@ open class Elements: NSCopying
|
|||
*/
|
||||
@discardableResult
|
||||
open func val(_ value: String)throws->Elements {
|
||||
for element: Element in this{
|
||||
try element.val(value);
|
||||
for element: Element in this {
|
||||
try element.val(value)
|
||||
}
|
||||
return self;
|
||||
return self
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -186,25 +184,23 @@ open class Elements: NSCopying
|
|||
* @see Element#text()
|
||||
*/
|
||||
open func text()throws->String {
|
||||
let sb: StringBuilder = StringBuilder();
|
||||
for element: Element in this
|
||||
{
|
||||
if (sb.length != 0){
|
||||
let sb: StringBuilder = StringBuilder()
|
||||
for element: Element in this {
|
||||
if (sb.length != 0) {
|
||||
sb.append(" ")
|
||||
}
|
||||
sb.append(try element.text());
|
||||
sb.append(try element.text())
|
||||
}
|
||||
return sb.toString();
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
open func hasText()->Bool {
|
||||
for element:Element in this
|
||||
{
|
||||
if (element.hasText()){
|
||||
return true;
|
||||
open func hasText() -> Bool {
|
||||
for element: Element in this {
|
||||
if (element.hasText()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -214,14 +210,14 @@ open class Elements: NSCopying
|
|||
* @see #outerHtml()
|
||||
*/
|
||||
open func html()throws->String {
|
||||
let sb: StringBuilder = StringBuilder();
|
||||
let sb: StringBuilder = StringBuilder()
|
||||
for element: Element in this {
|
||||
if (sb.length != 0){
|
||||
if (sb.length != 0) {
|
||||
sb.append("\n")
|
||||
}
|
||||
sb.append(try element.html());
|
||||
sb.append(try element.html())
|
||||
}
|
||||
return sb.toString();
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -231,15 +227,14 @@ open class Elements: NSCopying
|
|||
* @see #html()
|
||||
*/
|
||||
open func outerHtml()throws->String {
|
||||
let sb: StringBuilder = StringBuilder();
|
||||
for element in this
|
||||
{
|
||||
if (sb.length != 0){
|
||||
let sb: StringBuilder = StringBuilder()
|
||||
for element in this {
|
||||
if (sb.length != 0) {
|
||||
sb.append("\n")
|
||||
}
|
||||
sb.append(try element.outerHtml());
|
||||
sb.append(try element.outerHtml())
|
||||
}
|
||||
return sb.toString();
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -250,7 +245,7 @@ open class Elements: NSCopying
|
|||
*/
|
||||
|
||||
open func toString()throws->String {
|
||||
return try outerHtml();
|
||||
return try outerHtml()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -263,9 +258,9 @@ open class Elements: NSCopying
|
|||
@discardableResult
|
||||
open func tagName(_ tagName: String)throws->Elements {
|
||||
for element: Element in this {
|
||||
try element.tagName(tagName);
|
||||
try element.tagName(tagName)
|
||||
}
|
||||
return self;
|
||||
return self
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -277,9 +272,9 @@ open class Elements: NSCopying
|
|||
@discardableResult
|
||||
open func html(_ html: String)throws->Elements {
|
||||
for element: Element in this {
|
||||
try element.html(html);
|
||||
try element.html(html)
|
||||
}
|
||||
return self;
|
||||
return self
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -291,9 +286,9 @@ open class Elements: NSCopying
|
|||
@discardableResult
|
||||
open func prepend(_ html: String)throws->Elements {
|
||||
for element: Element in this {
|
||||
try element.prepend(html);
|
||||
try element.prepend(html)
|
||||
}
|
||||
return self;
|
||||
return self
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -305,9 +300,9 @@ open class Elements: NSCopying
|
|||
@discardableResult
|
||||
open func append(_ html: String)throws->Elements {
|
||||
for element: Element in this {
|
||||
try element.append(html);
|
||||
try element.append(html)
|
||||
}
|
||||
return self;
|
||||
return self
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -319,9 +314,9 @@ open class Elements: NSCopying
|
|||
@discardableResult
|
||||
open func before(_ html: String)throws->Elements {
|
||||
for element: Element in this {
|
||||
try element.before(html);
|
||||
try element.before(html)
|
||||
}
|
||||
return self;
|
||||
return self
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -333,9 +328,9 @@ open class Elements: NSCopying
|
|||
@discardableResult
|
||||
open func after(_ html: String)throws->Elements {
|
||||
for element: Element in this {
|
||||
try element.after(html);
|
||||
try element.after(html)
|
||||
}
|
||||
return self;
|
||||
return self
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -349,11 +344,11 @@ open class Elements: NSCopying
|
|||
*/
|
||||
@discardableResult
|
||||
open func wrap(_ html: String)throws->Elements {
|
||||
try Validate.notEmpty(string: html);
|
||||
try Validate.notEmpty(string: html)
|
||||
for element: Element in this {
|
||||
try element.wrap(html);
|
||||
try element.wrap(html)
|
||||
}
|
||||
return self;
|
||||
return self
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -373,9 +368,9 @@ open class Elements: NSCopying
|
|||
@discardableResult
|
||||
open func unwrap()throws->Elements {
|
||||
for element: Element in this {
|
||||
try element.unwrap();
|
||||
try element.unwrap()
|
||||
}
|
||||
return self;
|
||||
return self
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -390,11 +385,11 @@ open class Elements: NSCopying
|
|||
* @see #remove()
|
||||
*/
|
||||
@discardableResult
|
||||
open func empty()->Elements {
|
||||
open func empty() -> Elements {
|
||||
for element: Element in this {
|
||||
element.empty();
|
||||
element.empty()
|
||||
}
|
||||
return self;
|
||||
return self
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -411,11 +406,10 @@ open class Elements: NSCopying
|
|||
*/
|
||||
@discardableResult
|
||||
open func remove()throws->Elements {
|
||||
for element in this
|
||||
{
|
||||
try element.remove();
|
||||
for element in this {
|
||||
try element.remove()
|
||||
}
|
||||
return self;
|
||||
return self
|
||||
}
|
||||
|
||||
// filters
|
||||
|
@ -426,7 +420,7 @@ open class Elements: NSCopying
|
|||
* @return the filtered list of elements, or an empty list if none match.
|
||||
*/
|
||||
open func select(_ query: String)throws->Elements {
|
||||
return try Selector.select(query, this);
|
||||
return try Selector.select(query, this)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -440,8 +434,8 @@ open class Elements: NSCopying
|
|||
* @return a new elements list that contains only the filtered results
|
||||
*/
|
||||
open func not(_ query: String)throws->Elements {
|
||||
let out: Elements = try Selector.select(query, this);
|
||||
return Selector.filterOut(this, out.this);
|
||||
let out: Elements = try Selector.select(query, this)
|
||||
return Selector.filterOut(this, out.this)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -451,8 +445,8 @@ open class Elements: NSCopying
|
|||
* @param index the (zero-based) index of the element in the list to retain
|
||||
* @return Elements containing only the specified element, or, if that element did not exist, an empty list.
|
||||
*/
|
||||
open func eq(_ index: Int)->Elements {
|
||||
return size() > index ? Elements([get(index)]) : Elements();
|
||||
open func eq(_ index: Int) -> Elements {
|
||||
return size() > index ? Elements([get(index)]) : Elements()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -461,8 +455,8 @@ 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 children: Elements = try select(query);
|
||||
return !children.isEmpty();
|
||||
let children: Elements = try select(query)
|
||||
return !children.isEmpty()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -470,13 +464,12 @@ open class Elements: NSCopying
|
|||
* @return all of the parents and ancestor elements of the matched elements
|
||||
*/
|
||||
|
||||
open func parents()->Elements {
|
||||
let combo: OrderedSet<Element> = OrderedSet<Element>();
|
||||
for e:Element in this
|
||||
{
|
||||
combo.append(contentsOf: e.parents().array());
|
||||
open func parents() -> Elements {
|
||||
let combo: OrderedSet<Element> = OrderedSet<Element>()
|
||||
for e: Element in this {
|
||||
combo.append(contentsOf: e.parents().array())
|
||||
}
|
||||
return Elements(combo);
|
||||
return Elements(combo)
|
||||
}
|
||||
|
||||
// list-like methods
|
||||
|
@ -484,25 +477,24 @@ open class Elements: NSCopying
|
|||
Get the first matched element.
|
||||
@return The first matched element, or <code>null</code> if contents is empty.
|
||||
*/
|
||||
open func first()->Element? {
|
||||
return isEmpty() ? nil : get(0);
|
||||
open func first() -> Element? {
|
||||
return isEmpty() ? nil : get(0)
|
||||
}
|
||||
|
||||
open func isEmpty()->Bool{
|
||||
open func isEmpty() -> Bool {
|
||||
return array().count == 0
|
||||
}
|
||||
|
||||
open func size()->Int{
|
||||
open func size() -> Int {
|
||||
return array().count
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Get the last matched element.
|
||||
@return The last matched element, or <code>null</code> if contents is empty.
|
||||
*/
|
||||
open func last()->Element? {
|
||||
return isEmpty() ? nil : get(size() - 1);
|
||||
open func last() -> Element? {
|
||||
return isEmpty() ? nil : get(size() - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -513,10 +505,10 @@ open class Elements: NSCopying
|
|||
@discardableResult
|
||||
open func traverse(_ nodeVisitor: NodeVisitor)throws->Elements {
|
||||
let traversor: NodeTraversor = NodeTraversor(nodeVisitor)
|
||||
for el:Element in this {
|
||||
try traversor.traverse(el);
|
||||
for el: Element in this {
|
||||
try traversor.traverse(el)
|
||||
}
|
||||
return self;
|
||||
return self
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -525,15 +517,13 @@ open class Elements: NSCopying
|
|||
* no forms.
|
||||
*/
|
||||
open func forms()->Array<FormElement> {
|
||||
var forms: Array<FormElement> = Array<FormElement>();
|
||||
for el:Element in this
|
||||
{
|
||||
if let el = el as? FormElement
|
||||
{
|
||||
var forms: Array<FormElement> = Array<FormElement>()
|
||||
for el: Element in this {
|
||||
if let el = el as? FormElement {
|
||||
forms.append(el)
|
||||
}
|
||||
}
|
||||
return forms;
|
||||
return forms
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -550,18 +540,16 @@ open class Elements: NSCopying
|
|||
this.insert(element, at: index)
|
||||
}
|
||||
|
||||
open func get(_ i :Int)->Element{
|
||||
open func get(_ i: Int) -> Element {
|
||||
return this[i]
|
||||
}
|
||||
|
||||
open func array()->Array<Element>{
|
||||
open func array()->Array<Element> {
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension Elements: Equatable
|
||||
{
|
||||
extension Elements: Equatable {
|
||||
/// Returns a Boolean value indicating whether two values are equal.
|
||||
///
|
||||
/// Equality is the inverse of inequality. For any values `a` and `b`,
|
||||
|
@ -570,9 +558,7 @@ extension Elements: Equatable
|
|||
/// - Parameters:
|
||||
/// - lhs: A value to compare.
|
||||
/// - rhs: Another value to compare.
|
||||
public static func ==(lhs: Elements, rhs: Elements) -> Bool
|
||||
{
|
||||
public static func ==(lhs: Elements, rhs: Elements) -> Bool {
|
||||
return lhs.this == rhs.this
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -22,43 +22,38 @@ public class Evaluator {
|
|||
* @return Returns <tt>true</tt> if the requirements are met or
|
||||
* <tt>false</tt> otherwise
|
||||
*/
|
||||
open func matches(_ root: Element, _ element: Element)throws->Bool{
|
||||
open func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||
preconditionFailure("self method must be overridden")
|
||||
}
|
||||
|
||||
open func toString()->String
|
||||
{
|
||||
open func toString() -> String {
|
||||
preconditionFailure("self method must be overridden")
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evaluator for tag name
|
||||
*/
|
||||
public class Tag : Evaluator {
|
||||
private let tagName : String
|
||||
public class Tag: Evaluator {
|
||||
private let tagName: String
|
||||
|
||||
public init(_ tagName: String) {
|
||||
self.tagName = tagName
|
||||
}
|
||||
|
||||
|
||||
open override func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||
return (element.tagName().equalsIgnoreCase(string: tagName))
|
||||
}
|
||||
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return String(tagName)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evaluator for tag name that ends with
|
||||
*/
|
||||
public final class TagEndsWith : Evaluator {
|
||||
private let tagName : String
|
||||
public final class TagEndsWith: Evaluator {
|
||||
private let tagName: String
|
||||
|
||||
public init(_ tagName: String) {
|
||||
self.tagName = tagName
|
||||
|
@ -68,7 +63,7 @@ public class Evaluator {
|
|||
return (element.tagName().hasSuffix(tagName))
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return String(tagName)
|
||||
}
|
||||
}
|
||||
|
@ -76,8 +71,8 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for element id
|
||||
*/
|
||||
public final class Id : Evaluator {
|
||||
private let id : String
|
||||
public final class Id: Evaluator {
|
||||
private let id: String
|
||||
|
||||
public init(_ id: String) {
|
||||
self.id = id
|
||||
|
@ -87,7 +82,7 @@ public class Evaluator {
|
|||
return (id == element.id())
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return "#\(id)"
|
||||
}
|
||||
|
||||
|
@ -96,18 +91,18 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for element class
|
||||
*/
|
||||
public final class Class : Evaluator {
|
||||
private let className : String
|
||||
public final class Class: Evaluator {
|
||||
private let className: String
|
||||
|
||||
public init(_ className: String) {
|
||||
self.className = className
|
||||
}
|
||||
|
||||
open override func matches(_ root: Element, _ element: Element)->Bool {
|
||||
open override func matches(_ root: Element, _ element: Element) -> Bool {
|
||||
return (element.hasClass(className))
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return ".\(className)"
|
||||
}
|
||||
|
||||
|
@ -116,8 +111,8 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for attribute name matching
|
||||
*/
|
||||
public final class Attribute : Evaluator {
|
||||
private let key : String
|
||||
public final class Attribute: Evaluator {
|
||||
private let key: String
|
||||
|
||||
public init(_ key: String) {
|
||||
self.key = key
|
||||
|
@ -127,7 +122,7 @@ public class Evaluator {
|
|||
return element.hasAttr(key)
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return "[\(key)]"
|
||||
}
|
||||
|
||||
|
@ -136,8 +131,8 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for attribute name prefix matching
|
||||
*/
|
||||
public final class AttributeStarting : Evaluator {
|
||||
private let keyPrefix : String
|
||||
public final class AttributeStarting: Evaluator {
|
||||
private let keyPrefix: String
|
||||
|
||||
public init(_ keyPrefix: String)throws {
|
||||
try Validate.notEmpty(string: keyPrefix)
|
||||
|
@ -145,9 +140,9 @@ public class Evaluator {
|
|||
}
|
||||
|
||||
open override func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||
if let values = element.getAttributes(){
|
||||
if let values = element.getAttributes() {
|
||||
for attribute in values.iterator() {
|
||||
if (attribute.getKey().lowercased().hasPrefix(keyPrefix)){
|
||||
if (attribute.getKey().lowercased().hasPrefix(keyPrefix)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -155,7 +150,7 @@ public class Evaluator {
|
|||
return false
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return "[^\(keyPrefix)]"
|
||||
}
|
||||
|
||||
|
@ -164,22 +159,20 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for attribute name/value matching
|
||||
*/
|
||||
public final class AttributeWithValue : AttributeKeyPair {
|
||||
public final class AttributeWithValue: AttributeKeyPair {
|
||||
public override init(_ key: String, _ value: String)throws {
|
||||
try super.init(key, value)
|
||||
}
|
||||
|
||||
|
||||
open override func matches(_ root: Element, _ element: Element)throws->Bool
|
||||
{
|
||||
if(element.hasAttr(key)){
|
||||
open override func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||
if(element.hasAttr(key)) {
|
||||
let s = try element.attr(key)
|
||||
return value.equalsIgnoreCase(string: s.trim())
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return "[\(key)=\(value)]"
|
||||
}
|
||||
|
||||
|
@ -188,7 +181,7 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for attribute name != value matching
|
||||
*/
|
||||
public final class AttributeWithValueNot : AttributeKeyPair {
|
||||
public final class AttributeWithValueNot: AttributeKeyPair {
|
||||
public override init(_ key: String, _ value: String)throws {
|
||||
try super.init(key, value)
|
||||
}
|
||||
|
@ -198,7 +191,7 @@ public class Evaluator {
|
|||
return !value.equalsIgnoreCase(string: s)
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return "[\(key)!=\(value)]"
|
||||
}
|
||||
|
||||
|
@ -207,19 +200,19 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for attribute name/value matching (value prefix)
|
||||
*/
|
||||
public final class AttributeWithValueStarting : AttributeKeyPair {
|
||||
public final class AttributeWithValueStarting: AttributeKeyPair {
|
||||
public override init(_ key: String, _ value: String)throws {
|
||||
try super.init(key, value)
|
||||
}
|
||||
|
||||
open override func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||
if(element.hasAttr(key)){
|
||||
if(element.hasAttr(key)) {
|
||||
return try element.attr(key).lowercased().hasPrefix(value) // value is lower case already
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return "[\(key)^=\(value)]"
|
||||
}
|
||||
|
||||
|
@ -228,19 +221,19 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for attribute name/value matching (value ending)
|
||||
*/
|
||||
public final class AttributeWithValueEnding : AttributeKeyPair {
|
||||
public final class AttributeWithValueEnding: AttributeKeyPair {
|
||||
public override init(_ key: String, _ value: String)throws {
|
||||
try super.init(key, value)
|
||||
}
|
||||
|
||||
open override func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||
if(element.hasAttr(key)){
|
||||
if(element.hasAttr(key)) {
|
||||
return try element.attr(key).lowercased().hasSuffix(value) // value is lower case
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return "[\(key)$=\(value)]"
|
||||
}
|
||||
|
||||
|
@ -249,19 +242,19 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for attribute name/value matching (value containing)
|
||||
*/
|
||||
public final class AttributeWithValueContaining : AttributeKeyPair {
|
||||
public final class AttributeWithValueContaining: AttributeKeyPair {
|
||||
public override init(_ key: String, _ value: String)throws {
|
||||
try super.init(key, value)
|
||||
}
|
||||
|
||||
open override func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||
if(element.hasAttr(key)){
|
||||
if(element.hasAttr(key)) {
|
||||
return try element.attr(key).lowercased().contains(value) // value is lower case
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return "[\(key)*=\(value)]"
|
||||
}
|
||||
|
||||
|
@ -270,9 +263,9 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for attribute name/value matching (value regex matching)
|
||||
*/
|
||||
public final class AttributeWithValueMatching : Evaluator {
|
||||
let key : String
|
||||
let pattern : Pattern
|
||||
public final class AttributeWithValueMatching: Evaluator {
|
||||
let key: String
|
||||
let pattern: Pattern
|
||||
|
||||
public init(_ key: String, _ pattern: Pattern) {
|
||||
self.key = key.trim().lowercased()
|
||||
|
@ -281,14 +274,14 @@ public class Evaluator {
|
|||
}
|
||||
|
||||
open override func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||
if(element.hasAttr(key)){
|
||||
if(element.hasAttr(key)) {
|
||||
let s = try element.attr(key)
|
||||
return pattern.matcher(in:s).find()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return "[\(key)~=\(pattern.toString())]"
|
||||
}
|
||||
|
||||
|
@ -297,9 +290,9 @@ public class Evaluator {
|
|||
/**
|
||||
* Abstract evaluator for attribute name/value matching
|
||||
*/
|
||||
public class AttributeKeyPair : Evaluator {
|
||||
let key : String
|
||||
var value : String
|
||||
public class AttributeKeyPair: Evaluator {
|
||||
let key: String
|
||||
var value: String
|
||||
|
||||
public init(_ key: String, _ value2: String)throws {
|
||||
var value2 = value2
|
||||
|
@ -307,14 +300,13 @@ public class Evaluator {
|
|||
try Validate.notEmpty(string: value2)
|
||||
|
||||
self.key = key.trim().lowercased()
|
||||
if (value2.startsWith("\"") && value2.hasSuffix("\"") || value2.startsWith("'") && value2.hasSuffix("'"))
|
||||
{
|
||||
if (value2.startsWith("\"") && value2.hasSuffix("\"") || value2.startsWith("'") && value2.hasSuffix("'")) {
|
||||
value2 = value2.substring(1, value2.characters.count-2)
|
||||
}
|
||||
self.value = value2.trim().lowercased()
|
||||
}
|
||||
|
||||
open override func matches(_ root: Element, _ element: Element)throws->Bool{
|
||||
open override func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||
preconditionFailure("self method must be overridden")
|
||||
}
|
||||
}
|
||||
|
@ -322,13 +314,13 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for any / all element matching
|
||||
*/
|
||||
public final class AllElements : Evaluator {
|
||||
public final class AllElements: Evaluator {
|
||||
|
||||
open override func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return "*"
|
||||
}
|
||||
}
|
||||
|
@ -336,7 +328,7 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for matching by sibling index number (e {@literal <} idx)
|
||||
*/
|
||||
public final class IndexLessThan : IndexEvaluator {
|
||||
public final class IndexLessThan: IndexEvaluator {
|
||||
public override init(_ index: Int) {
|
||||
super.init(index)
|
||||
}
|
||||
|
@ -345,7 +337,7 @@ public class Evaluator {
|
|||
return try element.elementSiblingIndex() < index
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return ":lt(\(index))"
|
||||
}
|
||||
|
||||
|
@ -354,7 +346,7 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for matching by sibling index number (e {@literal >} idx)
|
||||
*/
|
||||
public final class IndexGreaterThan : IndexEvaluator {
|
||||
public final class IndexGreaterThan: IndexEvaluator {
|
||||
public override init(_ index: Int) {
|
||||
super.init(index)
|
||||
}
|
||||
|
@ -363,7 +355,7 @@ public class Evaluator {
|
|||
return try element.elementSiblingIndex() > index
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return ":gt(\(index))"
|
||||
}
|
||||
|
||||
|
@ -372,7 +364,7 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for matching by sibling index number (e = idx)
|
||||
*/
|
||||
public final class IndexEquals : IndexEvaluator {
|
||||
public final class IndexEquals: IndexEvaluator {
|
||||
public override init(_ index: Int) {
|
||||
super.init(index)
|
||||
}
|
||||
|
@ -381,7 +373,7 @@ public class Evaluator {
|
|||
return try element.elementSiblingIndex() == index
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return ":eq(\(index))"
|
||||
}
|
||||
|
||||
|
@ -390,43 +382,42 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for matching the last sibling (css :last-child)
|
||||
*/
|
||||
public final class IsLastChild : Evaluator {
|
||||
public final class IsLastChild: Evaluator {
|
||||
open override func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||
|
||||
if let p = element.parent(){
|
||||
if let p = element.parent() {
|
||||
let i = try element.elementSiblingIndex()
|
||||
return !((p as? Document) != nil) && i == (p.getChildNodes().count - 1)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return ":last-child"
|
||||
}
|
||||
}
|
||||
|
||||
public final class IsFirstOfType : IsNthOfType {
|
||||
public final class IsFirstOfType: IsNthOfType {
|
||||
public init() {
|
||||
super.init(0,1)
|
||||
super.init(0, 1)
|
||||
}
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return ":first-of-type"
|
||||
}
|
||||
}
|
||||
|
||||
public final class IsLastOfType : IsNthLastOfType {
|
||||
public final class IsLastOfType: IsNthLastOfType {
|
||||
public init() {
|
||||
super.init(0,1)
|
||||
super.init(0, 1)
|
||||
}
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return ":last-of-type"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class CssNthEvaluator : Evaluator {
|
||||
public let a : Int
|
||||
public let b : Int
|
||||
public class CssNthEvaluator: Evaluator {
|
||||
public let a: Int
|
||||
public let b: Int
|
||||
|
||||
public init(_ a: Int, _ b: Int) {
|
||||
self.a = a
|
||||
|
@ -447,42 +438,40 @@ public class Evaluator {
|
|||
return (pos-b)*a >= 0 && (pos-b)%a==0
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
if (a == 0){
|
||||
open override func toString() -> String {
|
||||
if (a == 0) {
|
||||
return ":\(getPseudoClass)(\(b))"
|
||||
}
|
||||
if (b == 0){
|
||||
if (b == 0) {
|
||||
return ":\(getPseudoClass)(\(a))"
|
||||
}
|
||||
return ":\(getPseudoClass)(\(a)\(b))"
|
||||
}
|
||||
|
||||
open func getPseudoClass()->String{
|
||||
open func getPseudoClass() -> String {
|
||||
preconditionFailure("self method must be overridden")
|
||||
}
|
||||
open func calculatePosition(_ root: Element, _ element: Element)throws->Int{
|
||||
open func calculatePosition(_ root: Element, _ element: Element)throws->Int {
|
||||
preconditionFailure("self method must be overridden")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* css-compatible Evaluator for :eq (css :nth-child)
|
||||
*
|
||||
* @see IndexEquals
|
||||
*/
|
||||
public final class IsNthChild : CssNthEvaluator {
|
||||
public final class IsNthChild: CssNthEvaluator {
|
||||
|
||||
public override init(_ a: Int, _ b: Int) {
|
||||
super.init(a,b)
|
||||
super.init(a, b)
|
||||
}
|
||||
|
||||
open override func calculatePosition(_ root: Element, _ element: Element)throws->Int {
|
||||
return try element.elementSiblingIndex()+1
|
||||
}
|
||||
|
||||
|
||||
open override func getPseudoClass()->String {
|
||||
open override func getPseudoClass() -> String {
|
||||
return "nth-child"
|
||||
}
|
||||
}
|
||||
|
@ -492,22 +481,21 @@ public class Evaluator {
|
|||
*
|
||||
* @see IndexEquals
|
||||
*/
|
||||
public final class IsNthLastChild : CssNthEvaluator {
|
||||
public final class IsNthLastChild: CssNthEvaluator {
|
||||
public override init(_ a: Int, _ b: Int) {
|
||||
super.init(a,b)
|
||||
super.init(a, b)
|
||||
}
|
||||
|
||||
open override func calculatePosition(_ root: Element, _ element: Element)throws->Int
|
||||
{
|
||||
open override func calculatePosition(_ root: Element, _ element: Element)throws->Int {
|
||||
var i = 0
|
||||
|
||||
if let l = element.parent(){
|
||||
if let l = element.parent() {
|
||||
i = l.children().array().count
|
||||
}
|
||||
return i - (try element.elementSiblingIndex())
|
||||
}
|
||||
|
||||
open override func getPseudoClass()->String {
|
||||
open override func getPseudoClass() -> String {
|
||||
return "nth-last-child"
|
||||
}
|
||||
}
|
||||
|
@ -516,32 +504,30 @@ public class Evaluator {
|
|||
* css pseudo class nth-of-type
|
||||
*
|
||||
*/
|
||||
public class IsNthOfType : CssNthEvaluator {
|
||||
public class IsNthOfType: CssNthEvaluator {
|
||||
public override init(_ a: Int, _ b: Int) {
|
||||
super.init(a,b)
|
||||
super.init(a, b)
|
||||
}
|
||||
|
||||
open override func calculatePosition(_ root: Element, _ element: Element)->Int {
|
||||
open override func calculatePosition(_ root: Element, _ element: Element) -> Int {
|
||||
var pos = 0
|
||||
let family: Elements? = element.parent()?.children()
|
||||
if let array = family?.array(){
|
||||
for el in array
|
||||
{
|
||||
if let array = family?.array() {
|
||||
for el in array {
|
||||
if (el.tag() == element.tag()) {pos+=1}
|
||||
if (el === element) {break}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return pos
|
||||
}
|
||||
|
||||
open override func getPseudoClass()->String {
|
||||
open override func getPseudoClass() -> String {
|
||||
return "nth-of-type"
|
||||
}
|
||||
}
|
||||
|
||||
public class IsNthLastOfType : CssNthEvaluator {
|
||||
public class IsNthLastOfType: CssNthEvaluator {
|
||||
|
||||
public override init(_ a: Int, _ b: Int) {
|
||||
super.init(a, b)
|
||||
|
@ -549,10 +535,10 @@ public class Evaluator {
|
|||
|
||||
open override func calculatePosition(_ root: Element, _ element: Element)throws->Int {
|
||||
var pos = 0
|
||||
if let family = element.parent()?.children(){
|
||||
if let family = element.parent()?.children() {
|
||||
let x = try element.elementSiblingIndex()
|
||||
for i in x..<family.array().count {
|
||||
if (family.get(i).tag() == element.tag()){
|
||||
if (family.get(i).tag() == element.tag()) {
|
||||
pos+=1
|
||||
}
|
||||
}
|
||||
|
@ -561,7 +547,7 @@ public class Evaluator {
|
|||
return pos
|
||||
}
|
||||
|
||||
open override func getPseudoClass()->String {
|
||||
open override func getPseudoClass() -> String {
|
||||
return "nth-last-of-type"
|
||||
}
|
||||
}
|
||||
|
@ -569,16 +555,16 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for matching the first sibling (css :first-child)
|
||||
*/
|
||||
public final class IsFirstChild : Evaluator {
|
||||
public final class IsFirstChild: Evaluator {
|
||||
open override func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||
let p = element.parent()
|
||||
if(p != nil && !(((p as? Document) != nil))){
|
||||
if(p != nil && !(((p as? Document) != nil))) {
|
||||
return (try element.elementSiblingIndex()) == 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return ":first-child"
|
||||
}
|
||||
}
|
||||
|
@ -588,54 +574,53 @@ public class Evaluator {
|
|||
* @see <a href="http://www.w3.org/TR/selectors/#root-pseudo">:root selector</a>
|
||||
*
|
||||
*/
|
||||
public final class IsRoot : Evaluator {
|
||||
public final class IsRoot: Evaluator {
|
||||
open override func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||
let r: Element = ((root as? Document) != nil) ? root.child(0) : root
|
||||
return element === r
|
||||
}
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return ":root"
|
||||
}
|
||||
}
|
||||
|
||||
public final class IsOnlyChild : Evaluator {
|
||||
public final class IsOnlyChild: Evaluator {
|
||||
open override func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||
let p = element.parent()
|
||||
return p != nil && !((p as? Document) != nil) && element.siblingElements().array().count == 0
|
||||
}
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return ":only-child"
|
||||
}
|
||||
}
|
||||
|
||||
public final class IsOnlyOfType : Evaluator {
|
||||
public final class IsOnlyOfType: Evaluator {
|
||||
open override func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||
let p = element.parent()
|
||||
if (p == nil || (p as? Document) != nil) {return false}
|
||||
|
||||
var pos = 0
|
||||
if let family = p?.children().array(){
|
||||
if let family = p?.children().array() {
|
||||
for el in family {
|
||||
if (el.tag() == element.tag()) {pos+=1}
|
||||
}
|
||||
}
|
||||
return pos == 1
|
||||
}
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return ":only-of-type"
|
||||
}
|
||||
}
|
||||
|
||||
public final class IsEmpty : Evaluator {
|
||||
public final class IsEmpty: Evaluator {
|
||||
open override func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||
let family: Array<Node> = element.getChildNodes()
|
||||
for n in family
|
||||
{
|
||||
for n in family {
|
||||
if (!((n as? Comment) != nil || (n as? XmlDeclaration) != nil || (n as? DocumentType) != nil)) {return false}
|
||||
}
|
||||
return true
|
||||
}
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return ":empty"
|
||||
}
|
||||
}
|
||||
|
@ -645,7 +630,7 @@ public class Evaluator {
|
|||
*
|
||||
* @author ant
|
||||
*/
|
||||
public class IndexEvaluator : Evaluator {
|
||||
public class IndexEvaluator: Evaluator {
|
||||
let index: Int
|
||||
|
||||
public init(_ index: Int) {
|
||||
|
@ -656,7 +641,7 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for matching Element (and its descendants) text
|
||||
*/
|
||||
public final class ContainsText : Evaluator {
|
||||
public final class ContainsText: Evaluator {
|
||||
private let searchText: String
|
||||
|
||||
public init(_ searchText: String) {
|
||||
|
@ -667,7 +652,7 @@ public class Evaluator {
|
|||
return (try element.text().lowercased().contains(searchText))
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return ":contains(\(searchText)"
|
||||
}
|
||||
}
|
||||
|
@ -675,8 +660,8 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for matching Element's own text
|
||||
*/
|
||||
public final class ContainsOwnText : Evaluator {
|
||||
private let searchText : String
|
||||
public final class ContainsOwnText: Evaluator {
|
||||
private let searchText: String
|
||||
|
||||
public init(_ searchText: String) {
|
||||
self.searchText = searchText.lowercased()
|
||||
|
@ -686,7 +671,7 @@ public class Evaluator {
|
|||
return (element.ownText().lowercased().contains(searchText))
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return ":containsOwn(\(searchText)"
|
||||
}
|
||||
}
|
||||
|
@ -694,7 +679,7 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for matching Element (and its descendants) text with regex
|
||||
*/
|
||||
public final class Matches : Evaluator {
|
||||
public final class Matches: Evaluator {
|
||||
private let pattern: Pattern
|
||||
|
||||
public init(_ pattern: Pattern) {
|
||||
|
@ -706,7 +691,7 @@ public class Evaluator {
|
|||
return m.find()
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return ":matches(\(pattern)"
|
||||
}
|
||||
}
|
||||
|
@ -714,7 +699,7 @@ public class Evaluator {
|
|||
/**
|
||||
* Evaluator for matching Element's own text with regex
|
||||
*/
|
||||
public final class MatchesOwn : Evaluator {
|
||||
public final class MatchesOwn: Evaluator {
|
||||
private let pattern: Pattern
|
||||
|
||||
public init(_ pattern: Pattern) {
|
||||
|
@ -726,7 +711,7 @@ public class Evaluator {
|
|||
return m.find()
|
||||
}
|
||||
|
||||
open override func toString()->String {
|
||||
open override func toString() -> String {
|
||||
return ":matchesOwn(\(pattern.toString())"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,6 @@ public enum ExceptionType {
|
|||
case SelectorParseException
|
||||
}
|
||||
|
||||
public enum Exception : Error {
|
||||
case Error(type:ExceptionType ,Message: String)
|
||||
public enum Exception: Error {
|
||||
case Error(type:ExceptionType, Message: String)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import Foundation
|
|||
* A HTML Form Element provides ready access to the form fields/controls that are associated with it. It also allows a
|
||||
* form to easily be submitted.
|
||||
*/
|
||||
public class FormElement : Element {
|
||||
public class FormElement: Element {
|
||||
private let _elements: Elements = Elements()
|
||||
|
||||
/**
|
||||
|
@ -30,7 +30,7 @@ public class FormElement : Element {
|
|||
* Get the list of form control elements associated with this form.
|
||||
* @return form controls associated with this element.
|
||||
*/
|
||||
public func elements()->Elements {
|
||||
public func elements() -> Elements {
|
||||
return _elements
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ public class FormElement : Element {
|
|||
* @return this form element, for chaining
|
||||
*/
|
||||
@discardableResult
|
||||
public func addElement(_ element: Element)->FormElement {
|
||||
public func addElement(_ element: Element) -> FormElement {
|
||||
_elements.add(element)
|
||||
return self
|
||||
}
|
||||
|
@ -106,23 +106,20 @@ public class FormElement : Element {
|
|||
// return data;
|
||||
// }
|
||||
|
||||
public override func copy(with zone: NSZone? = nil) -> Any
|
||||
{
|
||||
let clone = FormElement(_tag,baseUri!,attributes!)
|
||||
public override func copy(with zone: NSZone? = nil) -> Any {
|
||||
let clone = FormElement(_tag, baseUri!, attributes!)
|
||||
return copy(clone: clone)
|
||||
}
|
||||
|
||||
public override func copy(parent: Node?)->Node
|
||||
{
|
||||
let clone = FormElement(_tag,baseUri!,attributes!)
|
||||
return copy(clone: clone,parent: parent)
|
||||
public override func copy(parent: Node?) -> Node {
|
||||
let clone = FormElement(_tag, baseUri!, attributes!)
|
||||
return copy(clone: clone, parent: parent)
|
||||
}
|
||||
public override func copy(clone: Node, parent: Node?)->Node
|
||||
{
|
||||
public override func copy(clone: Node, parent: Node?) -> Node {
|
||||
let clone = clone as! FormElement
|
||||
for att in _elements.array(){
|
||||
for att in _elements.array() {
|
||||
clone._elements.add(att)
|
||||
}
|
||||
return super.copy(clone: clone,parent: parent)
|
||||
return super.copy(clone: clone, parent: parent)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,13 +43,11 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
private var fosterInserts: Bool = false // if next inserts should be fostered
|
||||
private var fragmentParsing: Bool = false // if parsing a fragment of html
|
||||
|
||||
|
||||
public override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
|
||||
public override func defaultSettings()->ParseSettings {
|
||||
public override func defaultSettings() -> ParseSettings {
|
||||
return ParseSettings.htmlDefault
|
||||
}
|
||||
|
||||
|
@ -74,17 +72,17 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
|
||||
// initialise the tokeniser state:
|
||||
let contextTag: String = context.tagName()
|
||||
if (StringUtil.inString(contextTag, haystack: "title", "textarea")){
|
||||
if (StringUtil.inString(contextTag, haystack: "title", "textarea")) {
|
||||
tokeniser.transition(TokeniserState.Rcdata)
|
||||
}else if (StringUtil.inString(contextTag, haystack: "iframe", "noembed", "noframes", "style", "xmp")){
|
||||
} else if (StringUtil.inString(contextTag, haystack: "iframe", "noembed", "noframes", "style", "xmp")) {
|
||||
tokeniser.transition(TokeniserState.Rawtext)
|
||||
}else if (contextTag=="script"){
|
||||
} else if (contextTag=="script") {
|
||||
tokeniser.transition(TokeniserState.ScriptData)
|
||||
}else if (contextTag==("noscript")){
|
||||
} else if (contextTag==("noscript")) {
|
||||
tokeniser.transition(TokeniserState.Data) // if scripting enabled, rawtext
|
||||
}else if (contextTag=="plaintext"){
|
||||
} else if (contextTag=="plaintext") {
|
||||
tokeniser.transition(TokeniserState.Data)
|
||||
}else{
|
||||
} else {
|
||||
tokeniser.transition(TokeniserState.Data) // default
|
||||
}
|
||||
|
||||
|
@ -98,7 +96,7 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
// with form correctly
|
||||
let contextChain: Elements = context.parents()
|
||||
contextChain.add(0, context)
|
||||
for parent:Element in contextChain.array() {
|
||||
for parent: Element in contextChain.array() {
|
||||
if let x = (parent as? FormElement) {
|
||||
formElement = x
|
||||
break
|
||||
|
@ -107,15 +105,15 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
}
|
||||
|
||||
try runParser()
|
||||
if (context != nil && root != nil){
|
||||
if (context != nil && root != nil) {
|
||||
return root!.getChildNodes()
|
||||
}else{
|
||||
} else {
|
||||
return doc.getChildNodes()
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public override func process(_ token: Token)throws->Bool{
|
||||
public override func process(_ token: Token)throws->Bool {
|
||||
currentToken = token
|
||||
return try self._state.process(token, self)
|
||||
}
|
||||
|
@ -130,7 +128,7 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
self._state = state
|
||||
}
|
||||
|
||||
func state()->HtmlTreeBuilderState {
|
||||
func state() -> HtmlTreeBuilderState {
|
||||
return _state
|
||||
}
|
||||
|
||||
|
@ -138,7 +136,7 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
_originalState = _state
|
||||
}
|
||||
|
||||
func originalState()->HtmlTreeBuilderState {
|
||||
func originalState() -> HtmlTreeBuilderState {
|
||||
return _originalState
|
||||
}
|
||||
|
||||
|
@ -146,20 +144,20 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
self._framesetOk = framesetOk
|
||||
}
|
||||
|
||||
func framesetOk()->Bool {
|
||||
func framesetOk() -> Bool {
|
||||
return _framesetOk
|
||||
}
|
||||
|
||||
func getDocument()->Document {
|
||||
func getDocument() -> Document {
|
||||
return doc
|
||||
}
|
||||
|
||||
func getBaseUri()->String {
|
||||
func getBaseUri() -> String {
|
||||
return baseUri
|
||||
}
|
||||
|
||||
func maybeSetBaseUri(_ base: Element)throws {
|
||||
if (baseUriSetFromDoc){ // only listen to the first <base href> in parse
|
||||
if (baseUriSetFromDoc) { // only listen to the first <base href> in parse
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -171,12 +169,12 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
func isFragmentParsing()->Bool {
|
||||
func isFragmentParsing() -> Bool {
|
||||
return fragmentParsing
|
||||
}
|
||||
|
||||
func error(_ state: HtmlTreeBuilderState) {
|
||||
if (errors.canAddError() && currentToken != nil){
|
||||
if (errors.canAddError() && currentToken != nil) {
|
||||
errors.add(ParseError(reader.getPos(), "Unexpected token [\(currentToken!.tokenType())] when in state [\(state.rawValue)]"))
|
||||
}
|
||||
}
|
||||
|
@ -235,7 +233,7 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
let el: FormElement = FormElement(tag, baseUri, startTag._attributes)
|
||||
setFormElement(el)
|
||||
try insertNode(el)
|
||||
if (onStack){
|
||||
if (onStack) {
|
||||
stack.append(el)
|
||||
}
|
||||
return el
|
||||
|
@ -250,10 +248,10 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
var node: Node
|
||||
// characters in script and style go in as datanodes, not text nodes
|
||||
let tagName: String? = currentElement()?.tagName()
|
||||
if (tagName=="script" || tagName=="style"){
|
||||
if (tagName=="script" || tagName=="style") {
|
||||
try Validate.notNull(obj: characterToken.getData())
|
||||
node = DataNode(characterToken.getData()!, baseUri)
|
||||
}else{
|
||||
} else {
|
||||
try Validate.notNull(obj: characterToken.getData())
|
||||
node = TextNode(characterToken.getData()!, baseUri)
|
||||
}
|
||||
|
@ -262,18 +260,18 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
|
||||
private func insertNode(_ node: Node)throws {
|
||||
// if the stack hasn't been set up yet, elements (doctype, comments) go into the doc
|
||||
if (stack.count == 0){
|
||||
if (stack.count == 0) {
|
||||
try doc.appendChild(node)
|
||||
}else if (isFosterInserts()){
|
||||
} else if (isFosterInserts()) {
|
||||
try insertInFosterParent(node)
|
||||
}else{
|
||||
} else {
|
||||
try currentElement()?.appendChild(node)
|
||||
}
|
||||
|
||||
// connect form controls to their form element
|
||||
if let n = (node as? Element) {
|
||||
if(n.tag().isFormListed()){
|
||||
if ( formElement != nil){
|
||||
if(n.tag().isFormListed()) {
|
||||
if ( formElement != nil) {
|
||||
formElement!.addElement(n)
|
||||
}
|
||||
}
|
||||
|
@ -281,7 +279,7 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
}
|
||||
|
||||
@discardableResult
|
||||
func pop()->Element {
|
||||
func pop() -> Element {
|
||||
let size: Int = stack.count
|
||||
return stack.remove(at: size-1)
|
||||
}
|
||||
|
@ -295,11 +293,11 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
}
|
||||
|
||||
@discardableResult
|
||||
func onStack(_ el: Element)->Bool {
|
||||
func onStack(_ el: Element) -> Bool {
|
||||
return isElementInQueue(stack, el)
|
||||
}
|
||||
|
||||
private func isElementInQueue(_ queue: Array<Element?>, _ element: Element?)->Bool {
|
||||
private func isElementInQueue(_ queue: Array<Element?>, _ element: Element?) -> Bool {
|
||||
for pos in (0..<queue.count).reversed() {
|
||||
let next: Element? = queue[pos]
|
||||
if (next == element) {
|
||||
|
@ -309,7 +307,7 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
return false
|
||||
}
|
||||
|
||||
func getFromStack(_ elName: String)->Element? {
|
||||
func getFromStack(_ elName: String) -> Element? {
|
||||
for pos in (0..<stack.count).reversed() {
|
||||
let next: Element = stack[pos]
|
||||
if next.nodeName() == elName {
|
||||
|
@ -320,7 +318,7 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
}
|
||||
|
||||
@discardableResult
|
||||
func removeFromStack(_ el: Element)->Bool {
|
||||
func removeFromStack(_ el: Element) -> Bool {
|
||||
for pos in (0..<stack.count).reversed() {
|
||||
let next: Element = stack[pos]
|
||||
if (next == el) {
|
||||
|
@ -335,7 +333,7 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
for pos in (0..<stack.count).reversed() {
|
||||
let next: Element = stack[pos]
|
||||
stack.remove(at: pos)
|
||||
if (next.nodeName() == elName){
|
||||
if (next.nodeName() == elName) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -348,7 +346,7 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
for pos in (0..<stack.count).reversed() {
|
||||
let next: Element = stack[pos]
|
||||
stack.remove(at: pos)
|
||||
if (StringUtil.inString(next.nodeName(),elNames)){
|
||||
if (StringUtil.inString(next.nodeName(), elNames)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -383,15 +381,15 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
private func clearStackToContext(_ nodeNames: [String]) {
|
||||
for pos in (0..<stack.count).reversed() {
|
||||
let next: Element = stack[pos]
|
||||
if (StringUtil.inString(next.nodeName(), nodeNames) || next.nodeName()=="html"){
|
||||
if (StringUtil.inString(next.nodeName(), nodeNames) || next.nodeName()=="html") {
|
||||
break
|
||||
}else{
|
||||
} else {
|
||||
stack.remove(at: pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func aboveOnStack(_ el: Element)->Element? {
|
||||
func aboveOnStack(_ el: Element) -> Element? {
|
||||
//assert(onStack(el), "Invalid parameter")
|
||||
onStack(el)
|
||||
for pos in (0..<stack.count).reversed() {
|
||||
|
@ -479,17 +477,17 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
return try inSpecificScope(specificScopeTarget, baseTypes, extraTypes)
|
||||
}
|
||||
|
||||
private func inSpecificScope(_ targetNames: [String?], _ baseTypes: [String] , _ extraTypes: [String]?)throws->Bool {
|
||||
private func inSpecificScope(_ targetNames: [String?], _ baseTypes: [String], _ extraTypes: [String]?)throws->Bool {
|
||||
for pos in (0..<stack.count).reversed() {
|
||||
let el: Element = stack[pos]
|
||||
let elName: String = el.nodeName()
|
||||
if (StringUtil.inString(elName, targetNames)){
|
||||
if (StringUtil.inString(elName, targetNames)) {
|
||||
return true
|
||||
}
|
||||
if (StringUtil.inString(elName, baseTypes)){
|
||||
if (StringUtil.inString(elName, baseTypes)) {
|
||||
return false
|
||||
}
|
||||
if (extraTypes != nil && StringUtil.inString(elName, extraTypes!)){
|
||||
if (extraTypes != nil && StringUtil.inString(elName, extraTypes!)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -527,10 +525,10 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
for pos in (0..<stack.count).reversed() {
|
||||
let el: Element = stack[pos]
|
||||
let elName: String = el.nodeName()
|
||||
if (elName.equals(targetName)){
|
||||
if (elName.equals(targetName)) {
|
||||
return true
|
||||
}
|
||||
if (!StringUtil.inString(elName, HtmlTreeBuilder.TagSearchSelectScope)){ // all elements except
|
||||
if (!StringUtil.inString(elName, HtmlTreeBuilder.TagSearchSelectScope)) { // all elements except
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -542,11 +540,11 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
self.headElement = headElement
|
||||
}
|
||||
|
||||
func getHeadElement()->Element? {
|
||||
func getHeadElement() -> Element? {
|
||||
return headElement
|
||||
}
|
||||
|
||||
func isFosterInserts()->Bool {
|
||||
func isFosterInserts() -> Bool {
|
||||
return fosterInserts
|
||||
}
|
||||
|
||||
|
@ -554,7 +552,7 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
self.fosterInserts = fosterInserts
|
||||
}
|
||||
|
||||
func getFormElement()->FormElement? {
|
||||
func getFormElement() -> FormElement? {
|
||||
return formElement
|
||||
}
|
||||
|
||||
|
@ -587,7 +585,7 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
func generateImpliedEndTags(_ excludeTag: String?) {
|
||||
|
||||
while ((excludeTag != nil && !currentElement()!.nodeName().equals(excludeTag!)) &&
|
||||
StringUtil.inString(currentElement()!.nodeName(), HtmlTreeBuilder.TagSearchEndTags)){
|
||||
StringUtil.inString(currentElement()!.nodeName(), HtmlTreeBuilder.TagSearchEndTags)) {
|
||||
pop()
|
||||
}
|
||||
}
|
||||
|
@ -596,22 +594,22 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
generateImpliedEndTags(nil)
|
||||
}
|
||||
|
||||
func isSpecial(_ el: Element)->Bool {
|
||||
func isSpecial(_ el: Element) -> Bool {
|
||||
// todo: mathml's mi, mo, mn
|
||||
// todo: svg's foreigObject, desc, title
|
||||
let name: String = el.nodeName()
|
||||
return StringUtil.inString(name, HtmlTreeBuilder.TagSearchSpecial)
|
||||
}
|
||||
|
||||
func lastFormattingElement()->Element? {
|
||||
func lastFormattingElement() -> Element? {
|
||||
return formattingElements.count > 0 ? formattingElements[formattingElements.count-1] : nil
|
||||
}
|
||||
|
||||
func removeLastFormattingElement()->Element? {
|
||||
func removeLastFormattingElement() -> Element? {
|
||||
let size: Int = formattingElements.count
|
||||
if (size > 0){
|
||||
if (size > 0) {
|
||||
return formattingElements.remove(at: size-1)
|
||||
}else{
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -621,11 +619,11 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
var numSeen: Int = 0
|
||||
for pos in (0..<formattingElements.count).reversed() {
|
||||
let el: Element? = formattingElements[pos]
|
||||
if (el == nil){ // marker
|
||||
if (el == nil) { // marker
|
||||
break
|
||||
}
|
||||
|
||||
if (isSameFormattingElement(input, el!)){
|
||||
if (isSameFormattingElement(input, el!)) {
|
||||
numSeen += 1
|
||||
}
|
||||
|
||||
|
@ -637,9 +635,9 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
formattingElements.append(input)
|
||||
}
|
||||
|
||||
private func isSameFormattingElement(_ a: Element, _ b: Element)->Bool {
|
||||
private func isSameFormattingElement(_ a: Element, _ b: Element) -> Bool {
|
||||
// same if: same namespace, tag, and attributes. Element.equals only checks tag, might in future check children
|
||||
if(a.attributes == nil){
|
||||
if(a.attributes == nil) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -651,14 +649,13 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
|
||||
func reconstructFormattingElements()throws {
|
||||
let last: Element? = lastFormattingElement()
|
||||
if (last == nil || onStack(last!)){
|
||||
if (last == nil || onStack(last!)) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
var entry:Element? = last
|
||||
let size:Int = formattingElements.count
|
||||
var pos:Int = size - 1
|
||||
var entry: Element? = last
|
||||
let size: Int = formattingElements.count
|
||||
var pos: Int = size - 1
|
||||
var skip: Bool = false
|
||||
while (true) {
|
||||
if (pos == 0) { // step 4. if none before, skip to 8
|
||||
|
@ -696,7 +693,7 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
func clearFormattingElementsToLastMarker() {
|
||||
while (!formattingElements.isEmpty) {
|
||||
let el: Element? = removeLastFormattingElement()
|
||||
if (el == nil){
|
||||
if (el == nil) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -712,27 +709,22 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
func isInActiveFormattingElements(_ el: Element)->Bool {
|
||||
func isInActiveFormattingElements(_ el: Element) -> Bool {
|
||||
return isElementInQueue(formattingElements, el)
|
||||
}
|
||||
|
||||
|
||||
func getActiveFormattingElement(_ nodeName: String)->Element? {
|
||||
func getActiveFormattingElement(_ nodeName: String) -> Element? {
|
||||
for pos in (0..<formattingElements.count).reversed() {
|
||||
let next: Element? = formattingElements[pos]
|
||||
if (next == nil){ // scope marker
|
||||
if (next == nil) { // scope marker
|
||||
break
|
||||
}else if (next!.nodeName().equals(nodeName)){
|
||||
} else if (next!.nodeName().equals(nodeName)) {
|
||||
return next
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
func replaceActiveFormattingElement(_ out: Element, _ input: Element)throws {
|
||||
try formattingElements = replaceInQueue(formattingElements as! Array<Element>, out, input)//todo: testare as! non è bello
|
||||
}
|
||||
|
@ -749,7 +741,7 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
if (lastTable.parent() != nil) {
|
||||
fosterParent = lastTable.parent()!
|
||||
isLastTableParent = true
|
||||
} else{
|
||||
} else {
|
||||
fosterParent = aboveOnStack(lastTable)
|
||||
}
|
||||
} else { // no table == frag
|
||||
|
@ -759,8 +751,7 @@ class HtmlTreeBuilder: TreeBuilder {
|
|||
if (isLastTableParent) {
|
||||
try Validate.notNull(obj: lastTable) // last table cannot be null by this point.
|
||||
try lastTable!.before(input)
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
try fosterParent?.appendChild(input)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,7 @@ protocol HtmlTreeBuilderStateProtocol {
|
|||
func process(_ t: Token, _ tb: HtmlTreeBuilder)throws->Bool
|
||||
}
|
||||
|
||||
enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
||||
{
|
||||
enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol {
|
||||
case Initial
|
||||
case BeforeHtml
|
||||
case BeforeHead
|
||||
|
@ -40,13 +39,11 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
|
||||
private static let nullString: String = "\u{0000}"
|
||||
|
||||
public func equals(_ s: HtmlTreeBuilderState)->Bool
|
||||
{
|
||||
public func equals(_ s: HtmlTreeBuilderState) -> Bool {
|
||||
return self.hashValue == s.hashValue
|
||||
}
|
||||
|
||||
func process(_ t: Token, _ tb: HtmlTreeBuilder)throws->Bool
|
||||
{
|
||||
func process(_ t: Token, _ tb: HtmlTreeBuilder)throws->Bool {
|
||||
switch self {
|
||||
case .Initial:
|
||||
if (HtmlTreeBuilderState.isWhitespace(t)) {
|
||||
|
@ -60,7 +57,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
let doctype: DocumentType = DocumentType(
|
||||
tb.settings.normalizeTag(d.getName()), d.getPublicIdentifier(), d.getSystemIdentifier(), tb.getBaseUri())
|
||||
try tb.getDocument().appendChild(doctype)
|
||||
if (d.isForceQuirks()){
|
||||
if (d.isForceQuirks()) {
|
||||
tb.getDocument().quirksMode(Document.QuirksMode.quirks)
|
||||
}
|
||||
tb.transition(.BeforeHtml)
|
||||
|
@ -147,7 +144,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
} else if (StringUtil.inString(name, haystack: "base", "basefont", "bgsound", "command", "link")) {
|
||||
let el: Element = try tb.insertEmpty(start)
|
||||
// jsoup special: update base the frist time it is seen
|
||||
if (name.equals("base") && el.hasAttr("href")){
|
||||
if (name.equals("base") && el.hasAttr("href")) {
|
||||
try tb.maybeSetBaseUri(el)
|
||||
}
|
||||
} else if (name.equals("meta")) {
|
||||
|
@ -267,15 +264,14 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
}
|
||||
return true
|
||||
case .InBody:
|
||||
func anyOtherEndTag(_ t: Token, _ tb: HtmlTreeBuilder)->Bool
|
||||
{
|
||||
func anyOtherEndTag(_ t: Token, _ tb: HtmlTreeBuilder) -> Bool {
|
||||
let name: String? = t.asEndTag().normalName()
|
||||
let stack: Array<Element> = tb.getStack()
|
||||
for pos in (0..<stack.count).reversed(){
|
||||
for pos in (0..<stack.count).reversed() {
|
||||
let node: Element = stack[pos]
|
||||
if (name != nil && node.nodeName().equals(name!)) {
|
||||
tb.generateImpliedEndTags(name)
|
||||
if (!name!.equals((tb.currentElement()?.nodeName())!)){
|
||||
if (!name!.equals((tb.currentElement()?.nodeName())!)) {
|
||||
tb.error(self)
|
||||
}
|
||||
tb.popStackToClose(name!)
|
||||
|
@ -290,8 +286,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return true
|
||||
}
|
||||
|
||||
switch (t.type)
|
||||
{
|
||||
switch (t.type) {
|
||||
case Token.TokenType.Char:
|
||||
let c: Token.Char = t.asCharacter()
|
||||
if (c.getData() != nil && c.getData()!.equals(HtmlTreeBuilderState.nullString)) {
|
||||
|
@ -315,8 +310,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return false
|
||||
case Token.TokenType.StartTag:
|
||||
let startTag: Token.StartTag = t.asStartTag()
|
||||
if let name: String = startTag.normalName()
|
||||
{
|
||||
if let name: String = startTag.normalName() {
|
||||
if (name.equals("a")) {
|
||||
if (tb.getActiveFormattingElement("a") != nil) {
|
||||
tb.error(self)
|
||||
|
@ -354,7 +348,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
try tb.processEndTag("li")
|
||||
break
|
||||
}
|
||||
if (tb.isSpecial(el) && !StringUtil.inSorted(el.nodeName(), haystack: Constants.InBodyStartLiBreakers)){
|
||||
if (tb.isSpecial(el) && !StringUtil.inSorted(el.nodeName(), haystack: Constants.InBodyStartLiBreakers)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -367,7 +361,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
// merge attributes onto real html
|
||||
let html: Element = tb.getStack()[0]
|
||||
for attribute in startTag.getAttributes().iterator() {
|
||||
if (!html.hasAttr(attribute.getKey())){
|
||||
if (!html.hasAttr(attribute.getKey())) {
|
||||
html.getAttributes()?.put(attribute: attribute)
|
||||
}
|
||||
}
|
||||
|
@ -375,7 +369,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return try tb.process(t, .InHead)
|
||||
} else if (name.equals("body")) {
|
||||
tb.error(self)
|
||||
let stack : Array<Element> = tb.getStack()
|
||||
let stack: Array<Element> = tb.getStack()
|
||||
if (stack.count == 1 || (stack.count > 2 && !stack[1].nodeName().equals("body"))) {
|
||||
// only in fragment case
|
||||
return false // ignore
|
||||
|
@ -383,7 +377,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
tb.framesetOk(false)
|
||||
let body: Element = stack[1]
|
||||
for attribute: Attribute in startTag.getAttributes().iterator() {
|
||||
if (!body.hasAttr(attribute.getKey())){
|
||||
if (!body.hasAttr(attribute.getKey())) {
|
||||
body.getAttributes()?.put(attribute: attribute)
|
||||
}
|
||||
}
|
||||
|
@ -398,11 +392,11 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return false // ignore frameset
|
||||
} else {
|
||||
let second: Element = stack[1]
|
||||
if (second.parent() != nil){
|
||||
if (second.parent() != nil) {
|
||||
try second.remove()
|
||||
}
|
||||
// pop up to html element
|
||||
while (stack.count > 1){
|
||||
while (stack.count > 1) {
|
||||
stack.remove(at: stack.count-1)
|
||||
}
|
||||
try tb.insert(startTag)
|
||||
|
@ -435,14 +429,14 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
try tb.insertForm(startTag, true)
|
||||
} else if (StringUtil.inSorted(name, haystack: Constants.DdDt)) {
|
||||
tb.framesetOk(false)
|
||||
let stack:Array<Element> = tb.getStack()
|
||||
let stack: Array<Element> = tb.getStack()
|
||||
for i in (1..<stack.count).reversed() {
|
||||
let el: Element = stack[i]
|
||||
if (StringUtil.inSorted(el.nodeName(), haystack: Constants.DdDt)) {
|
||||
try tb.processEndTag(el.nodeName())
|
||||
break
|
||||
}
|
||||
if (tb.isSpecial(el) && !StringUtil.inSorted(el.nodeName(), haystack: Constants.InBodyStartLiBreakers)){
|
||||
if (tb.isSpecial(el) && !StringUtil.inSorted(el.nodeName(), haystack: Constants.InBodyStartLiBreakers)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -495,7 +489,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
} else if (name.equals("input")) {
|
||||
try tb.reconstructFormattingElements()
|
||||
let el: Element = try tb.insertEmpty(startTag)
|
||||
if (try !el.attr("type").equalsIgnoreCase(string: "hidden")){
|
||||
if (try !el.attr("type").equalsIgnoreCase(string: "hidden")) {
|
||||
tb.framesetOk(false)
|
||||
}
|
||||
} else if (StringUtil.inSorted(name, haystack: Constants.InBodyStartMedia)) {
|
||||
|
@ -507,22 +501,22 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
try tb.insertEmpty(startTag)
|
||||
tb.framesetOk(false)
|
||||
} else if (name.equals("image")) {
|
||||
if (tb.getFromStack("svg") == nil){
|
||||
if (tb.getFromStack("svg") == nil) {
|
||||
return try tb.process(startTag.name("img")) // change <image> to <img>, unless in svg
|
||||
}else{
|
||||
} else {
|
||||
try tb.insert(startTag)
|
||||
}
|
||||
} else if (name.equals("isindex")) {
|
||||
// how much do we care about the early 90s?
|
||||
tb.error(self)
|
||||
if (tb.getFormElement() != nil){
|
||||
if (tb.getFormElement() != nil) {
|
||||
return false
|
||||
}
|
||||
|
||||
tb.tokeniser.acknowledgeSelfClosingFlag()
|
||||
try tb.processStartTag("form")
|
||||
if (startTag._attributes.hasKey(key: "action")) {
|
||||
if let form: Element = tb.getFormElement(){
|
||||
if let form: Element = tb.getFormElement() {
|
||||
try form.attr("action", startTag._attributes.get(key: "action"))
|
||||
}
|
||||
}
|
||||
|
@ -538,7 +532,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
// input
|
||||
let inputAttribs: Attributes = Attributes()
|
||||
for attr: Attribute in startTag._attributes.iterator() {
|
||||
if (!StringUtil.inSorted(attr.getKey(), haystack: Constants.InBodyStartInputAttribs)){
|
||||
if (!StringUtil.inSorted(attr.getKey(), haystack: Constants.InBodyStartInputAttribs)) {
|
||||
inputAttribs.put(attribute: attr)
|
||||
}
|
||||
}
|
||||
|
@ -573,13 +567,13 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
tb.framesetOk(false)
|
||||
|
||||
let state: HtmlTreeBuilderState = tb.state()
|
||||
if (state.equals(.InTable) || state.equals(.InCaption) || state.equals(.InTableBody) || state.equals(.InRow) || state.equals(.InCell)){
|
||||
if (state.equals(.InTable) || state.equals(.InCaption) || state.equals(.InTableBody) || state.equals(.InRow) || state.equals(.InCell)) {
|
||||
tb.transition(.InSelectInTable)
|
||||
}else{
|
||||
} else {
|
||||
tb.transition(.InSelect)
|
||||
}
|
||||
} else if (StringUtil.inSorted(name, haystack: Constants.InBodyStartOptions)) {
|
||||
if (tb.currentElement() != nil && tb.currentElement()!.nodeName().equals("option")){
|
||||
if (tb.currentElement() != nil && tb.currentElement()!.nodeName().equals("option")) {
|
||||
try tb.processEndTag("option")
|
||||
}
|
||||
try tb.reconstructFormattingElements()
|
||||
|
@ -610,7 +604,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
try tb.reconstructFormattingElements()
|
||||
try tb.insert(startTag)
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
try tb.reconstructFormattingElements()
|
||||
try tb.insert(startTag)
|
||||
}
|
||||
|
@ -618,22 +612,21 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
|
||||
case .EndTag:
|
||||
let endTag: Token.EndTag = t.asEndTag()
|
||||
if let name = endTag.normalName(){
|
||||
if let name = endTag.normalName() {
|
||||
if (StringUtil.inSorted(name, haystack: Constants.InBodyEndAdoptionFormatters)) {
|
||||
// Adoption Agency Algorithm.
|
||||
for i in 0..<8
|
||||
{
|
||||
for i in 0..<8 {
|
||||
let formatEl: Element? = tb.getActiveFormattingElement(name)
|
||||
if (formatEl == nil){
|
||||
if (formatEl == nil) {
|
||||
return anyOtherEndTag(t, tb)
|
||||
}else if (!tb.onStack(formatEl!)) {
|
||||
} else if (!tb.onStack(formatEl!)) {
|
||||
tb.error(self)
|
||||
tb.removeFromActiveFormattingElements(formatEl!)
|
||||
return true
|
||||
} else if (try !tb.inScope(formatEl!.nodeName())) {
|
||||
tb.error(self)
|
||||
return false
|
||||
} else if (tb.currentElement() != formatEl!){
|
||||
} else if (tb.currentElement() != formatEl!) {
|
||||
tb.error(self)
|
||||
}
|
||||
|
||||
|
@ -644,9 +637,8 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
// the spec doesn't limit to < 64, but in degenerate cases (9000+ stack depth) self prevents
|
||||
// run-aways
|
||||
var stackSize = stack.count
|
||||
if(stackSize > 64){stackSize = 64}
|
||||
for si in 0..<stackSize
|
||||
{
|
||||
if(stackSize > 64) {stackSize = 64}
|
||||
for si in 0..<stackSize {
|
||||
let el: Element = stack[si]
|
||||
if (el == formatEl) {
|
||||
commonAncestor = stack[si - 1]
|
||||
|
@ -666,17 +658,15 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
// does that mean: int pos of format el in list?
|
||||
var node: Element? = furthestBlock
|
||||
var lastNode: Element? = furthestBlock
|
||||
for j in 0..<3
|
||||
{
|
||||
if (node != nil && tb.onStack(node!)){
|
||||
for j in 0..<3 {
|
||||
if (node != nil && tb.onStack(node!)) {
|
||||
node = tb.aboveOnStack(node!)
|
||||
}
|
||||
// note no bookmark check
|
||||
if (node != nil && !tb.isInActiveFormattingElements(node!))
|
||||
{
|
||||
if (node != nil && !tb.isInActiveFormattingElements(node!)) {
|
||||
tb.removeFromStack(node!)
|
||||
continue
|
||||
} else if (node == formatEl){
|
||||
} else if (node == formatEl) {
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -690,7 +680,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
// todo: move the aforementioned bookmark to be immediately after the node in the list of active formatting elements.
|
||||
// not getting how self bookmark both straddles the element above, but is inbetween here...
|
||||
}
|
||||
if (lastNode!.parent() != nil){
|
||||
if (lastNode!.parent() != nil) {
|
||||
try lastNode?.remove()
|
||||
}
|
||||
try node!.appendChild(lastNode!)
|
||||
|
@ -699,12 +689,12 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
}
|
||||
|
||||
if (StringUtil.inSorted(commonAncestor!.nodeName(), haystack: Constants.InBodyEndTableFosters)) {
|
||||
if (lastNode!.parent() != nil){
|
||||
if (lastNode!.parent() != nil) {
|
||||
try lastNode!.remove()
|
||||
}
|
||||
try tb.insertInFosterParent(lastNode!)
|
||||
} else {
|
||||
if (lastNode!.parent() != nil){
|
||||
if (lastNode!.parent() != nil) {
|
||||
try lastNode!.remove()
|
||||
}
|
||||
try commonAncestor!.appendChild(lastNode!)
|
||||
|
@ -713,8 +703,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
let adopter: Element = Element(formatEl!.tag(), tb.getBaseUri())
|
||||
adopter.getAttributes()?.addAll(incoming: formatEl!.getAttributes())
|
||||
var childNodes: [Node] = furthestBlock!.getChildNodes()
|
||||
for childNode: Node in childNodes
|
||||
{
|
||||
for childNode: Node in childNodes {
|
||||
try adopter.appendChild(childNode) // append will reparent. thus the clone to avoid concurrent mod.
|
||||
}
|
||||
try furthestBlock?.appendChild(adopter)
|
||||
|
@ -730,7 +719,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return false
|
||||
} else {
|
||||
tb.generateImpliedEndTags()
|
||||
if (!tb.currentElement()!.nodeName().equals(name)){
|
||||
if (!tb.currentElement()!.nodeName().equals(name)) {
|
||||
tb.error(self)
|
||||
}
|
||||
tb.popStackToClose(name)
|
||||
|
@ -744,7 +733,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return false
|
||||
} else {
|
||||
tb.generateImpliedEndTags(name)
|
||||
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)){
|
||||
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)) {
|
||||
tb.error(self)
|
||||
}
|
||||
tb.popStackToClose(name)
|
||||
|
@ -759,7 +748,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
}
|
||||
} else if (name.equals("html")) {
|
||||
let notIgnored: Bool = try tb.processEndTag("body")
|
||||
if (notIgnored){
|
||||
if (notIgnored) {
|
||||
return try tb.process(endTag)
|
||||
}
|
||||
} else if (name.equals("form")) {
|
||||
|
@ -770,7 +759,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return false
|
||||
} else {
|
||||
tb.generateImpliedEndTags()
|
||||
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)){
|
||||
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)) {
|
||||
tb.error(self)
|
||||
}
|
||||
// remove currentForm from stack. will shift anything under up.
|
||||
|
@ -783,7 +772,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return try tb.process(endTag)
|
||||
} else {
|
||||
tb.generateImpliedEndTags(name)
|
||||
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)){
|
||||
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)) {
|
||||
tb.error(self)
|
||||
}
|
||||
tb.popStackToClose(name)
|
||||
|
@ -794,7 +783,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return false
|
||||
} else {
|
||||
tb.generateImpliedEndTags(name)
|
||||
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)){
|
||||
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)) {
|
||||
tb.error(self)
|
||||
}
|
||||
tb.popStackToClose(name)
|
||||
|
@ -805,7 +794,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return false
|
||||
} else {
|
||||
tb.generateImpliedEndTags(name)
|
||||
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)){
|
||||
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)) {
|
||||
tb.error(self)
|
||||
}
|
||||
tb.popStackToClose(Constants.Headings)
|
||||
|
@ -820,7 +809,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return false
|
||||
}
|
||||
tb.generateImpliedEndTags()
|
||||
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)){
|
||||
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)) {
|
||||
tb.error(self)
|
||||
}
|
||||
tb.popStackToClose(name)
|
||||
|
@ -833,7 +822,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
} else {
|
||||
return anyOtherEndTag(t, tb)
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
return anyOtherEndTag(t, tb)
|
||||
}
|
||||
|
||||
|
@ -886,8 +875,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return false
|
||||
} else if (t.isStartTag()) {
|
||||
let startTag: Token.StartTag = t.asStartTag()
|
||||
if let name: String = startTag.normalName()
|
||||
{
|
||||
if let name: String = startTag.normalName() {
|
||||
if (name.equals("caption")) {
|
||||
tb.clearStackToTableContext()
|
||||
tb.insertMarkerToFormattingElements()
|
||||
|
@ -922,9 +910,9 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
}
|
||||
} else if (name.equals("form")) {
|
||||
tb.error(self)
|
||||
if (tb.getFormElement() != nil){
|
||||
if (tb.getFormElement() != nil) {
|
||||
return false
|
||||
}else {
|
||||
} else {
|
||||
try tb.insertForm(startTag, false)
|
||||
}
|
||||
} else {
|
||||
|
@ -934,8 +922,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return true // todo: check if should return processed http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#parsing-main-intable
|
||||
} else if (t.isEndTag()) {
|
||||
let endTag: Token.EndTag = t.asEndTag()
|
||||
if let name: String = endTag.normalName()
|
||||
{
|
||||
if let name: String = endTag.normalName() {
|
||||
if (name.equals("table")) {
|
||||
if (try !tb.inTableScope(name)) {
|
||||
tb.error(self)
|
||||
|
@ -951,12 +938,12 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
} else {
|
||||
return try anythingElse(t, tb)
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
return try anythingElse(t, tb)
|
||||
}
|
||||
return true // todo: as above todo
|
||||
} else if (t.isEOF()) {
|
||||
if (tb.currentElement() != nil && tb.currentElement()!.nodeName().equals("html")){
|
||||
if (tb.currentElement() != nil && tb.currentElement()!.nodeName().equals("html")) {
|
||||
tb.error(self)
|
||||
}
|
||||
return true // stops parsing
|
||||
|
@ -978,8 +965,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
default:
|
||||
// todo - don't really like the way these table character data lists are built
|
||||
if (tb.getPendingTableCharacters().count > 0) {
|
||||
for character:String in tb.getPendingTableCharacters()
|
||||
{
|
||||
for character: String in tb.getPendingTableCharacters() {
|
||||
if (!HtmlTreeBuilderState.isWhitespace(character)) {
|
||||
// InTable anything else section:
|
||||
tb.error(self)
|
||||
|
@ -990,7 +976,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
} else {
|
||||
try tb.process(Token.Char().data(character), .InBody)
|
||||
}
|
||||
} else{
|
||||
} else {
|
||||
try tb.insert(Token.Char().data(character))
|
||||
}
|
||||
}
|
||||
|
@ -1001,8 +987,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
}
|
||||
return true
|
||||
case .InCaption:
|
||||
if (t.isEndTag() && t.asEndTag().normalName()!.equals("caption"))
|
||||
{
|
||||
if (t.isEndTag() && t.asEndTag().normalName()!.equals("caption")) {
|
||||
let endTag: Token.EndTag = t.asEndTag()
|
||||
let name: String? = endTag.normalName()
|
||||
if (try name != nil && !tb.inTableScope(name!)) {
|
||||
|
@ -1010,7 +995,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return false
|
||||
} else {
|
||||
tb.generateImpliedEndTags()
|
||||
if (!tb.currentElement()!.nodeName().equals("caption")){
|
||||
if (!tb.currentElement()!.nodeName().equals("caption")) {
|
||||
tb.error(self)
|
||||
}
|
||||
tb.popStackToClose("caption")
|
||||
|
@ -1024,7 +1009,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
) {
|
||||
tb.error(self)
|
||||
let processed: Bool = try tb.processEndTag("caption")
|
||||
if (processed){
|
||||
if (processed) {
|
||||
return try tb.process(t)
|
||||
}
|
||||
} else if (t.isEndTag() && StringUtil.inString(t.asEndTag().normalName()!,
|
||||
|
@ -1038,14 +1023,12 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
case .InColumnGroup:
|
||||
func anythingElse(_ t: Token, _ tb: TreeBuilder)throws->Bool {
|
||||
let processed: Bool = try tb.processEndTag("colgroup")
|
||||
if (processed){ // only ignored in frag case
|
||||
if (processed) { // only ignored in frag case
|
||||
return try tb.process(t)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (HtmlTreeBuilderState.isWhitespace(t)) {
|
||||
try tb.insert(t.asCharacter())
|
||||
return true
|
||||
|
@ -1060,11 +1043,11 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
case .StartTag:
|
||||
let startTag: Token.StartTag = t.asStartTag()
|
||||
let name: String? = startTag.normalName()
|
||||
if ("html".equals(name)){
|
||||
if ("html".equals(name)) {
|
||||
return try tb.process(t, .InBody)
|
||||
}else if ("col".equals(name)){
|
||||
} else if ("col".equals(name)) {
|
||||
try tb.insertEmpty(startTag)
|
||||
}else{
|
||||
} else {
|
||||
return try anythingElse(t, tb)
|
||||
}
|
||||
break
|
||||
|
@ -1079,14 +1062,14 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
tb.pop()
|
||||
tb.transition(.InTable)
|
||||
}
|
||||
} else{
|
||||
} else {
|
||||
return try anythingElse(t, tb)
|
||||
}
|
||||
break
|
||||
case .EOF:
|
||||
if ("html".equals(tb.currentElement()?.nodeName())){
|
||||
if ("html".equals(tb.currentElement()?.nodeName())) {
|
||||
return true // stop parsing; frag case
|
||||
}else{
|
||||
} else {
|
||||
return try anythingElse(t, tb)
|
||||
}
|
||||
default:
|
||||
|
@ -1095,7 +1078,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return true
|
||||
case .InTableBody:
|
||||
@discardableResult
|
||||
func exitTableBody(_ t:Token, _ tb: HtmlTreeBuilder)throws->Bool {
|
||||
func exitTableBody(_ t: Token, _ tb: HtmlTreeBuilder)throws->Bool {
|
||||
if (try !(tb.inTableScope("tbody") || tb.inTableScope("thead") || tb.inScope("tfoot"))) {
|
||||
// frag case
|
||||
tb.error(self)
|
||||
|
@ -1110,7 +1093,6 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return try tb.process(t, .InTable)
|
||||
}
|
||||
|
||||
|
||||
switch (t.type) {
|
||||
case .StartTag:
|
||||
let startTag: Token.StartTag = t.asStartTag()
|
||||
|
@ -1125,7 +1107,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return try tb.process(startTag)
|
||||
} else if (StringUtil.inString(name, haystack: "caption", "col", "colgroup", "tbody", "tfoot", "thead")) {
|
||||
return try exitTableBody(t, tb)
|
||||
} else{
|
||||
} else {
|
||||
return try anythingElse(t, tb)
|
||||
}
|
||||
break
|
||||
|
@ -1146,7 +1128,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
} else if (StringUtil.inString(name, haystack: "body", "caption", "col", "colgroup", "html", "td", "th", "tr")) {
|
||||
tb.error(self)
|
||||
return false
|
||||
} else{
|
||||
} else {
|
||||
return try anythingElse(t, tb)
|
||||
}
|
||||
break
|
||||
|
@ -1161,9 +1143,9 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
|
||||
func handleMissingTr(_ t: Token, _ tb: TreeBuilder)throws->Bool {
|
||||
let processed: Bool = try tb.processEndTag("tr")
|
||||
if (processed){
|
||||
if (processed) {
|
||||
return try tb.process(t)
|
||||
}else{
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -1219,14 +1201,13 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
}
|
||||
|
||||
func closeCell(_ tb: HtmlTreeBuilder)throws {
|
||||
if (try tb.inTableScope("td")){
|
||||
if (try tb.inTableScope("td")) {
|
||||
try tb.processEndTag("td")
|
||||
}else{
|
||||
} else {
|
||||
try tb.processEndTag("th") // only here if th or td in scope
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (t.isEndTag()) {
|
||||
let endTag: Token.EndTag = t.asEndTag()
|
||||
let name: String? = endTag.normalName()
|
||||
|
@ -1238,7 +1219,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return false
|
||||
}
|
||||
tb.generateImpliedEndTags()
|
||||
if (!name!.equals(tb.currentElement()?.nodeName())){
|
||||
if (!name!.equals(tb.currentElement()?.nodeName())) {
|
||||
tb.error(self)
|
||||
}
|
||||
tb.popStackToClose(name!)
|
||||
|
@ -1272,12 +1253,11 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return true
|
||||
case .InSelect:
|
||||
|
||||
func anythingElse(_ t: Token, _ tb: HtmlTreeBuilder)->Bool {
|
||||
func anythingElse(_ t: Token, _ tb: HtmlTreeBuilder) -> Bool {
|
||||
tb.error(self)
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
switch (t.type) {
|
||||
case .Char:
|
||||
let c: Token.Char = t.asCharacter()
|
||||
|
@ -1297,15 +1277,15 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
case .StartTag:
|
||||
let start: Token.StartTag = t.asStartTag()
|
||||
let name: String? = start.normalName()
|
||||
if ("html".equals(name)){
|
||||
if ("html".equals(name)) {
|
||||
return try tb.process(start, .InBody)
|
||||
}else if ("option".equals(name)) {
|
||||
} else if ("option".equals(name)) {
|
||||
try tb.processEndTag("option")
|
||||
try tb.insert(start)
|
||||
} else if ("optgroup".equals(name)) {
|
||||
if ("option".equals(tb.currentElement()?.nodeName())){
|
||||
if ("option".equals(tb.currentElement()?.nodeName())) {
|
||||
try tb.processEndTag("option")
|
||||
}else if ("optgroup".equals(tb.currentElement()?.nodeName())){
|
||||
} else if ("optgroup".equals(tb.currentElement()?.nodeName())) {
|
||||
try tb.processEndTag("optgroup")
|
||||
}
|
||||
try tb.insert(start)
|
||||
|
@ -1314,7 +1294,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return try tb.processEndTag("select")
|
||||
} else if (StringUtil.inString(name, haystack: "input", "keygen", "textarea")) {
|
||||
tb.error(self)
|
||||
if (try !tb.inSelectScope("select")){
|
||||
if (try !tb.inSelectScope("select")) {
|
||||
return false // frag
|
||||
}
|
||||
try tb.processEndTag("select")
|
||||
|
@ -1329,18 +1309,18 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
let end: Token.EndTag = t.asEndTag()
|
||||
let name = end.normalName()
|
||||
if ("optgroup".equals(name)) {
|
||||
if ("option".equals(tb.currentElement()?.nodeName()) && tb.currentElement() != nil && tb.aboveOnStack(tb.currentElement()!) != nil && "optgroup".equals(tb.aboveOnStack(tb.currentElement()!)?.nodeName())){
|
||||
if ("option".equals(tb.currentElement()?.nodeName()) && tb.currentElement() != nil && tb.aboveOnStack(tb.currentElement()!) != nil && "optgroup".equals(tb.aboveOnStack(tb.currentElement()!)?.nodeName())) {
|
||||
try tb.processEndTag("option")
|
||||
}
|
||||
if ("optgroup".equals(tb.currentElement()?.nodeName())){
|
||||
if ("optgroup".equals(tb.currentElement()?.nodeName())) {
|
||||
tb.pop()
|
||||
}else{
|
||||
} else {
|
||||
tb.error(self)
|
||||
}
|
||||
} else if ("option".equals(name)) {
|
||||
if ("option".equals(tb.currentElement()?.nodeName())){
|
||||
if ("option".equals(tb.currentElement()?.nodeName())) {
|
||||
tb.pop()
|
||||
}else{
|
||||
} else {
|
||||
tb.error(self)
|
||||
}
|
||||
} else if ("select".equals(name)) {
|
||||
|
@ -1351,12 +1331,12 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
tb.popStackToClose(name!)
|
||||
tb.resetInsertionMode()
|
||||
}
|
||||
} else{
|
||||
} else {
|
||||
return anythingElse(t, tb)
|
||||
}
|
||||
break
|
||||
case .EOF:
|
||||
if (!"html".equals(tb.currentElement()?.nodeName())){
|
||||
if (!"html".equals(tb.currentElement()?.nodeName())) {
|
||||
tb.error(self)
|
||||
}
|
||||
break
|
||||
|
@ -1374,7 +1354,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
if (try t.asEndTag().normalName() != nil && tb.inTableScope(t.asEndTag().normalName()!)) {
|
||||
try tb.processEndTag("select")
|
||||
return try (tb.process(t))
|
||||
} else{
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
|
@ -1507,7 +1487,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
|
||||
}
|
||||
|
||||
private static func isWhitespace(_ t: Token)->Bool {
|
||||
private static func isWhitespace(_ t: Token) -> Bool {
|
||||
if (t.isCharacter()) {
|
||||
let data: String? = t.asCharacter().getData()
|
||||
return isWhitespace(data)
|
||||
|
@ -1515,11 +1495,11 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
return false
|
||||
}
|
||||
|
||||
private static func isWhitespace(_ data: String?)->Bool {
|
||||
private static func isWhitespace(_ data: String?) -> Bool {
|
||||
// todo: self checks more than spec - "\t", "\n", "\f", "\r", " "
|
||||
if let data = data{
|
||||
if let data = data {
|
||||
for c in data.characters {
|
||||
if (!StringUtil.isWhitespace(c)){
|
||||
if (!StringUtil.isWhitespace(c)) {
|
||||
return false}
|
||||
}
|
||||
}
|
||||
|
@ -1540,13 +1520,6 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
|
|||
tb.transition(.Text)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// lists of tags to search through. A little harder to read here, but causes less GC than dynamic varargs.
|
||||
// was contributing around 10% of parse GC load.
|
||||
fileprivate final class Constants {
|
||||
|
|
|
@ -8,13 +8,12 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
open class Node : Equatable, Hashable
|
||||
{
|
||||
private static let EMPTY_NODES : Array<Node> = Array<Node>()
|
||||
var parentNode : Node?
|
||||
var childNodes : Array <Node>
|
||||
var attributes : Attributes?
|
||||
var baseUri : String?
|
||||
open class Node: Equatable, Hashable {
|
||||
private static let EMPTY_NODES: Array<Node> = Array<Node>()
|
||||
var parentNode: Node?
|
||||
var childNodes: Array <Node>
|
||||
var attributes: Attributes?
|
||||
var baseUri: String?
|
||||
|
||||
/**
|
||||
* Get the list index of this node in its node sibling list. I.e. if this is the first node
|
||||
|
@ -22,7 +21,7 @@ open class Node : Equatable, Hashable
|
|||
* @return position in node sibling list
|
||||
* @see org.jsoup.nodes.Element#elementSiblingIndex()
|
||||
*/
|
||||
public private(set) var siblingIndex : Int = 0
|
||||
public private(set) var siblingIndex: Int = 0
|
||||
|
||||
/**
|
||||
Create a new Node.
|
||||
|
@ -54,7 +53,7 @@ open class Node : Equatable, Hashable
|
|||
Get the node name of this node. Use for debugging purposes and not logic switching (for that, use instanceof).
|
||||
@return node name
|
||||
*/
|
||||
public func nodeName()->String {
|
||||
public func nodeName() -> String {
|
||||
preconditionFailure("This method must be overridden")
|
||||
}
|
||||
|
||||
|
@ -74,19 +73,19 @@ open class Node : Equatable, Hashable
|
|||
* @see #absUrl(String)
|
||||
*/
|
||||
open func attr(_ attributeKey: String)throws ->String {
|
||||
let val : String = try attributes!.getIgnoreCase(key: attributeKey)
|
||||
if (val.characters.count > 0){
|
||||
let val: String = try attributes!.getIgnoreCase(key: attributeKey)
|
||||
if (val.characters.count > 0) {
|
||||
return val
|
||||
}else if (attributeKey.lowercased().startsWith("abs:")){
|
||||
} else if (attributeKey.lowercased().startsWith("abs:")) {
|
||||
return try absUrl(attributeKey.substring("abs:".characters.count))
|
||||
}else {return ""}
|
||||
} else {return ""}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the element's attributes.
|
||||
* @return attributes (which implements iterable, in same order as presented in original HTML).
|
||||
*/
|
||||
open func getAttributes()->Attributes? {
|
||||
open func getAttributes() -> Attributes? {
|
||||
return attributes
|
||||
}
|
||||
|
||||
|
@ -98,7 +97,7 @@ open class Node : Equatable, Hashable
|
|||
*/
|
||||
@discardableResult
|
||||
open func attr(_ attributeKey: String, _ attributeValue: String)throws->Node {
|
||||
try attributes?.put(attributeKey , attributeValue)
|
||||
try attributes?.put(attributeKey, attributeValue)
|
||||
return self
|
||||
}
|
||||
|
||||
|
@ -107,18 +106,18 @@ open class Node : Equatable, Hashable
|
|||
* @param attributeKey The attribute key to check.
|
||||
* @return true if the attribute exists, false if not.
|
||||
*/
|
||||
open func hasAttr(_ attributeKey: String)->Bool {
|
||||
open func hasAttr(_ attributeKey: String) -> Bool {
|
||||
guard let attributes = attributes else {
|
||||
return false
|
||||
}
|
||||
if (attributeKey.startsWith("abs:")) {
|
||||
let key : String = attributeKey.substring("abs:".characters.count)
|
||||
do{
|
||||
let key: String = attributeKey.substring("abs:".characters.count)
|
||||
do {
|
||||
let abs = try absUrl(key)
|
||||
if (attributes.hasKeyIgnoreCase(key: key) && !"".equals(abs)){
|
||||
if (attributes.hasKeyIgnoreCase(key: key) && !"".equals(abs)) {
|
||||
return true
|
||||
}
|
||||
}catch{
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -141,7 +140,7 @@ open class Node : Equatable, Hashable
|
|||
Get the base URI of this node.
|
||||
@return base URI
|
||||
*/
|
||||
open func getBaseUri()->String {
|
||||
open func getBaseUri() -> String {
|
||||
return baseUri!
|
||||
}
|
||||
|
||||
|
@ -149,13 +148,10 @@ open class Node : Equatable, Hashable
|
|||
Update the base URI of this node and all of its descendants.
|
||||
@param baseUri base URI to set
|
||||
*/
|
||||
open func setBaseUri(_ baseUri: String)throws
|
||||
{
|
||||
class nodeVisitor : NodeVisitor
|
||||
{
|
||||
private let baseUri : String
|
||||
init(_ baseUri: String)
|
||||
{
|
||||
open func setBaseUri(_ baseUri: String)throws {
|
||||
class nodeVisitor: NodeVisitor {
|
||||
private let baseUri: String
|
||||
init(_ baseUri: String) {
|
||||
self.baseUri = baseUri
|
||||
}
|
||||
|
||||
|
@ -207,7 +203,7 @@ open class Node : Equatable, Hashable
|
|||
@param index index of child node
|
||||
@return the child node at this index. Throws a {@code IndexOutOfBoundsException} if the index is out of bounds.
|
||||
*/
|
||||
open func childNode(_ index: Int)->Node {
|
||||
open func childNode(_ index: Int) -> Node {
|
||||
return childNodes[index]
|
||||
}
|
||||
|
||||
|
@ -225,10 +221,9 @@ open class Node : Equatable, Hashable
|
|||
* nodes
|
||||
* @return a deep copy of this node's children
|
||||
*/
|
||||
open func childNodesCopy()->Array<Node>{
|
||||
open func childNodesCopy()->Array<Node> {
|
||||
var children: Array<Node> = Array<Node>()
|
||||
for node: Node in childNodes
|
||||
{
|
||||
for node: Node in childNodes {
|
||||
children.append(node.copy() as! Node)
|
||||
}
|
||||
return children
|
||||
|
@ -238,11 +233,11 @@ open class Node : Equatable, Hashable
|
|||
* Get the number of child nodes that this node holds.
|
||||
* @return the number of child nodes that this node holds.
|
||||
*/
|
||||
public func childNodeSize()->Int {
|
||||
public func childNodeSize() -> Int {
|
||||
return childNodes.count
|
||||
}
|
||||
|
||||
final func childNodesAsArray()->[Node] {
|
||||
final func childNodesAsArray() -> [Node] {
|
||||
return childNodes as Array
|
||||
}
|
||||
|
||||
|
@ -258,7 +253,7 @@ open class Node : Equatable, Hashable
|
|||
Gets this node's parent node. Node overridable by extending classes, so useful if you really just need the Node type.
|
||||
@return parent node or null if no parent.
|
||||
*/
|
||||
final func getParentNode()->Node? {
|
||||
final func getParentNode() -> Node? {
|
||||
return parentNode
|
||||
}
|
||||
|
||||
|
@ -266,12 +261,12 @@ open class Node : Equatable, Hashable
|
|||
* Gets the Document associated with this Node.
|
||||
* @return the Document associated with this Node, or null if there is no such Document.
|
||||
*/
|
||||
open func ownerDocument()-> Document? {
|
||||
if let this = self as? Document{
|
||||
open func ownerDocument() -> Document? {
|
||||
if let this = self as? Document {
|
||||
return this
|
||||
}else if (parentNode == nil){
|
||||
} else if (parentNode == nil) {
|
||||
return nil
|
||||
}else{
|
||||
} else {
|
||||
return parentNode!.ownerDocument()
|
||||
}
|
||||
}
|
||||
|
@ -306,7 +301,7 @@ open class Node : Equatable, Hashable
|
|||
try Validate.notNull(obj: node)
|
||||
try Validate.notNull(obj: parentNode)
|
||||
|
||||
try parentNode?.addChildren(siblingIndex,node)
|
||||
try parentNode?.addChildren(siblingIndex, node)
|
||||
return self
|
||||
}
|
||||
|
||||
|
@ -333,20 +328,19 @@ open class Node : Equatable, Hashable
|
|||
try Validate.notNull(obj: node)
|
||||
try Validate.notNull(obj: parentNode)
|
||||
|
||||
try parentNode?.addChildren(siblingIndex+1,node)
|
||||
try parentNode?.addChildren(siblingIndex+1, node)
|
||||
return self
|
||||
}
|
||||
|
||||
private func addSiblingHtml(_ index: Int, _ html: String)throws {
|
||||
try Validate.notNull(obj: parentNode)
|
||||
|
||||
let context : Element? = parent() as? Element
|
||||
let context: Element? = parent() as? Element
|
||||
|
||||
let nodes : Array<Node> = try Parser.parseFragment(html, context, getBaseUri())
|
||||
try parentNode?.addChildren(index,nodes)
|
||||
let nodes: Array<Node> = try Parser.parseFragment(html, context, getBaseUri())
|
||||
try parentNode?.addChildren(index, nodes)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Insert the specified HTML into the DOM after this node (i.e. as a following sibling).
|
||||
* @param html HTML to add after this node
|
||||
|
@ -378,8 +372,8 @@ open class Node : Equatable, Hashable
|
|||
try Validate.notNull(obj: html)
|
||||
try Validate.notNull(obj: parentNode)
|
||||
|
||||
let context : Element? = parent() as? Element
|
||||
let nodes : Array<Node> = try Parser.parseFragment(html, context, getBaseUri())
|
||||
let context: Element? = parent() as? Element
|
||||
let nodes: Array<Node> = try Parser.parseFragment(html, context, getBaseUri())
|
||||
try parentNode?.addChildren(index, nodes)
|
||||
}
|
||||
|
||||
|
@ -392,25 +386,23 @@ open class Node : Equatable, Hashable
|
|||
open func wrap(_ html: String)throws->Node? {
|
||||
try Validate.notEmpty(string: html)
|
||||
|
||||
let context : Element? = parent() as? Element
|
||||
var wrapChildren : Array<Node> = try Parser.parseFragment(html, context, getBaseUri())
|
||||
let wrapNode : Node? = wrapChildren.count > 0 ? wrapChildren[0] : nil
|
||||
if (wrapNode == nil || !(((wrapNode as? Element) != nil))){ // nothing to wrap with; noop
|
||||
let context: Element? = parent() as? Element
|
||||
var wrapChildren: Array<Node> = try Parser.parseFragment(html, context, getBaseUri())
|
||||
let wrapNode: Node? = wrapChildren.count > 0 ? wrapChildren[0] : nil
|
||||
if (wrapNode == nil || !(((wrapNode as? Element) != nil))) { // nothing to wrap with; noop
|
||||
return nil
|
||||
}
|
||||
|
||||
let wrap : Element = wrapNode as! Element
|
||||
let deepest : Element = getDeepChild(el: wrap)
|
||||
let wrap: Element = wrapNode as! Element
|
||||
let deepest: Element = getDeepChild(el: wrap)
|
||||
try parentNode?.replaceChild(self, wrap)
|
||||
wrapChildren = wrapChildren.filter { $0 != wrap}
|
||||
try deepest.addChildren(self)
|
||||
|
||||
// remainder (unbalanced wrap, like <div></div><p></p> -- The <p> is remainder
|
||||
if (wrapChildren.count > 0)
|
||||
{
|
||||
for i in 0..<wrapChildren.count
|
||||
{
|
||||
let remainder : Node = wrapChildren[i]
|
||||
if (wrapChildren.count > 0) {
|
||||
for i in 0..<wrapChildren.count {
|
||||
let remainder: Node = wrapChildren[i]
|
||||
try remainder.parentNode?.removeChild(remainder)
|
||||
try wrap.appendChild(remainder)
|
||||
}
|
||||
|
@ -437,18 +429,18 @@ open class Node : Equatable, Hashable
|
|||
open func unwrap()throws ->Node? {
|
||||
try Validate.notNull(obj: parentNode)
|
||||
|
||||
let firstChild : Node? = childNodes.count > 0 ? childNodes[0] : nil
|
||||
let firstChild: Node? = childNodes.count > 0 ? childNodes[0] : nil
|
||||
try parentNode?.addChildren(siblingIndex, self.childNodesAsArray())
|
||||
try self.remove()
|
||||
|
||||
return firstChild
|
||||
}
|
||||
|
||||
private func getDeepChild(el: Element)->Element {
|
||||
private func getDeepChild(el: Element) -> Element {
|
||||
let children = el.children()
|
||||
if (children.size() > 0){
|
||||
if (children.size() > 0) {
|
||||
return getDeepChild(el: children.get(0))
|
||||
}else{
|
||||
} else {
|
||||
return el
|
||||
}
|
||||
}
|
||||
|
@ -464,7 +456,7 @@ open class Node : Equatable, Hashable
|
|||
}
|
||||
|
||||
public func setParentNode(_ parentNode: Node)throws {
|
||||
if (self.parentNode != nil){
|
||||
if (self.parentNode != nil) {
|
||||
try self.parentNode?.removeChild(self)
|
||||
}
|
||||
self.parentNode = parentNode
|
||||
|
@ -473,11 +465,11 @@ open class Node : Equatable, Hashable
|
|||
public func replaceChild(_ out: Node, _ input: Node)throws {
|
||||
try Validate.isTrue(val: out.parentNode === self)
|
||||
try Validate.notNull(obj: input)
|
||||
if (input.parentNode != nil){
|
||||
if (input.parentNode != nil) {
|
||||
try input.parentNode?.removeChild(input)
|
||||
}
|
||||
|
||||
let index : Int = out.siblingIndex
|
||||
let index: Int = out.siblingIndex
|
||||
childNodes[index] = input
|
||||
input.parentNode = self
|
||||
input.setSiblingIndex(index)
|
||||
|
@ -486,7 +478,7 @@ open class Node : Equatable, Hashable
|
|||
|
||||
public func removeChild(_ out: Node)throws {
|
||||
try Validate.isTrue(val: out.parentNode === self)
|
||||
let index : Int = out.siblingIndex
|
||||
let index: Int = out.siblingIndex
|
||||
childNodes.remove(at: index)
|
||||
reindexChildren(index)
|
||||
out.parentNode = nil
|
||||
|
@ -499,8 +491,7 @@ open class Node : Equatable, Hashable
|
|||
|
||||
public func addChildren(_ children: [Node])throws {
|
||||
//most used. short circuit addChildren(int), which hits reindex children and array copy
|
||||
for child in children
|
||||
{
|
||||
for child in children {
|
||||
try reparentChild(child)
|
||||
ensureChildNodes()
|
||||
childNodes.append(child)
|
||||
|
@ -508,38 +499,35 @@ open class Node : Equatable, Hashable
|
|||
}
|
||||
}
|
||||
|
||||
public func addChildren(_ index: Int,_ children: Node...)throws {
|
||||
try addChildren(index,children)
|
||||
public func addChildren(_ index: Int, _ children: Node...)throws {
|
||||
try addChildren(index, children)
|
||||
}
|
||||
|
||||
public func addChildren(_ index: Int,_ children: [Node])throws {
|
||||
public func addChildren(_ index: Int, _ children: [Node])throws {
|
||||
ensureChildNodes()
|
||||
for i in (0..<children.count).reversed()
|
||||
{
|
||||
let input : Node = children[i]
|
||||
for i in (0..<children.count).reversed() {
|
||||
let input: Node = children[i]
|
||||
try reparentChild(input)
|
||||
childNodes.insert(input, at: index)
|
||||
reindexChildren(index)
|
||||
}
|
||||
}
|
||||
|
||||
public func ensureChildNodes()
|
||||
{
|
||||
public func ensureChildNodes() {
|
||||
// if (childNodes === Node.EMPTY_NODES) {
|
||||
// childNodes = Array<Node>()
|
||||
// }
|
||||
}
|
||||
|
||||
public func reparentChild(_ child: Node)throws {
|
||||
if (child.parentNode != nil){
|
||||
if (child.parentNode != nil) {
|
||||
try child.parentNode?.removeChild(child)
|
||||
}
|
||||
try child.setParentNode(self)
|
||||
}
|
||||
|
||||
private func reindexChildren(_ start: Int) {
|
||||
for i in start..<childNodes.count
|
||||
{
|
||||
for i in start..<childNodes.count {
|
||||
childNodes[i].setSiblingIndex(i)
|
||||
}
|
||||
}
|
||||
|
@ -550,15 +538,14 @@ open class Node : Equatable, Hashable
|
|||
@return node siblings. If the node has no parent, returns an empty list.
|
||||
*/
|
||||
open func siblingNodes()->Array<Node> {
|
||||
if (parentNode == nil){
|
||||
if (parentNode == nil) {
|
||||
return Array<Node>()
|
||||
}
|
||||
|
||||
let nodes : Array<Node> = parentNode!.childNodes
|
||||
var siblings : Array<Node> = Array<Node>()
|
||||
for node in nodes
|
||||
{
|
||||
if (node !== self){
|
||||
let nodes: Array<Node> = parentNode!.childNodes
|
||||
var siblings: Array<Node> = Array<Node>()
|
||||
for node in nodes {
|
||||
if (node !== self) {
|
||||
siblings.append(node)
|
||||
}
|
||||
}
|
||||
|
@ -566,21 +553,20 @@ open class Node : Equatable, Hashable
|
|||
return siblings
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Get this node's next sibling.
|
||||
@return next sibling, or null if this is the last sibling
|
||||
*/
|
||||
open func nextSibling()->Node? {
|
||||
if (parentNode == nil){
|
||||
open func nextSibling() -> Node? {
|
||||
if (parentNode == nil) {
|
||||
return nil // root
|
||||
}
|
||||
|
||||
let siblings : Array<Node> = parentNode!.childNodes
|
||||
let index : Int = siblingIndex+1
|
||||
if (siblings.count > index){
|
||||
let siblings: Array<Node> = parentNode!.childNodes
|
||||
let index: Int = siblingIndex+1
|
||||
if (siblings.count > index) {
|
||||
return siblings[index]
|
||||
}else{
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -589,20 +575,18 @@ open class Node : Equatable, Hashable
|
|||
Get this node's previous sibling.
|
||||
@return the previous sibling, or null if this is the first sibling
|
||||
*/
|
||||
open func previousSibling()->Node? {
|
||||
if (parentNode == nil){
|
||||
open func previousSibling() -> Node? {
|
||||
if (parentNode == nil) {
|
||||
return nil // root
|
||||
}
|
||||
|
||||
if (siblingIndex > 0){
|
||||
if (siblingIndex > 0) {
|
||||
return parentNode?.childNodes[siblingIndex-1]
|
||||
}else{
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public func setSiblingIndex(_ siblingIndex: Int) {
|
||||
self.siblingIndex = siblingIndex
|
||||
}
|
||||
|
@ -614,7 +598,7 @@ open class Node : Equatable, Hashable
|
|||
*/
|
||||
@discardableResult
|
||||
open func traverse(_ nodeVisitor: NodeVisitor)throws->Node {
|
||||
let traversor : NodeTraversor = NodeTraversor(nodeVisitor)
|
||||
let traversor: NodeTraversor = NodeTraversor(nodeVisitor)
|
||||
try traversor.traverse(self)
|
||||
return self
|
||||
}
|
||||
|
@ -624,7 +608,7 @@ open class Node : Equatable, Hashable
|
|||
@return HTML
|
||||
*/
|
||||
open func outerHtml()throws->String {
|
||||
let accum : StringBuilder = StringBuilder(128)
|
||||
let accum: StringBuilder = StringBuilder(128)
|
||||
try outerHtml(accum)
|
||||
return accum.toString()
|
||||
}
|
||||
|
@ -634,7 +618,7 @@ open class Node : Equatable, Hashable
|
|||
}
|
||||
|
||||
// if this node has no document (or parent), retrieve the default output settings
|
||||
func getOutputSettings()-> OutputSettings {
|
||||
func getOutputSettings() -> OutputSettings {
|
||||
return ownerDocument() != nil ? ownerDocument()!.outputSettings() : (Document("")).outputSettings()
|
||||
}
|
||||
|
||||
|
@ -643,17 +627,14 @@ open class Node : Equatable, Hashable
|
|||
@param accum accumulator to place HTML into
|
||||
@throws IOException if appending to the given accumulator fails.
|
||||
*/
|
||||
func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) throws
|
||||
{
|
||||
func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) throws {
|
||||
preconditionFailure("This method must be overridden")
|
||||
}
|
||||
|
||||
func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) throws
|
||||
{
|
||||
func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) throws {
|
||||
preconditionFailure("This method must be overridden")
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write this node and its children to the given {@link Appendable}.
|
||||
*
|
||||
|
@ -680,7 +661,7 @@ open class Node : Equatable, Hashable
|
|||
* @see Node#hasSameValue(Object) to compare nodes by their value
|
||||
*/
|
||||
|
||||
open func equals(_ o: Node)->Bool {
|
||||
open func equals(_ o: Node) -> Bool {
|
||||
// implemented just so that javadoc is clear this is an identity test
|
||||
return self === o
|
||||
}
|
||||
|
@ -692,8 +673,8 @@ open class Node : Equatable, Hashable
|
|||
* @return true if the content of this node is the same as the other
|
||||
*/
|
||||
|
||||
open func hasSameValue(_ o : Node)throws->Bool {
|
||||
if (self === o){return true}
|
||||
open func hasSameValue(_ o: Node)throws->Bool {
|
||||
if (self === o) {return true}
|
||||
// if (type(of:self) != type(of: o))
|
||||
// {
|
||||
// return false
|
||||
|
@ -710,31 +691,27 @@ open class Node : Equatable, Hashable
|
|||
* The cloned node may be adopted into another Document or node structure using {@link Element#appendChild(Node)}.
|
||||
* @return stand-alone cloned node
|
||||
*/
|
||||
public func copy(with zone: NSZone? = nil) -> Any
|
||||
{
|
||||
public func copy(with zone: NSZone? = nil) -> Any {
|
||||
return copy(clone: Node())
|
||||
}
|
||||
|
||||
public func copy(parent: Node?)->Node
|
||||
{
|
||||
public func copy(parent: Node?) -> Node {
|
||||
let clone = Node()
|
||||
return copy(clone: clone,parent: parent)
|
||||
return copy(clone: clone, parent: parent)
|
||||
}
|
||||
|
||||
public func copy(clone: Node)->Node
|
||||
{
|
||||
let thisClone : Node = copy(clone: clone, parent: nil) // splits for orphan
|
||||
public func copy(clone: Node) -> Node {
|
||||
let thisClone: Node = copy(clone: clone, parent: nil) // splits for orphan
|
||||
|
||||
// Queue up nodes that need their children cloned (BFS).
|
||||
var nodesToProcess : Array<Node> = Array<Node>()
|
||||
var nodesToProcess: Array<Node> = Array<Node>()
|
||||
nodesToProcess.append(thisClone)
|
||||
|
||||
while (!nodesToProcess.isEmpty) {
|
||||
let currParent : Node = nodesToProcess.removeFirst()
|
||||
let currParent: Node = nodesToProcess.removeFirst()
|
||||
|
||||
for i in 0..<currParent.childNodes.count
|
||||
{
|
||||
let childClone : Node = currParent.childNodes[i].copy(parent:currParent)
|
||||
for i in 0..<currParent.childNodes.count {
|
||||
let childClone: Node = currParent.childNodes[i].copy(parent:currParent)
|
||||
currParent.childNodes[i] = childClone
|
||||
nodesToProcess.append(childClone)
|
||||
}
|
||||
|
@ -746,48 +723,41 @@ open class Node : Equatable, Hashable
|
|||
* Return a clone of the node using the given parent (which can be null).
|
||||
* Not a deep copy of children.
|
||||
*/
|
||||
public func copy(clone: Node, parent: Node?)->Node
|
||||
{
|
||||
public func copy(clone: Node, parent: Node?) -> Node {
|
||||
clone.parentNode = parent // can be null, to create an orphan split
|
||||
clone.siblingIndex = parent == nil ? 0 : siblingIndex
|
||||
clone.attributes = attributes != nil ? attributes?.clone() : nil
|
||||
clone.baseUri = baseUri
|
||||
clone.childNodes = Array<Node>()
|
||||
|
||||
for child in childNodes{
|
||||
for child in childNodes {
|
||||
clone.childNodes.append(child)
|
||||
}
|
||||
|
||||
return clone
|
||||
}
|
||||
|
||||
private class OuterHtmlVisitor : NodeVisitor {
|
||||
private var accum : StringBuilder
|
||||
private var out : OutputSettings
|
||||
private class OuterHtmlVisitor: NodeVisitor {
|
||||
private var accum: StringBuilder
|
||||
private var out: OutputSettings
|
||||
|
||||
init(_ accum: StringBuilder, _ out: OutputSettings) {
|
||||
self.accum = accum
|
||||
self.out = out
|
||||
}
|
||||
|
||||
open func head(_ node: Node, _ depth: Int)throws{
|
||||
open func head(_ node: Node, _ depth: Int)throws {
|
||||
|
||||
try node.outerHtmlHead(accum, depth, out)
|
||||
}
|
||||
|
||||
open func tail(_ node: Node, _ depth: Int)throws {
|
||||
if (!(node.nodeName() == "#text"))
|
||||
{ // saves a void hit.
|
||||
if (!(node.nodeName() == "#text")) { // saves a void hit.
|
||||
try node.outerHtmlTail(accum, depth, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Returns a Boolean value indicating whether two values are equal.
|
||||
///
|
||||
/// Equality is the inverse of inequality. For any values `a` and `b`,
|
||||
|
@ -796,46 +766,40 @@ open class Node : Equatable, Hashable
|
|||
/// - Parameters:
|
||||
/// - lhs: A value to compare.
|
||||
/// - rhs: Another value to compare.
|
||||
public static func ==(lhs: Node, rhs: Node) -> Bool{
|
||||
public static func ==(lhs: Node, rhs: Node) -> Bool {
|
||||
return lhs === rhs
|
||||
}
|
||||
|
||||
|
||||
/// The hash value.
|
||||
///
|
||||
/// Hash values are not guaranteed to be equal across different executions of
|
||||
/// your program. Do not save hash values to use during a future execution.
|
||||
public var hashValue: Int {
|
||||
var result : Int = description.hashValue
|
||||
var result: Int = description.hashValue
|
||||
result = Int.addWithOverflow(Int.multiplyWithOverflow(31, result).0, baseUri != nil ? baseUri!.hashValue : 31).0
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
extension Node : CustomStringConvertible
|
||||
{
|
||||
extension Node : CustomStringConvertible {
|
||||
public var description: String {
|
||||
do{
|
||||
do {
|
||||
return try toString()
|
||||
}catch{
|
||||
} catch {
|
||||
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
extension Node : CustomDebugStringConvertible
|
||||
{
|
||||
extension Node : CustomDebugStringConvertible {
|
||||
public var debugDescription: String {
|
||||
do{
|
||||
do {
|
||||
return try String(describing: type(of: self)) + " " + toString()
|
||||
}catch{
|
||||
} catch {
|
||||
|
||||
}
|
||||
return String(describing: type(of: self))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
class NodeTraversor
|
||||
{
|
||||
private let visitor : NodeVisitor
|
||||
class NodeTraversor {
|
||||
private let visitor: NodeVisitor
|
||||
|
||||
/**
|
||||
* Create a new traversor.
|
||||
|
@ -25,8 +24,8 @@ class NodeTraversor
|
|||
* @param root the root node point to traverse.
|
||||
*/
|
||||
open func traverse(_ root: Node?)throws {
|
||||
var node : Node? = root
|
||||
var depth : Int = 0
|
||||
var node: Node? = root
|
||||
var depth: Int = 0
|
||||
|
||||
while (node != nil) {
|
||||
try visitor.head(node!, depth)
|
||||
|
@ -40,7 +39,7 @@ class NodeTraversor
|
|||
depth-=1
|
||||
}
|
||||
try visitor.tail(node!, depth)
|
||||
if (node === root){
|
||||
if (node === root) {
|
||||
break
|
||||
}
|
||||
node = node!.nextSibling()
|
||||
|
@ -48,5 +47,4 @@ class NodeTraversor
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollection ,Hashable {
|
||||
public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollection, Hashable {
|
||||
|
||||
/// Returns the position immediately after the given index.
|
||||
///
|
||||
|
@ -32,7 +32,7 @@ public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollecti
|
|||
// ======================================================= //
|
||||
|
||||
public init() {}
|
||||
public init(count:Int) {}
|
||||
public init(count: Int) {}
|
||||
|
||||
public init(elements: [Element]) {
|
||||
for (key, value) in elements {
|
||||
|
@ -44,16 +44,14 @@ public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollecti
|
|||
return copy(with: nil)
|
||||
}
|
||||
|
||||
public func mutableCopy(with zone: NSZone? = nil) -> Any
|
||||
{
|
||||
public func mutableCopy(with zone: NSZone? = nil) -> Any {
|
||||
return copy()
|
||||
}
|
||||
|
||||
public func copy(with zone: NSZone? = nil) -> Any {
|
||||
let copy = OrderedDictionary<Key,Value>()
|
||||
let copy = OrderedDictionary<Key, Value>()
|
||||
//let copy = type(of:self).init()
|
||||
for element in orderedKeys
|
||||
{
|
||||
for element in orderedKeys {
|
||||
copy.put(value: valueForKey(key: element)!, forKey: element)
|
||||
}
|
||||
return copy
|
||||
|
@ -113,7 +111,6 @@ public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollecti
|
|||
return 0
|
||||
}
|
||||
|
||||
|
||||
public func hashCode() -> Int {
|
||||
return hashValue
|
||||
}
|
||||
|
@ -136,15 +133,12 @@ public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollecti
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public func put(value: Value, forKey key: Key) {
|
||||
self[key] = value
|
||||
}
|
||||
|
||||
public func putAll(all:OrderedDictionary<Key, Value>)
|
||||
{
|
||||
for i in all.orderedKeys
|
||||
{
|
||||
public func putAll(all: OrderedDictionary<Key, Value>) {
|
||||
for i in all.orderedKeys {
|
||||
put(value:all[i]!, forKey: i)
|
||||
}
|
||||
}
|
||||
|
@ -175,7 +169,6 @@ public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollecti
|
|||
_keysToValues.removeAll(keepingCapacity: keepCapacity)
|
||||
}
|
||||
|
||||
|
||||
// ======================================================= //
|
||||
// MARK: - Managing Content Using Indexes
|
||||
// ======================================================= //
|
||||
|
@ -370,8 +363,7 @@ extension OrderedDictionary: CustomStringConvertible, CustomDebugStringConvertib
|
|||
|
||||
}
|
||||
|
||||
extension OrderedDictionary: Equatable
|
||||
{
|
||||
extension OrderedDictionary: Equatable {
|
||||
/// Returns a Boolean value indicating whether two values are equal.
|
||||
///
|
||||
/// Equality is the inverse of inequality. For any values `a` and `b`,
|
||||
|
@ -381,12 +373,11 @@ extension OrderedDictionary: Equatable
|
|||
/// - lhs: A value to compare.
|
||||
/// - rhs: Another value to compare.
|
||||
public static func ==(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
|
||||
if(lhs.count != rhs.count){return false}
|
||||
if(lhs.count != rhs.count) {return false}
|
||||
return (lhs._orderedKeys == rhs._orderedKeys) && (lhs._keysToValues == rhs._keysToValues)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//public func == <Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
|
||||
// return lhs._orderedKeys == rhs._orderedKeys && lhs._keysToValues == rhs._keysToValues
|
||||
//}
|
||||
|
|
|
@ -13,14 +13,13 @@ public class OrderedSet<T: Hashable> {
|
|||
fileprivate var contents = [T: Index]() // Needs to have a value of Index instead of Void for fast removals
|
||||
fileprivate var sequencedContents = Array<UnsafeMutablePointer<T>>()
|
||||
|
||||
|
||||
/**
|
||||
Inititalizes an empty ordered set.
|
||||
- returns: An empty ordered set.
|
||||
*/
|
||||
public init() { }
|
||||
|
||||
deinit{
|
||||
deinit {
|
||||
removeAllObjects()
|
||||
}
|
||||
|
||||
|
|
|
@ -12,8 +12,8 @@ import Foundation
|
|||
* A Parse Error records an error in the input HTML that occurs in either the tokenisation or the tree building phase.
|
||||
*/
|
||||
open class ParseError {
|
||||
private let pos : Int
|
||||
private let errorMsg : String
|
||||
private let pos: Int
|
||||
private let errorMsg: String
|
||||
|
||||
init(_ pos: Int, _ errorMsg: String) {
|
||||
self.pos = pos
|
||||
|
@ -24,7 +24,7 @@ open class ParseError {
|
|||
* Retrieve the error message.
|
||||
* @return the error message.
|
||||
*/
|
||||
open func getErrorMessage()->String {
|
||||
open func getErrorMessage() -> String {
|
||||
return errorMsg
|
||||
}
|
||||
|
||||
|
@ -32,12 +32,11 @@ open class ParseError {
|
|||
* Retrieves the offset of the error.
|
||||
* @return error offset within input
|
||||
*/
|
||||
open func getPosition()->Int {
|
||||
open func getPosition() -> Int {
|
||||
return pos
|
||||
}
|
||||
|
||||
|
||||
open func toString()->String {
|
||||
open func toString() -> String {
|
||||
return "\(pos): " + errorMsg
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,12 +8,11 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public class ParseErrorList
|
||||
{
|
||||
private static let INITIAL_CAPACITY : Int = 16
|
||||
private let maxSize : Int
|
||||
private let initialCapacity : Int
|
||||
private var array : Array<ParseError?> = Array<ParseError>()
|
||||
public class ParseErrorList {
|
||||
private static let INITIAL_CAPACITY: Int = 16
|
||||
private let maxSize: Int
|
||||
private let initialCapacity: Int
|
||||
private var array: Array<ParseError?> = Array<ParseError>()
|
||||
|
||||
init(_ initialCapacity: Int, _ maxSize: Int) {
|
||||
self.maxSize = maxSize
|
||||
|
@ -21,19 +20,19 @@ public class ParseErrorList
|
|||
array = Array(repeating: nil, count: maxSize)
|
||||
}
|
||||
|
||||
func canAddError()->Bool {
|
||||
func canAddError() -> Bool {
|
||||
return array.count < maxSize
|
||||
}
|
||||
|
||||
func getMaxSize()->Int {
|
||||
func getMaxSize() -> Int {
|
||||
return maxSize
|
||||
}
|
||||
|
||||
static func noTracking()->ParseErrorList {
|
||||
static func noTracking() -> ParseErrorList {
|
||||
return ParseErrorList(0, 0)
|
||||
}
|
||||
|
||||
static func tracking(_ maxSize: Int)->ParseErrorList {
|
||||
static func tracking(_ maxSize: Int) -> ParseErrorList {
|
||||
return ParseErrorList(INITIAL_CAPACITY, maxSize)
|
||||
}
|
||||
|
||||
|
@ -46,7 +45,7 @@ public class ParseErrorList
|
|||
array.append(e)
|
||||
}
|
||||
|
||||
open func add(_ index: Int, _ element: ParseError){
|
||||
open func add(_ index: Int, _ element: ParseError) {
|
||||
array.insert(element, at: index)
|
||||
}
|
||||
|
||||
|
|
|
@ -8,21 +8,18 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
open class ParseSettings
|
||||
{
|
||||
open class ParseSettings {
|
||||
/**
|
||||
* HTML default settings: both tag and attribute names are lower-cased during parsing.
|
||||
*/
|
||||
public static let htmlDefault : ParseSettings = ParseSettings(false, false)
|
||||
public static let htmlDefault: ParseSettings = ParseSettings(false, false)
|
||||
/**
|
||||
* Preserve both tag and attribute case.
|
||||
*/
|
||||
public static let preserveCase : ParseSettings = ParseSettings(true, true)
|
||||
|
||||
|
||||
private let preserveTagCase : Bool
|
||||
private let preserveAttributeCase : Bool
|
||||
public static let preserveCase: ParseSettings = ParseSettings(true, true)
|
||||
|
||||
private let preserveTagCase: Bool
|
||||
private let preserveAttributeCase: Bool
|
||||
|
||||
/**
|
||||
* Define parse settings.
|
||||
|
@ -34,17 +31,17 @@ open class ParseSettings
|
|||
preserveAttributeCase = attribute
|
||||
}
|
||||
|
||||
open func normalizeTag(_ name: String)->String {
|
||||
open func normalizeTag(_ name: String) -> String {
|
||||
var name = name.trim()
|
||||
if (!preserveTagCase){
|
||||
if (!preserveTagCase) {
|
||||
name = name.lowercased()
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
open func normalizeAttribute(_ name: String)->String {
|
||||
open func normalizeAttribute(_ name: String) -> String {
|
||||
var name = name.trim()
|
||||
if (!preserveAttributeCase){
|
||||
if (!preserveAttributeCase) {
|
||||
name = name.lowercased()
|
||||
}
|
||||
return name
|
||||
|
@ -52,13 +49,11 @@ open class ParseSettings
|
|||
|
||||
open func normalizeAttributes(_ attributes: Attributes)throws ->Attributes {
|
||||
if (!preserveAttributeCase) {
|
||||
for attr in attributes.iterator()
|
||||
{
|
||||
for attr in attributes.iterator() {
|
||||
try attr.setKey(key: attr.getKey().lowercased())
|
||||
}
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -8,18 +8,16 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/**
|
||||
* Parses HTML into a {@link org.jsoup.nodes.Document}. Generally best to use one of the more convenient parse methods
|
||||
* in {@link org.jsoup.Jsoup}.
|
||||
*/
|
||||
public class Parser
|
||||
{
|
||||
public class Parser {
|
||||
private static let DEFAULT_MAX_ERRORS: Int = 0 // by default, error tracking is disabled.
|
||||
|
||||
private var _treeBuilder: TreeBuilder
|
||||
private var _maxErrors: Int = DEFAULT_MAX_ERRORS
|
||||
private var _errors: ParseErrorList = ParseErrorList(16,16)
|
||||
private var _errors: ParseErrorList = ParseErrorList(16, 16)
|
||||
private var _settings: ParseSettings
|
||||
|
||||
/**
|
||||
|
@ -41,7 +39,7 @@ public class Parser
|
|||
* Get the TreeBuilder currently in use.
|
||||
* @return current TreeBuilder.
|
||||
*/
|
||||
public func getTreeBuilder()->TreeBuilder {
|
||||
public func getTreeBuilder() -> TreeBuilder {
|
||||
return _treeBuilder
|
||||
}
|
||||
|
||||
|
@ -51,7 +49,7 @@ public class Parser
|
|||
* @return this, for chaining
|
||||
*/
|
||||
@discardableResult
|
||||
public func setTreeBuilder(_ treeBuilder: TreeBuilder)->Parser {
|
||||
public func setTreeBuilder(_ treeBuilder: TreeBuilder) -> Parser {
|
||||
self._treeBuilder = treeBuilder
|
||||
return self
|
||||
}
|
||||
|
@ -60,7 +58,7 @@ public class Parser
|
|||
* Check if parse error tracking is enabled.
|
||||
* @return current track error state.
|
||||
*/
|
||||
public func isTrackErrors()->Bool {
|
||||
public func isTrackErrors() -> Bool {
|
||||
return _maxErrors > 0
|
||||
}
|
||||
|
||||
|
@ -70,7 +68,7 @@ public class Parser
|
|||
* @return this, for chaining
|
||||
*/
|
||||
@discardableResult
|
||||
public func setTrackErrors(_ maxErrors: Int)->Parser {
|
||||
public func setTrackErrors(_ maxErrors: Int) -> Parser {
|
||||
self._maxErrors = maxErrors
|
||||
return self
|
||||
}
|
||||
|
@ -79,17 +77,17 @@ public class Parser
|
|||
* Retrieve the parse errors, if any, from the last parse.
|
||||
* @return list of parse errors, up to the size of the maximum errors tracked.
|
||||
*/
|
||||
public func getErrors()->ParseErrorList {
|
||||
public func getErrors() -> ParseErrorList {
|
||||
return _errors
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func settings(_ settings: ParseSettings)->Parser {
|
||||
public func settings(_ settings: ParseSettings) -> Parser {
|
||||
self._settings = settings
|
||||
return self
|
||||
}
|
||||
|
||||
public func settings()->ParseSettings {
|
||||
public func settings() -> ParseSettings {
|
||||
return _settings
|
||||
}
|
||||
|
||||
|
@ -144,15 +142,13 @@ public class Parser
|
|||
*/
|
||||
public static func parseBodyFragment(_ bodyHtml: String, _ baseUri: String)throws->Document {
|
||||
let doc: Document = Document.createShell(baseUri)
|
||||
if let body: Element = doc.body()
|
||||
{
|
||||
if let body: Element = doc.body() {
|
||||
let nodeList: Array<Node> = try parseFragment(bodyHtml, body, baseUri)
|
||||
//var nodes: [Node] = nodeList.toArray(Node[nodeList.size()]) // the node list gets modified when re-parented
|
||||
for i in (nodeList.count - 1)...1
|
||||
{
|
||||
for i in (nodeList.count - 1)...1 {
|
||||
try nodeList[i].remove()
|
||||
}
|
||||
for node:Node in nodeList {
|
||||
for node: Node in nodeList {
|
||||
try body.appendChild(node)
|
||||
}
|
||||
}
|
||||
|
@ -188,7 +184,7 @@ public class Parser
|
|||
* based on a knowledge of the semantics of the incoming tags.
|
||||
* @return a new HTML parser.
|
||||
*/
|
||||
public static func htmlParser()->Parser {
|
||||
public static func htmlParser() -> Parser {
|
||||
return Parser(HtmlTreeBuilder())
|
||||
}
|
||||
|
||||
|
@ -197,7 +193,7 @@ public class Parser
|
|||
* rather creates a simple tree directly from the input.
|
||||
* @return a new simple XML parser.
|
||||
*/
|
||||
public static func xmlParser()->Parser {
|
||||
public static func xmlParser() -> Parser {
|
||||
return Parser(XmlTreeBuilder())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,17 +24,14 @@ public struct Pattern {
|
|||
self.pattern = pattern
|
||||
}
|
||||
|
||||
static public func compile(_ s: String)->Pattern
|
||||
{
|
||||
static public func compile(_ s: String) -> Pattern {
|
||||
return Pattern(s)
|
||||
}
|
||||
static public func compile(_ s: String, _ op: Int)->Pattern
|
||||
{
|
||||
static public func compile(_ s: String, _ op: Int) -> Pattern {
|
||||
return Pattern(s)
|
||||
}
|
||||
|
||||
func validate()throws
|
||||
{
|
||||
func validate()throws {
|
||||
_ = try NCRegularExpression(pattern: self.pattern, options:[])
|
||||
}
|
||||
|
||||
|
@ -42,55 +39,49 @@ public struct Pattern {
|
|||
do {
|
||||
let regex = try NCRegularExpression(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)
|
||||
return Matcher(results, text)
|
||||
} catch let error {
|
||||
print("invalid regex: \(error.localizedDescription)")
|
||||
return Matcher([],text)
|
||||
return Matcher([], text)
|
||||
}
|
||||
}
|
||||
|
||||
public func toString()->String{
|
||||
public func toString() -> String {
|
||||
return pattern
|
||||
}
|
||||
}
|
||||
|
||||
public class Matcher
|
||||
{
|
||||
let matches :[NCTextCheckingResult]
|
||||
let string : String
|
||||
var index : Int = -1
|
||||
public class Matcher {
|
||||
let matches: [NCTextCheckingResult]
|
||||
let string: String
|
||||
var index: Int = -1
|
||||
|
||||
public var count : Int { return matches.count}
|
||||
public var count: Int { return matches.count}
|
||||
|
||||
init(_ m:[NCTextCheckingResult],_ s: String)
|
||||
{
|
||||
init(_ m: [NCTextCheckingResult], _ s: String) {
|
||||
matches = m
|
||||
string = s
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func find() -> Bool
|
||||
{
|
||||
public func find() -> Bool {
|
||||
index += 1
|
||||
if(index < matches.count)
|
||||
{
|
||||
if(index < matches.count) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public func group(_ i: Int) -> String?
|
||||
{
|
||||
public func group(_ i: Int) -> String? {
|
||||
let b = matches[index]
|
||||
let c = b.range(at:i)
|
||||
if(c.location == NSNotFound) {return nil}
|
||||
let result = string.substring(c.location,c.length)
|
||||
let result = string.substring(c.location, c.length)
|
||||
return result
|
||||
}
|
||||
public func group() -> String?
|
||||
{
|
||||
public func group() -> String? {
|
||||
return group(0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@ import Foundation
|
|||
* Parses a CSS selector into an Evaluator tree.
|
||||
*/
|
||||
public class QueryParser {
|
||||
private static let combinators : [String] = [",", ">", "+", "~", " "]
|
||||
private static let AttributeEvals : [String] = ["=", "!=", "^=", "$=", "*=", "~="]
|
||||
private static let combinators: [String] = [",", ">", "+", "~", " "]
|
||||
private static let AttributeEvals: [String] = ["=", "!=", "^=", "$=", "*=", "~="]
|
||||
|
||||
private var tq: TokenQueue
|
||||
private var query: String
|
||||
|
@ -65,7 +65,7 @@ public class QueryParser {
|
|||
}
|
||||
}
|
||||
|
||||
if (evals.count == 1){
|
||||
if (evals.count == 1) {
|
||||
return evals[0]
|
||||
}
|
||||
return CombiningEvaluator.And(evals)
|
||||
|
@ -88,24 +88,15 @@ public class QueryParser {
|
|||
currentEval = (currentEval as! CombiningEvaluator.Or).rightMostEvaluator()
|
||||
replaceRightMost = true
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
currentEval = CombiningEvaluator.And(evals)
|
||||
rootEval = currentEval
|
||||
}
|
||||
evals.removeAll()
|
||||
|
||||
// for most combinators: change the current eval into an AND of the current eval and the new eval
|
||||
if (combinator == ">")
|
||||
{currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.ImmediateParent(currentEval!))}
|
||||
else if (combinator == " ")
|
||||
{currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.Parent(currentEval!))}
|
||||
else if (combinator == "+")
|
||||
{currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.ImmediatePreviousSibling(currentEval!))}
|
||||
else if (combinator == "~")
|
||||
{currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.PreviousSibling(currentEval!))}
|
||||
else if (combinator == ",") { // group or.
|
||||
let or : CombiningEvaluator.Or
|
||||
if (combinator == ">") {currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.ImmediateParent(currentEval!))} else if (combinator == " ") {currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.Parent(currentEval!))} else if (combinator == "+") {currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.ImmediatePreviousSibling(currentEval!))} else if (combinator == "~") {currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.PreviousSibling(currentEval!))} else if (combinator == ",") { // group or.
|
||||
let or: CombiningEvaluator.Or
|
||||
if ((currentEval as? CombiningEvaluator.Or) != nil) {
|
||||
or = currentEval as! CombiningEvaluator.Or
|
||||
or.add(newEval)
|
||||
|
@ -115,33 +106,28 @@ public class QueryParser {
|
|||
or.add(newEval)
|
||||
}
|
||||
currentEval = or
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
throw Exception.Error(type: ExceptionType.SelectorParseException, Message: "Unknown combinator: \(String(combinator))")
|
||||
}
|
||||
|
||||
|
||||
if (replaceRightMost)
|
||||
{
|
||||
if (replaceRightMost) {
|
||||
(rootEval as! CombiningEvaluator.Or).replaceRightMostEvaluator(currentEval!)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
rootEval = currentEval
|
||||
}
|
||||
evals.append(rootEval!)
|
||||
}
|
||||
|
||||
|
||||
private func consumeSubQuery()->String {
|
||||
let sq : StringBuilder = StringBuilder()
|
||||
private func consumeSubQuery() -> String {
|
||||
let sq: StringBuilder = StringBuilder()
|
||||
while (!tq.isEmpty()) {
|
||||
if (tq.matches("(")){
|
||||
if (tq.matches("(")) {
|
||||
sq.append("(").append(tq.chompBalanced("(", ")")).append(")")
|
||||
}else if (tq.matches("[")){
|
||||
} else if (tq.matches("[")) {
|
||||
sq.append("[").append(tq.chompBalanced("[", "]")).append("]")
|
||||
}else if (tq.matchesAny(QueryParser.combinators)){
|
||||
} else if (tq.matchesAny(QueryParser.combinators)) {
|
||||
break
|
||||
}else{
|
||||
} else {
|
||||
sq.append(tq.consume())
|
||||
}
|
||||
}
|
||||
|
@ -149,61 +135,10 @@ public class QueryParser {
|
|||
}
|
||||
|
||||
private func findElements()throws {
|
||||
if (tq.matchChomp("#"))
|
||||
{
|
||||
if (tq.matchChomp("#")) {
|
||||
try byId()
|
||||
}else if (tq.matchChomp("."))
|
||||
{
|
||||
try byClass()}
|
||||
else if (tq.matchesWord() || tq.matches("*|"))
|
||||
{try byTag()}
|
||||
else if (tq.matches("["))
|
||||
{try byAttribute()}
|
||||
else if (tq.matchChomp("*"))
|
||||
{ allElements()}
|
||||
else if (tq.matchChomp(":lt("))
|
||||
{try indexLessThan()}
|
||||
else if (tq.matchChomp(":gt("))
|
||||
{try indexGreaterThan()}
|
||||
else if (tq.matchChomp(":eq("))
|
||||
{try indexEquals()}
|
||||
else if (tq.matches(":has("))
|
||||
{try has()}
|
||||
else if (tq.matches(":contains("))
|
||||
{try contains(false)}
|
||||
else if (tq.matches(":containsOwn("))
|
||||
{try contains(true)}
|
||||
else if (tq.matches(":matches("))
|
||||
{try matches(false)}
|
||||
else if (tq.matches(":matchesOwn("))
|
||||
{try matches(true)}
|
||||
else if (tq.matches(":not("))
|
||||
{try not()}
|
||||
else if (tq.matchChomp(":nth-child("))
|
||||
{try cssNthChild(false, false)}
|
||||
else if (tq.matchChomp(":nth-last-child("))
|
||||
{try cssNthChild(true, false)}
|
||||
else if (tq.matchChomp(":nth-of-type("))
|
||||
{try cssNthChild(false, true)}
|
||||
else if (tq.matchChomp(":nth-last-of-type("))
|
||||
{try cssNthChild(true, true)}
|
||||
else if (tq.matchChomp(":first-child"))
|
||||
{evals.append(Evaluator.IsFirstChild())}
|
||||
else if (tq.matchChomp(":last-child"))
|
||||
{evals.append(Evaluator.IsLastChild())}
|
||||
else if (tq.matchChomp(":first-of-type"))
|
||||
{evals.append(Evaluator.IsFirstOfType())}
|
||||
else if (tq.matchChomp(":last-of-type"))
|
||||
{evals.append(Evaluator.IsLastOfType())}
|
||||
else if (tq.matchChomp(":only-child"))
|
||||
{evals.append(Evaluator.IsOnlyChild())}
|
||||
else if (tq.matchChomp(":only-of-type"))
|
||||
{evals.append(Evaluator.IsOnlyOfType())}
|
||||
else if (tq.matchChomp(":empty"))
|
||||
{evals.append(Evaluator.IsEmpty())}
|
||||
else if (tq.matchChomp(":root"))
|
||||
{evals.append(Evaluator.IsRoot())}
|
||||
else // unhandled
|
||||
} 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())")
|
||||
}
|
||||
|
@ -234,7 +169,7 @@ public class QueryParser {
|
|||
Evaluator.TagEndsWith(tagName.replacingOccurrences(of: "*|", with: ":").trim().lowercased())))
|
||||
} else {
|
||||
// namespaces: if element name is "abc:def", selector must be "abc|def", so flip:
|
||||
if (tagName.contains("|")){
|
||||
if (tagName.contains("|")) {
|
||||
tagName = tagName.replacingOccurrences(of: "|", with: ":")
|
||||
}
|
||||
|
||||
|
@ -249,35 +184,25 @@ public class QueryParser {
|
|||
cq.consumeWhitespace()
|
||||
|
||||
if (cq.isEmpty()) {
|
||||
if (key.startsWith("^")){
|
||||
if (key.startsWith("^")) {
|
||||
evals.append(try Evaluator.AttributeStarting(key.substring(1)))
|
||||
}else{
|
||||
} else {
|
||||
evals.append(Evaluator.Attribute(key))
|
||||
}
|
||||
} else {
|
||||
if (cq.matchChomp("=")){
|
||||
if (cq.matchChomp("=")) {
|
||||
evals.append(try Evaluator.AttributeWithValue(key, cq.remainder()))
|
||||
}
|
||||
|
||||
else if (cq.matchChomp("!=")){
|
||||
} else if (cq.matchChomp("!=")) {
|
||||
evals.append(try Evaluator.AttributeWithValueNot(key, cq.remainder()))
|
||||
}
|
||||
|
||||
else if (cq.matchChomp("^=")){
|
||||
} else if (cq.matchChomp("^=")) {
|
||||
evals.append(try Evaluator.AttributeWithValueStarting(key, cq.remainder()))
|
||||
}
|
||||
|
||||
else if (cq.matchChomp("$=")){
|
||||
} else if (cq.matchChomp("$=")) {
|
||||
evals.append(try Evaluator.AttributeWithValueEnding(key, cq.remainder()))
|
||||
}
|
||||
|
||||
else if (cq.matchChomp("*=")){
|
||||
} else if (cq.matchChomp("*=")) {
|
||||
evals.append(try Evaluator.AttributeWithValueContaining(key, cq.remainder()))
|
||||
}
|
||||
|
||||
else if (cq.matchChomp("~=")){
|
||||
} else if (cq.matchChomp("~=")) {
|
||||
evals.append( Evaluator.AttributeWithValueMatching(key, Pattern.compile(cq.remainder())))
|
||||
}else{
|
||||
} else {
|
||||
throw Exception.Error(type: ExceptionType.SelectorParseException, Message:"Could not parse attribute query '\(query)': unexpected token at '\(cq.remainder())'")
|
||||
}
|
||||
}
|
||||
|
@ -327,26 +252,21 @@ public class QueryParser {
|
|||
} else {
|
||||
throw Exception.Error(type: ExceptionType.SelectorParseException, Message:"Could not parse nth-index '\(argS)': unexpected format")
|
||||
}
|
||||
if (ofType){
|
||||
if (backwards){
|
||||
if (ofType) {
|
||||
if (backwards) {
|
||||
evals.append(Evaluator.IsNthLastOfType(a, b))
|
||||
}else{
|
||||
} else {
|
||||
evals.append(Evaluator.IsNthOfType(a, b))
|
||||
}
|
||||
}else {
|
||||
if (backwards){
|
||||
} else {
|
||||
if (backwards) {
|
||||
evals.append(Evaluator.IsNthLastChild(a, b))
|
||||
}else{
|
||||
} else {
|
||||
evals.append(Evaluator.IsNthChild(a, b))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private func consumeIndex()throws->Int {
|
||||
let indexS: String = tq.chompTo(")").trim()
|
||||
try Validate.isTrue(val: StringUtil.isNumeric(indexS), msg: "Index must be numeric")
|
||||
|
@ -366,9 +286,9 @@ public class QueryParser {
|
|||
try tq.consume(own ? ":containsOwn" : ":contains")
|
||||
let searchText: String = TokenQueue.unescape(tq.chompBalanced("(", ")"))
|
||||
try Validate.notEmpty(string: searchText, msg: ":contains(text) query must not be empty")
|
||||
if (own){
|
||||
if (own) {
|
||||
evals.append(Evaluator.ContainsOwnText(searchText))
|
||||
}else{
|
||||
} else {
|
||||
evals.append(Evaluator.ContainsText(searchText))
|
||||
}
|
||||
}
|
||||
|
@ -379,9 +299,9 @@ public class QueryParser {
|
|||
let regex: String = tq.chompBalanced("(", ")") // don't unescape, as regex bits will be escaped
|
||||
try Validate.notEmpty(string: regex, msg: ":matches(regex) query must not be empty")
|
||||
|
||||
if (own){
|
||||
if (own) {
|
||||
evals.append(Evaluator.MatchesOwn(Pattern.compile(regex)))
|
||||
}else{
|
||||
} else {
|
||||
evals.append(Evaluator.Matches(Pattern.compile(regex)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,8 +73,8 @@ import Foundation
|
|||
* @see Element#select(String)
|
||||
*/
|
||||
open class Selector {
|
||||
private let evaluator : Evaluator
|
||||
private let root : Element
|
||||
private let evaluator: Evaluator
|
||||
private let root: Element
|
||||
|
||||
private init(_ query: String, _ root: Element)throws {
|
||||
let query = query.trim()
|
||||
|
@ -127,11 +127,9 @@ open class Selector {
|
|||
var seenElements: Array<Element> = Array<Element>()
|
||||
// dedupe elements by identity, not equality
|
||||
|
||||
for root: Element in roots
|
||||
{
|
||||
for root: Element in roots {
|
||||
let found: Elements = try select(evaluator, root)
|
||||
for el: Element in found.array()
|
||||
{
|
||||
for el: Element in found.array() {
|
||||
if (!seenElements.contains(el)) {
|
||||
elements.append(el)
|
||||
seenElements.append(el)
|
||||
|
@ -146,7 +144,7 @@ open class Selector {
|
|||
}
|
||||
|
||||
// exclude set. package open so that Elements can implement .not() selector.
|
||||
static func filterOut(_ elements: Array<Element>, _ outs: Array<Element>)->Elements {
|
||||
static func filterOut(_ elements: Array<Element>, _ outs: Array<Element>) -> Elements {
|
||||
let output: Elements = Elements()
|
||||
for el: Element in elements {
|
||||
var found: Bool = false
|
||||
|
@ -156,7 +154,7 @@ open class Selector {
|
|||
break
|
||||
}
|
||||
}
|
||||
if (!found){
|
||||
if (!found) {
|
||||
output.add(el)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
public class SimpleDictionary<KeyType: Hashable, ValueType> {
|
||||
|
||||
public typealias DictionaryType = [KeyType: ValueType]
|
||||
|
@ -25,17 +24,15 @@ public class SimpleDictionary<KeyType: Hashable, ValueType> {
|
|||
values.removeValue(forKey: key)
|
||||
}
|
||||
|
||||
|
||||
public func contains(_ key: KeyType) -> Bool {
|
||||
return self.values[key] != nil
|
||||
}
|
||||
|
||||
|
||||
public func put(_ value: ValueType, forKey key: KeyType) {
|
||||
self.values[key] = value
|
||||
}
|
||||
|
||||
public func get(_ key: KeyType)->ValueType?{
|
||||
public func get(_ key: KeyType) -> ValueType? {
|
||||
return self.values[key]
|
||||
}
|
||||
|
||||
|
|
|
@ -10,12 +10,12 @@ import Foundation
|
|||
|
||||
class StreamReader {
|
||||
|
||||
let encoding : String.Encoding
|
||||
let chunkSize : Int
|
||||
var fileHandle : FileHandle!
|
||||
let delimData : Data
|
||||
var buffer : Data
|
||||
var atEof : Bool
|
||||
let encoding: String.Encoding
|
||||
let chunkSize: Int
|
||||
var fileHandle: FileHandle!
|
||||
let delimData: Data
|
||||
var buffer: Data
|
||||
var atEof: Bool
|
||||
|
||||
init?(path: String, delimiter: String = "\n", encoding: String.Encoding = .utf8,
|
||||
chunkSize: Int = 4096) {
|
||||
|
|
|
@ -18,20 +18,16 @@ extension String {
|
|||
return String(self[i] as Character)
|
||||
}
|
||||
|
||||
|
||||
func unicodeScalar(_ i: Int)->UnicodeScalar{
|
||||
func unicodeScalar(_ i: Int) -> UnicodeScalar {
|
||||
return self.unicodeScalars.prefix(i+1).last!
|
||||
}
|
||||
|
||||
|
||||
func string(_ offset: Int, _ count: Int)->String{
|
||||
func string(_ offset: Int, _ count: Int) -> String {
|
||||
let truncStart = self.unicodeScalars.count-offset
|
||||
return String(self.unicodeScalars.suffix(truncStart).prefix(count))
|
||||
}
|
||||
|
||||
|
||||
static func split(_ value: String,_ offset : Int, _ count: Int) -> String
|
||||
{
|
||||
static func split(_ value: String, _ offset: Int, _ count: Int) -> String {
|
||||
let start = value.index(value.startIndex, offsetBy: offset)
|
||||
let end = value.index(value.startIndex, offsetBy: count+offset)
|
||||
let range = start..<end
|
||||
|
@ -46,17 +42,15 @@ extension String {
|
|||
return (self.trimmingCharacters(in: CharacterSet.whitespaces) == "")
|
||||
}
|
||||
|
||||
func startsWith(_ string:String) -> Bool
|
||||
{
|
||||
func startsWith(_ string: String) -> Bool {
|
||||
return self.hasPrefix(string)
|
||||
}
|
||||
|
||||
func indexOf(_ substring: String, _ offset: Int ) -> Int {
|
||||
if(offset > characters.count){return -1}
|
||||
if(offset > characters.count) {return -1}
|
||||
|
||||
let maxIndex = self.characters.count - substring.characters.count
|
||||
if(maxIndex >= 0)
|
||||
{
|
||||
if(maxIndex >= 0) {
|
||||
for index in offset...maxIndex {
|
||||
let rangeSubstring = self.characters.index(self.startIndex, offsetBy: index)..<self.characters.index(self.startIndex, offsetBy: index + substring.characters.count)
|
||||
if self.substring(with: rangeSubstring) == substring {
|
||||
|
@ -71,31 +65,28 @@ extension String {
|
|||
return self.indexOf(substring, 0)
|
||||
}
|
||||
|
||||
|
||||
|
||||
func trim() -> String {
|
||||
return trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines)
|
||||
}
|
||||
|
||||
func equalsIgnoreCase(string:String?)->Bool
|
||||
{
|
||||
if(string == nil){return false}
|
||||
func equalsIgnoreCase(string: String?) -> Bool {
|
||||
if(string == nil) {return false}
|
||||
return caseInsensitiveCompare(string!) == ComparisonResult.orderedSame
|
||||
}
|
||||
|
||||
static func toHexString(n:Int)->String{
|
||||
static func toHexString(n: Int) -> String {
|
||||
return String(format:"%2x", n)
|
||||
}
|
||||
|
||||
func toCharArray() -> [Int] {
|
||||
return characters.flatMap{Int($0.unicodeScalar.value)}
|
||||
return characters.flatMap {Int($0.unicodeScalar.value)}
|
||||
}
|
||||
|
||||
func insert(string:String,ind:Int) -> String {
|
||||
func insert(string: String, ind: Int) -> String {
|
||||
return String(self.characters.prefix(ind)) + string + String(self.characters.suffix(self.characters.count-ind))
|
||||
}
|
||||
|
||||
func charAt(_ i:Int) -> Character {
|
||||
func charAt(_ i: Int) -> Character {
|
||||
return self[i] as Character
|
||||
}
|
||||
|
||||
|
@ -107,23 +98,22 @@ extension String {
|
|||
return String.split(self, beginIndex, count)
|
||||
}
|
||||
|
||||
func regionMatches(_ ignoreCase: Bool, _ selfOffset: Int, _ other: String, _ otherOffset: Int, _ length: Int )->Bool{
|
||||
func regionMatches(_ ignoreCase: Bool, _ selfOffset: Int, _ other: String, _ otherOffset: Int, _ length: Int ) -> Bool {
|
||||
if ((otherOffset < 0) || (selfOffset < 0)
|
||||
|| (selfOffset > self.characters.count - length)
|
||||
|| (otherOffset > other.characters.count - length)) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i in 0..<length
|
||||
{
|
||||
let charSelf : Character = self[i+selfOffset]
|
||||
let charOther : Character = other[i+otherOffset]
|
||||
if(ignoreCase){
|
||||
if(charSelf.lowercase != charOther.lowercase){
|
||||
for i in 0..<length {
|
||||
let charSelf: Character = self[i+selfOffset]
|
||||
let charOther: Character = other[i+otherOffset]
|
||||
if(ignoreCase) {
|
||||
if(charSelf.lowercase != charOther.lowercase) {
|
||||
return false
|
||||
}
|
||||
}else{
|
||||
if(charSelf != charOther){
|
||||
} else {
|
||||
if(charSelf != charOther) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -131,42 +121,40 @@ extension String {
|
|||
return true
|
||||
}
|
||||
|
||||
func startsWith(_ input: String , _ offset: Int)->Bool
|
||||
{
|
||||
func startsWith(_ input: String, _ offset: Int) -> Bool {
|
||||
if ((offset < 0) || (offset > characters.count - input.characters.count)) {
|
||||
return false
|
||||
}
|
||||
for i in 0..<input.characters.count
|
||||
{
|
||||
let charSelf : Character = self[i+offset]
|
||||
let charOther : Character = input[i]
|
||||
if(charSelf != charOther){return false}
|
||||
for i in 0..<input.characters.count {
|
||||
let charSelf: Character = self[i+offset]
|
||||
let charOther: Character = input[i]
|
||||
if(charSelf != charOther) {return false}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func replaceFirst(of pattern:String,with replacement:String) -> String {
|
||||
if let range = self.range(of: pattern){
|
||||
func replaceFirst(of pattern: String, with replacement: String) -> String {
|
||||
if let range = self.range(of: pattern) {
|
||||
return self.replacingCharacters(in: range, with: replacement)
|
||||
}else{
|
||||
} else {
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
func replaceAll(of pattern:String,with replacement:String,options: NCRegularExpression.Options = []) -> String{
|
||||
do{
|
||||
func replaceAll(of pattern: String, with replacement: String, options: NCRegularExpression.Options = []) -> String {
|
||||
do {
|
||||
let regex = try NCRegularExpression(pattern: pattern, options: [])
|
||||
let range = NSRange(0..<self.utf16.count)
|
||||
return regex.stringByReplacingMatches(in: self, options: [],
|
||||
range: range, withTemplate: replacement)
|
||||
}catch{
|
||||
} catch {
|
||||
NSLog("replaceAll error: \(error)")
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
func equals(_ s: String?)->Bool{
|
||||
if(s == nil){return false}
|
||||
func equals(_ s: String?) -> Bool {
|
||||
if(s == nil) {return false}
|
||||
return self == s!
|
||||
}
|
||||
|
||||
|
@ -176,15 +164,12 @@ extension String {
|
|||
|
||||
}
|
||||
|
||||
|
||||
extension String.Encoding
|
||||
{
|
||||
func canEncode(_ string: String) -> Bool
|
||||
{
|
||||
extension String.Encoding {
|
||||
func canEncode(_ string: String) -> Bool {
|
||||
return string.cString(using: self) != nil
|
||||
}
|
||||
|
||||
public func displayName()->String{
|
||||
public func displayName() -> String {
|
||||
switch self {
|
||||
case String.Encoding.ascii: return "US-ASCII"
|
||||
case String.Encoding.nextstep: return "nextstep"
|
||||
|
@ -214,9 +199,3 @@ extension String.Encoding
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -46,13 +46,12 @@ open class StringBuilder {
|
|||
stringValue += string
|
||||
}
|
||||
|
||||
|
||||
open func appendCodePoint(_ chr: Character) {
|
||||
stringValue = stringValue + String(chr)
|
||||
}
|
||||
|
||||
open func appendCodePoints(_ chr: [Character]) {
|
||||
for c in chr{
|
||||
for c in chr {
|
||||
stringValue = stringValue + String(c)
|
||||
}
|
||||
}
|
||||
|
@ -66,12 +65,11 @@ open class StringBuilder {
|
|||
}
|
||||
|
||||
open func appendCodePoints(_ chr: [UnicodeScalar]) {
|
||||
for c in chr{
|
||||
for c in chr {
|
||||
stringValue = stringValue + String(c)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Append a Printable to the object
|
||||
|
||||
|
@ -86,7 +84,7 @@ open class StringBuilder {
|
|||
}
|
||||
|
||||
@discardableResult
|
||||
open func insert<T: CustomStringConvertible>(_ offset: Int ,_ value: T) -> StringBuilder {
|
||||
open func insert<T: CustomStringConvertible>(_ offset: Int, _ value: T) -> StringBuilder {
|
||||
stringValue = stringValue.insert(string: value.description, ind: offset)
|
||||
return self
|
||||
}
|
||||
|
@ -160,4 +158,3 @@ public func += <T: CustomStringConvertible>(lhs: StringBuilder, rhs: T) {
|
|||
public func +(lhs: StringBuilder, rhs: StringBuilder) -> StringBuilder {
|
||||
return StringBuilder(string: lhs.toString() + rhs.toString())
|
||||
}
|
||||
|
||||
|
|
|
@ -11,8 +11,7 @@ import Foundation
|
|||
/**
|
||||
* A minimal String utility class. Designed for internal jsoup use only.
|
||||
*/
|
||||
open class StringUtil
|
||||
{
|
||||
open class StringUtil {
|
||||
enum StringError: Error {
|
||||
case empty
|
||||
case short
|
||||
|
@ -20,7 +19,7 @@ open class StringUtil
|
|||
}
|
||||
|
||||
// memoised padding up to 10
|
||||
fileprivate static var padding : [String] = ["", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
|
||||
fileprivate static var padding: [String] = ["", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
|
||||
|
||||
/**
|
||||
* Join a collection of strings by a seperator
|
||||
|
@ -28,18 +27,17 @@ open class StringUtil
|
|||
* @param sep string to place between strings
|
||||
* @return joined string
|
||||
*/
|
||||
open static func join(_ strings:[String], sep:String) -> String {
|
||||
open static func join(_ strings: [String], sep: String) -> String {
|
||||
return strings.joined(separator: sep)
|
||||
}
|
||||
open static func join(_ strings:Set<String>, sep:String) -> String {
|
||||
open static func join(_ strings: Set<String>, sep: String) -> String {
|
||||
return strings.joined(separator: sep)
|
||||
}
|
||||
|
||||
open static func join(_ strings:OrderedSet<String>, sep:String) -> String {
|
||||
open static func join(_ strings: OrderedSet<String>, sep: String) -> String {
|
||||
return strings.joined(separator: sep)
|
||||
}
|
||||
|
||||
|
||||
// /**
|
||||
// * Join a collection of strings by a seperator
|
||||
// * @param strings iterator of string objects
|
||||
|
@ -66,20 +64,19 @@ open class StringUtil
|
|||
* @param width amount of padding desired
|
||||
* @return string of spaces * width
|
||||
*/
|
||||
open static func padding(_ width:Int) -> String{
|
||||
open static func padding(_ width: Int) -> String {
|
||||
|
||||
if(width <= 0){
|
||||
if(width <= 0) {
|
||||
return ""
|
||||
}
|
||||
|
||||
if (width < padding.count){
|
||||
if (width < padding.count) {
|
||||
return padding[width]
|
||||
}
|
||||
|
||||
var out: [Character] = [Character]()
|
||||
|
||||
for _ in 0..<width
|
||||
{
|
||||
for _ in 0..<width {
|
||||
out.append(" ")
|
||||
}
|
||||
return String(out)
|
||||
|
@ -90,14 +87,13 @@ open class StringUtil
|
|||
* @param string string to test
|
||||
* @return if string is blank
|
||||
*/
|
||||
open static func isBlank(_ string:String) -> Bool {
|
||||
if (string.characters.count == 0){
|
||||
open static func isBlank(_ string: String) -> Bool {
|
||||
if (string.characters.count == 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
for chr in string.characters
|
||||
{
|
||||
if (!StringUtil.isWhitespace(chr)){
|
||||
for chr in string.characters {
|
||||
if (!StringUtil.isWhitespace(chr)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -109,15 +105,13 @@ open class StringUtil
|
|||
* @param string string to test
|
||||
* @return true if only digit chars, false if empty or null or contains non-digit chrs
|
||||
*/
|
||||
open static func isNumeric(_ string:String) -> Bool {
|
||||
if (string.characters.count == 0){
|
||||
open static func isNumeric(_ string: String) -> Bool {
|
||||
if (string.characters.count == 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
for chr in string.characters
|
||||
{
|
||||
if !("0"..."9" ~= chr)
|
||||
{
|
||||
for chr in string.characters {
|
||||
if !("0"..."9" ~= chr) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -129,8 +123,7 @@ open class StringUtil
|
|||
* @param c code point to test
|
||||
* @return true if code point is whitespace, false otherwise
|
||||
*/
|
||||
open static func isWhitespace(_ c:Character) -> Bool
|
||||
{
|
||||
open static func isWhitespace(_ c: Character) -> Bool {
|
||||
//(c == " " || c == "\t" || c == "\n" || (c == "\f" ) || c == "\r")
|
||||
return c.isWhitespace
|
||||
}
|
||||
|
@ -141,8 +134,8 @@ open class StringUtil
|
|||
* @param string content to normalise
|
||||
* @return normalised string
|
||||
*/
|
||||
open static func normaliseWhitespace(_ string:String) -> String {
|
||||
let sb : StringBuilder = StringBuilder.init()
|
||||
open static func normaliseWhitespace(_ string: String) -> String {
|
||||
let sb: StringBuilder = StringBuilder.init()
|
||||
appendNormalisedWhitespace(sb, string: string, stripLeading: false)
|
||||
return sb.toString()
|
||||
}
|
||||
|
@ -153,22 +146,18 @@ open class StringUtil
|
|||
* @param string string to normalize whitespace within
|
||||
* @param stripLeading set to true if you wish to remove any leading whitespace
|
||||
*/
|
||||
open static func appendNormalisedWhitespace(_ accum:StringBuilder, string: String , stripLeading:Bool ) {
|
||||
var lastWasWhite : Bool = false
|
||||
open static func appendNormalisedWhitespace(_ accum: StringBuilder, string: String, stripLeading: Bool ) {
|
||||
var lastWasWhite: Bool = false
|
||||
var reachedNonWhite: Bool = false
|
||||
|
||||
for c in string.characters
|
||||
{
|
||||
if (isWhitespace(c))
|
||||
{
|
||||
if ((stripLeading && !reachedNonWhite) || lastWasWhite){
|
||||
for c in string.characters {
|
||||
if (isWhitespace(c)) {
|
||||
if ((stripLeading && !reachedNonWhite) || lastWasWhite) {
|
||||
continue
|
||||
}
|
||||
accum.append(" ")
|
||||
lastWasWhite = true
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
accum.appendCodePoint(c)
|
||||
lastWasWhite = false
|
||||
reachedNonWhite = true
|
||||
|
@ -176,25 +165,24 @@ open class StringUtil
|
|||
}
|
||||
}
|
||||
|
||||
open static func inString(_ needle:String? , haystack:String...) -> Bool {
|
||||
return inString(needle,haystack)
|
||||
open static func inString(_ needle: String?, haystack: String...) -> Bool {
|
||||
return inString(needle, haystack)
|
||||
}
|
||||
open static func inString(_ needle:String? , _ haystack:[String?]) -> Bool {
|
||||
if(needle == nil){return false}
|
||||
for hay in haystack
|
||||
{
|
||||
if(hay != nil && hay!.compare(needle!) == ComparisonResult.orderedSame){
|
||||
open static func inString(_ needle: String?, _ haystack: [String?]) -> Bool {
|
||||
if(needle == nil) {return false}
|
||||
for hay in haystack {
|
||||
if(hay != nil && hay!.compare(needle!) == ComparisonResult.orderedSame) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
open static func inSorted(_ needle:String, haystack:[String]) -> Bool {
|
||||
open static func inSorted(_ needle: String, haystack: [String]) -> Bool {
|
||||
return binarySearch(haystack, searchItem: needle) >= 0
|
||||
}
|
||||
|
||||
open static func binarySearch<T:Comparable>(_ inputArr:Array<T>, searchItem: T)->Int{
|
||||
open static func binarySearch<T: Comparable>(_ inputArr: Array<T>, searchItem: T) -> Int {
|
||||
var lowerIndex = 0
|
||||
var upperIndex = inputArr.count - 1
|
||||
|
||||
|
@ -222,10 +210,9 @@ open class StringUtil
|
|||
* @throws MalformedURLException if an error occurred generating the URL
|
||||
*/
|
||||
//NOTE: Not sure it work
|
||||
open static func resolve(_ base:URL, relUrl: String ) -> URL? {
|
||||
open static func resolve(_ base: URL, relUrl: String ) -> URL? {
|
||||
var base = base
|
||||
if(base.pathComponents.count == 0 && base.absoluteString.characters.last != "/" && !base.isFileURL)
|
||||
{
|
||||
if(base.pathComponents.count == 0 && base.absoluteString.characters.last != "/" && !base.isFileURL) {
|
||||
base = base.appendingPathComponent("/", isDirectory: false)
|
||||
}
|
||||
let u = URL(string: relUrl, relativeTo : base)
|
||||
|
@ -238,22 +225,21 @@ open class StringUtil
|
|||
* @param relUrl the relative URL to resolve. (If it's already absolute, it will be returned)
|
||||
* @return an absolute URL if one was able to be generated, or the empty string if not
|
||||
*/
|
||||
open static func resolve(_ baseUrl : String , relUrl : String ) -> String {
|
||||
open static func resolve(_ baseUrl: String, relUrl: String ) -> String {
|
||||
|
||||
let base = URL(string: baseUrl)
|
||||
|
||||
if(base == nil || base?.scheme == nil)
|
||||
{
|
||||
if(base == nil || base?.scheme == nil) {
|
||||
let abs = URL(string: relUrl)
|
||||
return abs != nil && abs?.scheme != nil ? abs!.absoluteURL.absoluteString : ""
|
||||
}else{
|
||||
} else {
|
||||
let url = resolve(base!, relUrl: relUrl)
|
||||
if(url != nil){
|
||||
if(url != nil) {
|
||||
let ext = url!.absoluteURL.absoluteString
|
||||
return ext
|
||||
}
|
||||
|
||||
if(base != nil && base?.scheme != nil){
|
||||
if(base != nil && base?.scheme != nil) {
|
||||
let ext = base!.absoluteString
|
||||
return ext
|
||||
}
|
||||
|
@ -277,15 +263,3 @@ open class StringUtil
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -11,83 +11,81 @@ import Foundation
|
|||
/**
|
||||
* Base structural evaluator.
|
||||
*/
|
||||
public class StructuralEvaluator : Evaluator {
|
||||
public class StructuralEvaluator: Evaluator {
|
||||
let evaluator: Evaluator
|
||||
|
||||
public init(_ evaluator: Evaluator) {
|
||||
self.evaluator = evaluator
|
||||
}
|
||||
|
||||
public class Root : Evaluator {
|
||||
public override func matches(_ root: Element, _ element: Element)->Bool {
|
||||
public class Root: Evaluator {
|
||||
public override func matches(_ root: Element, _ element: Element) -> Bool {
|
||||
return root === element
|
||||
}
|
||||
}
|
||||
|
||||
public class Has : StructuralEvaluator {
|
||||
public class Has: StructuralEvaluator {
|
||||
public override init(_ evaluator: Evaluator) {
|
||||
super.init(evaluator)
|
||||
}
|
||||
|
||||
public override func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||
for e in try element.getAllElements().array() {
|
||||
do{
|
||||
if(e != element){
|
||||
if ((try evaluator.matches(root, e)))
|
||||
{
|
||||
do {
|
||||
if(e != element) {
|
||||
if ((try evaluator.matches(root, e))) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}catch{}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
public override func toString()->String {
|
||||
public override func toString() -> String {
|
||||
return ":has(\(evaluator.toString()))"
|
||||
}
|
||||
}
|
||||
|
||||
public class Not : StructuralEvaluator {
|
||||
public class Not: StructuralEvaluator {
|
||||
public override init(_ evaluator: Evaluator) {
|
||||
super.init(evaluator)
|
||||
}
|
||||
|
||||
public override func matches(_ root: Element, _ node: Element)->Bool {
|
||||
do{
|
||||
public override func matches(_ root: Element, _ node: Element) -> Bool {
|
||||
do {
|
||||
return try !evaluator.matches(root, node)
|
||||
}catch{}
|
||||
} catch {}
|
||||
return false
|
||||
}
|
||||
|
||||
public override func toString()->String {
|
||||
public override func toString() -> String {
|
||||
return ":not\(evaluator.toString())"
|
||||
}
|
||||
}
|
||||
|
||||
public class Parent : StructuralEvaluator {
|
||||
public class Parent: StructuralEvaluator {
|
||||
public override init(_ evaluator: Evaluator) {
|
||||
super.init(evaluator)
|
||||
}
|
||||
|
||||
public override func matches(_ root: Element, _ element: Element)->Bool {
|
||||
if (root == element){
|
||||
public override func matches(_ root: Element, _ element: Element) -> Bool {
|
||||
if (root == element) {
|
||||
return false
|
||||
}
|
||||
|
||||
var parent = element.parent()
|
||||
while (true) {
|
||||
do{
|
||||
if parent != nil{
|
||||
if (try evaluator.matches(root, parent!)){
|
||||
do {
|
||||
if parent != nil {
|
||||
if (try evaluator.matches(root, parent!)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}catch{}
|
||||
} catch {}
|
||||
|
||||
if (parent == root){
|
||||
if (parent == root) {
|
||||
break
|
||||
}
|
||||
parent = parent?.parent()
|
||||
|
@ -95,83 +93,83 @@ public class StructuralEvaluator : Evaluator {
|
|||
return false
|
||||
}
|
||||
|
||||
public override func toString()->String {
|
||||
public override func toString() -> String {
|
||||
return ":parent\(evaluator.toString())"
|
||||
}
|
||||
}
|
||||
|
||||
public class ImmediateParent : StructuralEvaluator {
|
||||
public class ImmediateParent: StructuralEvaluator {
|
||||
public override init(_ evaluator: Evaluator) {
|
||||
super.init(evaluator)
|
||||
}
|
||||
|
||||
public override func matches(_ root: Element, _ element: Element)->Bool {
|
||||
if (root == element){
|
||||
public override func matches(_ root: Element, _ element: Element) -> Bool {
|
||||
if (root == element) {
|
||||
return false
|
||||
}
|
||||
|
||||
if let parent = element.parent(){
|
||||
do{
|
||||
if let parent = element.parent() {
|
||||
do {
|
||||
return try evaluator.matches(root, parent)
|
||||
}catch{}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
public override func toString()->String {
|
||||
public override func toString() -> String {
|
||||
return ":ImmediateParent\(evaluator.toString())"
|
||||
}
|
||||
}
|
||||
|
||||
public class PreviousSibling : StructuralEvaluator {
|
||||
public class PreviousSibling: StructuralEvaluator {
|
||||
public override init(_ evaluator: Evaluator) {
|
||||
super.init(evaluator)
|
||||
}
|
||||
|
||||
public override func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||
if (root == element){
|
||||
if (root == element) {
|
||||
return false
|
||||
}
|
||||
|
||||
var prev = try element.previousElementSibling()
|
||||
|
||||
while (prev != nil) {
|
||||
do{
|
||||
if (try evaluator.matches(root, prev!)){
|
||||
do {
|
||||
if (try evaluator.matches(root, prev!)) {
|
||||
return true
|
||||
}
|
||||
}catch{}
|
||||
} catch {}
|
||||
|
||||
prev = try prev!.previousElementSibling()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public override func toString()->String {
|
||||
public override func toString() -> String {
|
||||
return ":prev*\(evaluator.toString())"
|
||||
}
|
||||
}
|
||||
|
||||
class ImmediatePreviousSibling : StructuralEvaluator {
|
||||
class ImmediatePreviousSibling: StructuralEvaluator {
|
||||
public override init(_ evaluator: Evaluator) {
|
||||
super.init(evaluator)
|
||||
}
|
||||
|
||||
public override func matches(_ root: Element, _ element: Element)throws->Bool {
|
||||
if (root == element){
|
||||
if (root == element) {
|
||||
return false
|
||||
}
|
||||
|
||||
if let prev = try element.previousElementSibling(){
|
||||
do{
|
||||
if let prev = try element.previousElementSibling() {
|
||||
do {
|
||||
return try evaluator.matches(root, prev)
|
||||
}catch{}
|
||||
} catch {}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public override func toString()->String {
|
||||
public override func toString() -> String {
|
||||
return ":prev\(evaluator.toString())"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,12 +8,11 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
/**
|
||||
The core public access point to the jsoup functionality.
|
||||
*/
|
||||
open class SwiftSoup {
|
||||
private init(){}
|
||||
private init() {}
|
||||
|
||||
/**
|
||||
Parse HTML into a Document. The parser will make a sensible, balanced document tree out of any HTML.
|
||||
|
@ -234,5 +233,4 @@ open class SwiftSoup {
|
|||
return try clean.body()?.html()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -8,28 +8,27 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
open class Tag : Hashable
|
||||
{
|
||||
open class Tag: Hashable {
|
||||
// map of known tags
|
||||
static var tags: Dictionary<String, Tag> = {
|
||||
do{
|
||||
do {
|
||||
return try Tag.initializeMaps()
|
||||
}catch{
|
||||
} catch {
|
||||
preconditionFailure("This method must be overridden")
|
||||
}
|
||||
return Dictionary<String, Tag>()
|
||||
}()
|
||||
|
||||
fileprivate var _tagName : String
|
||||
fileprivate var _isBlock : Bool = true // block or inline
|
||||
fileprivate var _formatAsBlock : Bool = true // should be formatted as a block
|
||||
fileprivate var _canContainBlock : Bool = true // Can this tag hold block level tags?
|
||||
fileprivate var _canContainInline : Bool = true // only pcdata if not
|
||||
fileprivate var _empty : Bool = false // can hold nothing e.g. img
|
||||
fileprivate var _selfClosing : Bool = false // can self close (<foo />). used for unknown tags that self close, without forcing them as empty.
|
||||
fileprivate var _preserveWhitespace : Bool = false // for pre, textarea, script etc
|
||||
fileprivate var _formList : Bool = false // a control that appears in forms: input, textarea, output etc
|
||||
fileprivate var _formSubmit : Bool = false // a control that can be submitted in a form: input etc
|
||||
fileprivate var _tagName: String
|
||||
fileprivate var _isBlock: Bool = true // block or inline
|
||||
fileprivate var _formatAsBlock: Bool = true // should be formatted as a block
|
||||
fileprivate var _canContainBlock: Bool = true // Can this tag hold block level tags?
|
||||
fileprivate var _canContainInline: Bool = true // only pcdata if not
|
||||
fileprivate var _empty: Bool = false // can hold nothing e.g. img
|
||||
fileprivate var _selfClosing: Bool = false // can self close (<foo />). used for unknown tags that self close, without forcing them as empty.
|
||||
fileprivate var _preserveWhitespace: Bool = false // for pre, textarea, script etc
|
||||
fileprivate var _formList: Bool = false // a control that appears in forms: input, textarea, output etc
|
||||
fileprivate var _formSubmit: Bool = false // a control that can be submitted in a form: input etc
|
||||
|
||||
public init(_ tagName: String) {
|
||||
self._tagName = tagName
|
||||
|
@ -40,7 +39,7 @@ open class Tag : Hashable
|
|||
*
|
||||
* @return the tag's name
|
||||
*/
|
||||
open func getName()->String {
|
||||
open func getName() -> String {
|
||||
return self._tagName
|
||||
}
|
||||
|
||||
|
@ -56,7 +55,7 @@ open class Tag : Hashable
|
|||
*/
|
||||
open static func valueOf(_ tagName: String, _ settings: ParseSettings)throws->Tag {
|
||||
var tagName = tagName
|
||||
var tag : Tag? = Tag.tags[tagName]
|
||||
var tag: Tag? = Tag.tags[tagName]
|
||||
|
||||
if (tag == nil) {
|
||||
tagName = settings.normalizeTag(tagName)
|
||||
|
@ -91,7 +90,7 @@ open class Tag : Hashable
|
|||
*
|
||||
* @return if block tag
|
||||
*/
|
||||
open func isBlock()->Bool {
|
||||
open func isBlock() -> Bool {
|
||||
return _isBlock
|
||||
}
|
||||
|
||||
|
@ -100,7 +99,7 @@ open class Tag : Hashable
|
|||
*
|
||||
* @return if should be formatted as block or inline
|
||||
*/
|
||||
open func formatAsBlock()->Bool {
|
||||
open func formatAsBlock() -> Bool {
|
||||
return _formatAsBlock
|
||||
}
|
||||
|
||||
|
@ -109,7 +108,7 @@ open class Tag : Hashable
|
|||
*
|
||||
* @return if tag can contain block tags
|
||||
*/
|
||||
open func canContainBlock()->Bool {
|
||||
open func canContainBlock() -> Bool {
|
||||
return _canContainBlock
|
||||
}
|
||||
|
||||
|
@ -118,7 +117,7 @@ open class Tag : Hashable
|
|||
*
|
||||
* @return if this tag is an inline tag.
|
||||
*/
|
||||
open func isInline()->Bool {
|
||||
open func isInline() -> Bool {
|
||||
return !_isBlock
|
||||
}
|
||||
|
||||
|
@ -127,7 +126,7 @@ open class Tag : Hashable
|
|||
*
|
||||
* @return if this tag is a data only tag
|
||||
*/
|
||||
open func isData()->Bool {
|
||||
open func isData() -> Bool {
|
||||
return !_canContainInline && !isEmpty()
|
||||
}
|
||||
|
||||
|
@ -136,7 +135,7 @@ open class Tag : Hashable
|
|||
*
|
||||
* @return if this is an empty tag
|
||||
*/
|
||||
open func isEmpty()->Bool {
|
||||
open func isEmpty() -> Bool {
|
||||
return _empty
|
||||
}
|
||||
|
||||
|
@ -145,7 +144,7 @@ open class Tag : Hashable
|
|||
*
|
||||
* @return if this tag should be output as self closing.
|
||||
*/
|
||||
open func isSelfClosing()->Bool {
|
||||
open func isSelfClosing() -> Bool {
|
||||
return _empty || _selfClosing
|
||||
}
|
||||
|
||||
|
@ -154,7 +153,7 @@ open class Tag : Hashable
|
|||
*
|
||||
* @return if a known tag
|
||||
*/
|
||||
open func isKnownTag()->Bool {
|
||||
open func isKnownTag() -> Bool {
|
||||
return Tag.tags[_tagName] != nil
|
||||
}
|
||||
|
||||
|
@ -164,7 +163,7 @@ open class Tag : Hashable
|
|||
* @param tagName name of tag
|
||||
* @return if known HTML tag
|
||||
*/
|
||||
open static func isKnownTag(_ tagName: String)->Bool {
|
||||
open static func isKnownTag(_ tagName: String) -> Bool {
|
||||
return Tag.tags[tagName] != nil
|
||||
}
|
||||
|
||||
|
@ -173,7 +172,7 @@ open class Tag : Hashable
|
|||
*
|
||||
* @return if preserve whitepace
|
||||
*/
|
||||
public func preserveWhitespace()->Bool {
|
||||
public func preserveWhitespace() -> Bool {
|
||||
return _preserveWhitespace
|
||||
}
|
||||
|
||||
|
@ -181,7 +180,7 @@ open class Tag : Hashable
|
|||
* Get if this tag represents a control associated with a form. E.g. input, textarea, output
|
||||
* @return if associated with a form
|
||||
*/
|
||||
public func isFormListed()->Bool {
|
||||
public func isFormListed() -> Bool {
|
||||
return _formList
|
||||
}
|
||||
|
||||
|
@ -189,12 +188,12 @@ open class Tag : Hashable
|
|||
* Get if this tag represents an element that should be submitted with a form. E.g. input, option
|
||||
* @return if submittable with a form
|
||||
*/
|
||||
public func isFormSubmittable()->Bool {
|
||||
public func isFormSubmittable() -> Bool {
|
||||
return _formSubmit
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func setSelfClosing() ->Tag{
|
||||
func setSelfClosing() -> Tag {
|
||||
_selfClosing = true
|
||||
return self
|
||||
}
|
||||
|
@ -207,14 +206,13 @@ open class Tag : Hashable
|
|||
/// - Parameters:
|
||||
/// - lhs: A value to compare.
|
||||
/// - rhs: Another value to compare.
|
||||
static public func ==(lhs: Tag, rhs: Tag) -> Bool
|
||||
{
|
||||
static public func ==(lhs: Tag, rhs: Tag) -> Bool {
|
||||
let this = lhs
|
||||
let o = rhs
|
||||
if (this === o) {return true}
|
||||
if (type(of:this) != type(of:o)) {return false}
|
||||
|
||||
let tag : Tag = o
|
||||
let tag: Tag = o
|
||||
|
||||
if (lhs._tagName != tag._tagName) {return false}
|
||||
if (lhs._canContainBlock != tag._canContainBlock) {return false}
|
||||
|
@ -228,8 +226,7 @@ open class Tag : Hashable
|
|||
return lhs._formSubmit == tag._formSubmit
|
||||
}
|
||||
|
||||
public func equals(_ tag : Tag)->Bool
|
||||
{
|
||||
public func equals(_ tag: Tag) -> Bool {
|
||||
return self == tag
|
||||
}
|
||||
|
||||
|
@ -237,9 +234,8 @@ open class Tag : Hashable
|
|||
///
|
||||
/// Hash values are not guaranteed to be equal across different executions of
|
||||
/// your program. Do not save hash values to use during a future execution.
|
||||
public var hashValue: Int
|
||||
{
|
||||
var result : Int = _tagName.hashValue
|
||||
public var hashValue: Int {
|
||||
var result: Int = _tagName.hashValue
|
||||
result = Int.addWithOverflow(Int.multiplyWithOverflow(31, result).0, _isBlock ? 1 : 0).0
|
||||
result = Int.addWithOverflow(Int.multiplyWithOverflow(31, result).0, _formatAsBlock ? 1 : 0).0
|
||||
result = Int.addWithOverflow(Int.multiplyWithOverflow(31, result).0, _canContainBlock ? 1 : 0).0
|
||||
|
@ -252,14 +248,13 @@ open class Tag : Hashable
|
|||
return result
|
||||
}
|
||||
|
||||
|
||||
open func toString()->String {
|
||||
open func toString() -> String {
|
||||
return _tagName
|
||||
}
|
||||
|
||||
// internal static initialisers:
|
||||
// prepped from http://www.w3.org/TR/REC-html40/sgml/dtd.html and other sources
|
||||
private static let blockTags : [String] = [
|
||||
private static let blockTags: [String] = [
|
||||
"html", "head", "body", "frameset", "script", "noscript", "style", "meta", "link", "title", "frame",
|
||||
"noframes", "section", "nav", "aside", "hgroup", "header", "footer", "p", "h1", "h2", "h3", "h4", "h5", "h6",
|
||||
"ul", "ol", "pre", "div", "blockquote", "hr", "address", "figure", "figcaption", "form", "fieldset", "ins",
|
||||
|
@ -267,7 +262,7 @@ open class Tag : Hashable
|
|||
"td", "video", "audio", "canvas", "details", "menu", "plaintext", "template", "article", "main",
|
||||
"svg", "math"
|
||||
]
|
||||
private static let inlineTags : [String] = [
|
||||
private static let inlineTags: [String] = [
|
||||
"object", "base", "font", "tt", "i", "b", "u", "big", "small", "em", "strong", "dfn", "code", "samp", "kbd",
|
||||
"var", "cite", "abbr", "time", "acronym", "mark", "ruby", "rt", "rp", "a", "img", "br", "wbr", "map", "q",
|
||||
"sub", "sup", "bdo", "iframe", "embed", "span", "input", "select", "textarea", "label", "button", "optgroup",
|
||||
|
@ -275,29 +270,27 @@ open class Tag : Hashable
|
|||
"summary", "command", "device", "area", "basefont", "bgsound", "menuitem", "param", "source", "track",
|
||||
"data", "bdi"
|
||||
]
|
||||
private static let emptyTags : [String] = [
|
||||
private static let emptyTags: [String] = [
|
||||
"meta", "link", "base", "frame", "img", "br", "wbr", "embed", "hr", "input", "keygen", "col", "command",
|
||||
"device", "area", "basefont", "bgsound", "menuitem", "param", "source", "track"
|
||||
]
|
||||
private static let formatAsInlineTags : [String] = [
|
||||
private static let formatAsInlineTags: [String] = [
|
||||
"title", "a", "p", "h1", "h2", "h3", "h4", "h5", "h6", "pre", "address", "li", "th", "td", "script", "style",
|
||||
"ins", "del", "s"
|
||||
]
|
||||
private static let preserveWhitespaceTags : [String] = [
|
||||
private static let preserveWhitespaceTags: [String] = [
|
||||
"pre", "plaintext", "title", "textarea"
|
||||
// script is not here as it is a data node, which always preserve whitespace
|
||||
]
|
||||
// todo: I think we just need submit tags, and can scrub listed
|
||||
private static let formListedTags : [String] = [
|
||||
private static let formListedTags: [String] = [
|
||||
"button", "fieldset", "input", "keygen", "object", "output", "select", "textarea"
|
||||
]
|
||||
private static let formSubmitTags : [String] = [
|
||||
private static let formSubmitTags: [String] = [
|
||||
"input", "keygen", "object", "select", "textarea"
|
||||
]
|
||||
|
||||
|
||||
static private func initializeMaps()throws->Dictionary<String, Tag>
|
||||
{
|
||||
static private func initializeMaps()throws->Dictionary<String, Tag> {
|
||||
var dict = Dictionary<String, Tag>()
|
||||
|
||||
// creates
|
||||
|
@ -348,9 +341,3 @@ open class Tag : Hashable
|
|||
return dict
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -11,14 +11,14 @@ import Foundation
|
|||
/**
|
||||
A text node.
|
||||
*/
|
||||
open class TextNode : Node {
|
||||
open class TextNode: Node {
|
||||
/*
|
||||
TextNode is a node, and so by default comes with attributes and children. The attributes are seldom used, but use
|
||||
memory, and the child nodes are never used. So we don't have them, and override accessors to attributes to create
|
||||
them as needed on the fly.
|
||||
*/
|
||||
private static let TEXT_KEY : String = "text"
|
||||
var _text : String
|
||||
private static let TEXT_KEY: String = "text"
|
||||
var _text: String
|
||||
|
||||
/**
|
||||
Create a new TextNode representing the supplied (unencoded) text).
|
||||
|
@ -34,7 +34,7 @@ open class TextNode : Node {
|
|||
|
||||
}
|
||||
|
||||
open override func nodeName()->String {
|
||||
open override func nodeName() -> String {
|
||||
return "#text"
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ open class TextNode : Node {
|
|||
* @return Unencoded, normalised text.
|
||||
* @see TextNode#getWholeText()
|
||||
*/
|
||||
open func text()->String {
|
||||
open func text() -> String {
|
||||
return TextNode.normaliseWhitespace(getWholeText())
|
||||
}
|
||||
|
||||
|
@ -53,14 +53,14 @@ open class TextNode : Node {
|
|||
* @return this, for chaining
|
||||
*/
|
||||
@discardableResult
|
||||
public func text(_ text: String)->TextNode {
|
||||
public func text(_ text: String) -> TextNode {
|
||||
self._text = text
|
||||
guard let attributes = attributes else {
|
||||
return self
|
||||
}
|
||||
do{
|
||||
do {
|
||||
try attributes.put(TextNode.TEXT_KEY, text)
|
||||
}catch{
|
||||
} catch {
|
||||
|
||||
}
|
||||
return self
|
||||
|
@ -70,7 +70,7 @@ open class TextNode : Node {
|
|||
Get the (unencoded) text of this text node, including any newlines and spaces present in the original.
|
||||
@return text
|
||||
*/
|
||||
open func getWholeText()->String {
|
||||
open func getWholeText() -> String {
|
||||
return attributes == nil ? _text : attributes!.get(key: TextNode.TEXT_KEY)
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@ open class TextNode : Node {
|
|||
Test if this text node is blank -- that is, empty or only whitespace (including newlines).
|
||||
@return true if this document is empty or only whitespace, false if it contains any text content.
|
||||
*/
|
||||
open func isBlank()->Bool {
|
||||
open func isBlank() -> Bool {
|
||||
return StringUtil.isBlank(getWholeText())
|
||||
}
|
||||
|
||||
|
@ -92,25 +92,24 @@ open class TextNode : Node {
|
|||
try Validate.isTrue(val: offset >= 0, msg: "Split offset must be not be negative")
|
||||
try Validate.isTrue(val: offset < _text.characters.count, msg: "Split offset must not be greater than current text length")
|
||||
|
||||
let head : String = getWholeText().substring(0, offset)
|
||||
let tail : String = getWholeText().substring(offset)
|
||||
let head: String = getWholeText().substring(0, offset)
|
||||
let tail: String = getWholeText().substring(offset)
|
||||
text(head)
|
||||
let tailNode : TextNode = TextNode(tail, self.getBaseUri())
|
||||
if (parent() != nil){
|
||||
let tailNode: TextNode = TextNode(tail, self.getBaseUri())
|
||||
if (parent() != nil) {
|
||||
try parent()?.addChildren(siblingIndex+1, tailNode)
|
||||
}
|
||||
return tailNode
|
||||
}
|
||||
|
||||
override func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings)throws
|
||||
{
|
||||
override func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings)throws {
|
||||
if (out.prettyPrint() &&
|
||||
((siblingIndex == 0 && (parentNode as? Element) != nil && (parentNode as! Element).tag().formatAsBlock() && !isBlank()) ||
|
||||
(out.outline() && siblingNodes().count > 0 && !isBlank()) )){
|
||||
(out.outline() && siblingNodes().count > 0 && !isBlank()) )) {
|
||||
indent(accum, depth, out)
|
||||
}
|
||||
|
||||
let par : Element? = parent() as? Element
|
||||
let par: Element? = parent() as? Element
|
||||
let normaliseWhite = out.prettyPrint() && par != nil && !Element.preserveWhitespace(par!)
|
||||
|
||||
Entities.escape(accum, getWholeText(), out, false, normaliseWhite, false)
|
||||
|
@ -119,7 +118,6 @@ open class TextNode : Node {
|
|||
override func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {
|
||||
}
|
||||
|
||||
|
||||
open override func toString()throws->String {
|
||||
return try outerHtml()
|
||||
}
|
||||
|
@ -131,21 +129,21 @@ open class TextNode : Node {
|
|||
* @return TextNode containing unencoded data (e.g. <)
|
||||
*/
|
||||
open static func createFromEncoded(_ encodedText: String, _ baseUri: String)throws->TextNode {
|
||||
let text : String = try Entities.unescape(encodedText)
|
||||
let text: String = try Entities.unescape(encodedText)
|
||||
return TextNode(text, baseUri)
|
||||
}
|
||||
|
||||
static open func normaliseWhitespace(_ text: String)->String {
|
||||
static open func normaliseWhitespace(_ text: String) -> String {
|
||||
let _text = StringUtil.normaliseWhitespace(text)
|
||||
return _text
|
||||
}
|
||||
|
||||
static open func stripLeadingWhitespace(_ text: String)->String {
|
||||
static open func stripLeadingWhitespace(_ text: String) -> String {
|
||||
return text.replaceFirst(of: "^\\s+", with: "")
|
||||
//return text.replaceFirst("^\\s+", "")
|
||||
}
|
||||
|
||||
static open func lastCharIsWhitespace(_ sb: StringBuilder)->Bool {
|
||||
static open func lastCharIsWhitespace(_ sb: StringBuilder) -> Bool {
|
||||
return sb.toString().characters.last == " "
|
||||
}
|
||||
|
||||
|
@ -153,19 +151,18 @@ open class TextNode : Node {
|
|||
private func ensureAttributes() {
|
||||
if (attributes == nil) {
|
||||
attributes = Attributes()
|
||||
do{
|
||||
do {
|
||||
try attributes?.put(TextNode.TEXT_KEY, _text)
|
||||
}catch{}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
open override func attr(_ attributeKey: String)throws->String {
|
||||
ensureAttributes()
|
||||
return try super.attr(attributeKey)
|
||||
}
|
||||
|
||||
open override func getAttributes()->Attributes {
|
||||
open override func getAttributes() -> Attributes {
|
||||
ensureAttributes()
|
||||
return super.getAttributes()!
|
||||
}
|
||||
|
@ -175,38 +172,32 @@ open class TextNode : Node {
|
|||
return try super.attr(attributeKey, attributeValue)
|
||||
}
|
||||
|
||||
|
||||
open override func hasAttr(_ attributeKey: String)->Bool {
|
||||
open override func hasAttr(_ attributeKey: String) -> Bool {
|
||||
ensureAttributes()
|
||||
return super.hasAttr(attributeKey)
|
||||
}
|
||||
|
||||
|
||||
open override func removeAttr(_ attributeKey: String)throws->Node {
|
||||
ensureAttributes()
|
||||
return try super.removeAttr(attributeKey)
|
||||
}
|
||||
|
||||
|
||||
open override func absUrl(_ attributeKey: String)throws->String {
|
||||
ensureAttributes()
|
||||
return try super.absUrl(attributeKey)
|
||||
}
|
||||
|
||||
public override func copy(with zone: NSZone? = nil) -> Any
|
||||
{
|
||||
let clone = TextNode(_text,baseUri)
|
||||
public override func copy(with zone: NSZone? = nil) -> Any {
|
||||
let clone = TextNode(_text, baseUri)
|
||||
return super.copy(clone: clone)
|
||||
}
|
||||
|
||||
public override func copy(parent: Node?)->Node
|
||||
{
|
||||
let clone = TextNode(_text,baseUri)
|
||||
return super.copy(clone: clone,parent: parent)
|
||||
public override func copy(parent: Node?) -> Node {
|
||||
let clone = TextNode(_text, baseUri)
|
||||
return super.copy(clone: clone, parent: parent)
|
||||
}
|
||||
|
||||
public override func copy(clone: Node, parent: Node?)->Node
|
||||
{
|
||||
return super.copy(clone: clone,parent: parent)
|
||||
public override func copy(clone: Node, parent: Node?) -> Node {
|
||||
return super.copy(clone: clone, parent: parent)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,15 +8,13 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
open class Token
|
||||
{
|
||||
var type : TokenType = TokenType.Doctype
|
||||
open class Token {
|
||||
var type: TokenType = TokenType.Doctype
|
||||
|
||||
private init() {
|
||||
}
|
||||
|
||||
func tokenType()->String
|
||||
{
|
||||
func tokenType() -> String {
|
||||
return String(describing: type(of: self))
|
||||
}
|
||||
|
||||
|
@ -25,8 +23,7 @@ open class Token
|
|||
* piece of data, which immediately get GCed.
|
||||
*/
|
||||
@discardableResult
|
||||
public func reset()->Token
|
||||
{
|
||||
public func reset() -> Token {
|
||||
preconditionFailure("This method must be overridden")
|
||||
}
|
||||
|
||||
|
@ -38,11 +35,11 @@ open class Token
|
|||
return String(describing: type(of: self))
|
||||
}
|
||||
|
||||
final class Doctype : Token {
|
||||
final class Doctype: Token {
|
||||
let name: StringBuilder = StringBuilder()
|
||||
let publicIdentifier: StringBuilder = StringBuilder()
|
||||
let systemIdentifier : StringBuilder = StringBuilder()
|
||||
var forceQuirks : Bool = false
|
||||
let systemIdentifier: StringBuilder = StringBuilder()
|
||||
var forceQuirks: Bool = false
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
|
@ -50,7 +47,7 @@ open class Token
|
|||
}
|
||||
|
||||
@discardableResult
|
||||
override func reset()->Token {
|
||||
override func reset() -> Token {
|
||||
Token.reset(name)
|
||||
Token.reset(publicIdentifier)
|
||||
Token.reset(systemIdentifier)
|
||||
|
@ -58,41 +55,41 @@ open class Token
|
|||
return self
|
||||
}
|
||||
|
||||
func getName()->String {
|
||||
func getName() -> String {
|
||||
return name.toString()
|
||||
}
|
||||
|
||||
func getPublicIdentifier()->String {
|
||||
func getPublicIdentifier() -> String {
|
||||
return publicIdentifier.toString()
|
||||
}
|
||||
|
||||
open func getSystemIdentifier()->String {
|
||||
open func getSystemIdentifier() -> String {
|
||||
return systemIdentifier.toString()
|
||||
}
|
||||
|
||||
open func isForceQuirks()->Bool {
|
||||
open func isForceQuirks() -> Bool {
|
||||
return forceQuirks
|
||||
}
|
||||
}
|
||||
|
||||
class Tag : Token {
|
||||
class Tag: Token {
|
||||
public var _tagName: String?
|
||||
public var _normalName: String? // lc version of tag name, for case insensitive tree build
|
||||
private var _pendingAttributeName: String? // attribute names are generally caught in one hop, not accumulated
|
||||
private let _pendingAttributeValue : StringBuilder = StringBuilder() // but values are accumulated, from e.g. & in hrefs
|
||||
private let _pendingAttributeValue: StringBuilder = StringBuilder() // but values are accumulated, from e.g. & in hrefs
|
||||
private var _pendingAttributeValueS: String? // try to get attr vals in one shot, vs Builder
|
||||
private var _hasEmptyAttributeValue : Bool = false // distinguish boolean attribute from empty string value
|
||||
private var _hasEmptyAttributeValue: Bool = false // distinguish boolean attribute from empty string value
|
||||
private var _hasPendingAttributeValue: Bool = false
|
||||
public var _selfClosing : Bool = false
|
||||
public var _selfClosing: Bool = false
|
||||
// start tags get attributes on construction. End tags get attributes on first new attribute (but only for parser convenience, not used).
|
||||
public var _attributes : Attributes = Attributes()
|
||||
public var _attributes: Attributes = Attributes()
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
override func reset()->Tag {
|
||||
override func reset() -> Tag {
|
||||
_tagName = nil
|
||||
_normalName = nil
|
||||
_pendingAttributeName = nil
|
||||
|
@ -111,12 +108,12 @@ open class Token
|
|||
// }
|
||||
|
||||
if (_pendingAttributeName != nil) {
|
||||
var attribute : Attribute
|
||||
if (_hasPendingAttributeValue){
|
||||
attribute = try Attribute(key: _pendingAttributeName!,value: _pendingAttributeValue.length > 0 ? _pendingAttributeValue.toString() : _pendingAttributeValueS!)
|
||||
}else if (_hasEmptyAttributeValue){
|
||||
var attribute: Attribute
|
||||
if (_hasPendingAttributeValue) {
|
||||
attribute = try Attribute(key: _pendingAttributeName!, value: _pendingAttributeValue.length > 0 ? _pendingAttributeValue.toString() : _pendingAttributeValueS!)
|
||||
} else if (_hasEmptyAttributeValue) {
|
||||
attribute = try Attribute(key: _pendingAttributeName!, value: "")
|
||||
}else{
|
||||
} else {
|
||||
attribute = try BooleanAttribute(key: _pendingAttributeName!)
|
||||
}
|
||||
_attributes.put(attribute: attribute)
|
||||
|
@ -141,22 +138,22 @@ open class Token
|
|||
return _tagName!
|
||||
}
|
||||
|
||||
func normalName()->String? { // loses case, used in tree building for working out where in tree it should go
|
||||
func normalName() -> String? { // loses case, used in tree building for working out where in tree it should go
|
||||
return _normalName
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func name(_ name: String)->Tag {
|
||||
func name(_ name: String) -> Tag {
|
||||
_tagName = name
|
||||
_normalName = name.lowercased()
|
||||
return self
|
||||
}
|
||||
|
||||
func isSelfClosing()->Bool {
|
||||
func isSelfClosing() -> Bool {
|
||||
return _selfClosing
|
||||
}
|
||||
|
||||
func getAttributes()->Attributes {
|
||||
func getAttributes() -> Attributes {
|
||||
return _attributes
|
||||
}
|
||||
|
||||
|
@ -166,7 +163,7 @@ open class Token
|
|||
_normalName = _tagName?.lowercased()
|
||||
}
|
||||
|
||||
func appendTagName(_ append : UnicodeScalar) {
|
||||
func appendTagName(_ append: UnicodeScalar) {
|
||||
appendTagName("\(append)")
|
||||
}
|
||||
|
||||
|
@ -218,7 +215,7 @@ open class Token
|
|||
}
|
||||
}
|
||||
|
||||
final class StartTag : Tag {
|
||||
final class StartTag: Tag {
|
||||
override init() {
|
||||
super.init()
|
||||
_attributes = Attributes()
|
||||
|
@ -226,7 +223,7 @@ open class Token
|
|||
}
|
||||
|
||||
@discardableResult
|
||||
override func reset()->Tag {
|
||||
override func reset() -> Tag {
|
||||
super.reset()
|
||||
_attributes = Attributes()
|
||||
// todo - would prefer these to be null, but need to check Element assertions
|
||||
|
@ -234,7 +231,7 @@ open class Token
|
|||
}
|
||||
|
||||
@discardableResult
|
||||
func nameAttr(_ name: String, _ attributes: Attributes)->StartTag {
|
||||
func nameAttr(_ name: String, _ attributes: Attributes) -> StartTag {
|
||||
self._tagName = name
|
||||
self._attributes = attributes
|
||||
_normalName = _tagName?.lowercased()
|
||||
|
@ -242,32 +239,31 @@ open class Token
|
|||
}
|
||||
|
||||
open override func toString()throws->String {
|
||||
if (_attributes.size() > 0){
|
||||
if (_attributes.size() > 0) {
|
||||
return try "<" + (name()) + " " + (_attributes.toString()) + ">"
|
||||
}else{
|
||||
} else {
|
||||
return try "<" + name() + ">"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class EndTag : Tag{
|
||||
final class EndTag: Tag {
|
||||
override init() {
|
||||
super.init()
|
||||
type = TokenType.EndTag
|
||||
}
|
||||
|
||||
|
||||
open override func toString()throws->String {
|
||||
return "</" + (try name()) + ">"
|
||||
}
|
||||
}
|
||||
|
||||
final class Comment : Token {
|
||||
let data : StringBuilder = StringBuilder()
|
||||
var bogus : Bool = false
|
||||
final class Comment: Token {
|
||||
let data: StringBuilder = StringBuilder()
|
||||
var bogus: Bool = false
|
||||
|
||||
@discardableResult
|
||||
override func reset()->Token {
|
||||
override func reset() -> Token {
|
||||
Token.reset(data)
|
||||
bogus = false
|
||||
return self
|
||||
|
@ -278,18 +274,17 @@ open class Token
|
|||
type = TokenType.Comment
|
||||
}
|
||||
|
||||
func getData()->String {
|
||||
func getData() -> String {
|
||||
return data.toString()
|
||||
}
|
||||
|
||||
|
||||
open override func toString()throws->String {
|
||||
return "<!--" + getData() + "-->"
|
||||
}
|
||||
}
|
||||
|
||||
final class Char : Token {
|
||||
public var data : String?
|
||||
final class Char: Token {
|
||||
public var data: String?
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
|
@ -297,85 +292,83 @@ open class Token
|
|||
}
|
||||
|
||||
@discardableResult
|
||||
override func reset()->Token {
|
||||
override func reset() -> Token {
|
||||
data = nil
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func data(_ data: String)->Char {
|
||||
func data(_ data: String) -> Char {
|
||||
self.data = data
|
||||
return self
|
||||
}
|
||||
|
||||
func getData()->String? {
|
||||
func getData() -> String? {
|
||||
return data
|
||||
}
|
||||
|
||||
|
||||
open override func toString()throws->String {
|
||||
try Validate.notNull(obj: data)
|
||||
return getData()!
|
||||
}
|
||||
}
|
||||
|
||||
final class EOF : Token {
|
||||
final class EOF: Token {
|
||||
override init() {
|
||||
super.init()
|
||||
type = Token.TokenType.EOF
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
override func reset()->Token {
|
||||
override func reset() -> Token {
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
func isDoctype()->Bool {
|
||||
func isDoctype() -> Bool {
|
||||
return type == TokenType.Doctype
|
||||
}
|
||||
|
||||
func asDoctype()->Doctype {
|
||||
func asDoctype() -> Doctype {
|
||||
return self as! Doctype
|
||||
}
|
||||
|
||||
func isStartTag()->Bool {
|
||||
func isStartTag() -> Bool {
|
||||
return type == TokenType.StartTag
|
||||
}
|
||||
|
||||
func asStartTag()->StartTag {
|
||||
func asStartTag() -> StartTag {
|
||||
return self as! StartTag
|
||||
}
|
||||
|
||||
func isEndTag()->Bool {
|
||||
func isEndTag() -> Bool {
|
||||
return type == TokenType.EndTag
|
||||
}
|
||||
|
||||
func asEndTag()->EndTag {
|
||||
func asEndTag() -> EndTag {
|
||||
return self as! EndTag
|
||||
}
|
||||
|
||||
func isComment()->Bool {
|
||||
func isComment() -> Bool {
|
||||
return type == TokenType.Comment
|
||||
}
|
||||
|
||||
func asComment()->Comment {
|
||||
func asComment() -> Comment {
|
||||
return self as! Comment
|
||||
}
|
||||
|
||||
func isCharacter()->Bool {
|
||||
func isCharacter() -> Bool {
|
||||
return type == TokenType.Char
|
||||
}
|
||||
|
||||
func asCharacter()->Char {
|
||||
func asCharacter() -> Char {
|
||||
return self as! Char
|
||||
}
|
||||
|
||||
func isEOF()->Bool {
|
||||
func isEOF() -> Bool {
|
||||
return type == TokenType.EOF
|
||||
}
|
||||
|
||||
|
||||
public enum TokenType {
|
||||
case Doctype
|
||||
case StartTag
|
||||
|
|
|
@ -8,12 +8,11 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
open class TokenQueue
|
||||
{
|
||||
private var queue : String
|
||||
private var pos : Int = 0
|
||||
open class TokenQueue {
|
||||
private var queue: String
|
||||
private var pos: Int = 0
|
||||
|
||||
private static let ESC : Character = "\\" // escape char for chomp balanced.
|
||||
private static let ESC: Character = "\\" // escape char for chomp balanced.
|
||||
|
||||
/**
|
||||
Create a new TokenQueue.
|
||||
|
@ -27,11 +26,11 @@ open class TokenQueue
|
|||
* Is the queue empty?
|
||||
* @return true if no data left in queue.
|
||||
*/
|
||||
open func isEmpty()->Bool {
|
||||
open func isEmpty() -> Bool {
|
||||
return remainingLength() == 0
|
||||
}
|
||||
|
||||
private func remainingLength()->Int {
|
||||
private func remainingLength() -> Int {
|
||||
return queue.characters.count - pos
|
||||
}
|
||||
|
||||
|
@ -39,7 +38,7 @@ open class TokenQueue
|
|||
* Retrieves but does not remove the first character from the queue.
|
||||
* @return First character, or 0 if empty.
|
||||
*/
|
||||
open func peek()-> Character {
|
||||
open func peek() -> Character {
|
||||
return isEmpty() ? Character(UnicodeScalar(0)) : queue[pos]
|
||||
}
|
||||
|
||||
|
@ -66,7 +65,7 @@ open class TokenQueue
|
|||
* @param seq String to check queue for.
|
||||
* @return true if the next characters match.
|
||||
*/
|
||||
open func matches(_ seq: String)->Bool {
|
||||
open func matches(_ seq: String) -> Bool {
|
||||
return queue.regionMatches(true, pos, seq, 0, seq.characters.count)
|
||||
}
|
||||
|
||||
|
@ -75,7 +74,7 @@ open class TokenQueue
|
|||
* @param seq string to case sensitively check for
|
||||
* @return true if matched, false if not
|
||||
*/
|
||||
open func matchesCS(_ seq: String)->Bool {
|
||||
open func matchesCS(_ seq: String) -> Bool {
|
||||
return queue.startsWith(seq, pos)
|
||||
}
|
||||
|
||||
|
@ -84,32 +83,32 @@ open class TokenQueue
|
|||
@param seq list of strings to case insensitively check for
|
||||
@return true of any matched, false if none did
|
||||
*/
|
||||
open func matchesAny(_ seq:[String])->Bool {
|
||||
open func matchesAny(_ seq: [String]) -> Bool {
|
||||
for s in seq {
|
||||
if (matches(s)){
|
||||
if (matches(s)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
open func matchesAny(_ seq: String...)->Bool {
|
||||
open func matchesAny(_ seq: String...) -> Bool {
|
||||
return matchesAny(seq)
|
||||
}
|
||||
|
||||
open func matchesAny(_ seq: Character...)->Bool {
|
||||
if (isEmpty()){
|
||||
open func matchesAny(_ seq: Character...) -> Bool {
|
||||
if (isEmpty()) {
|
||||
return false
|
||||
}
|
||||
|
||||
for c in seq {
|
||||
if (queue[pos] as Character == c){
|
||||
if (queue[pos] as Character == c) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
open func matchesStartTag()->Bool {
|
||||
open func matchesStartTag() -> Bool {
|
||||
// micro opt for matching "<x"
|
||||
return (remainingLength() >= 2 && queue[pos] as Character == "<" && Character.isLetter(queue.charAt(pos+1)))
|
||||
}
|
||||
|
@ -121,7 +120,7 @@ open class TokenQueue
|
|||
* @return true if found and removed, false if not found.
|
||||
*/
|
||||
@discardableResult
|
||||
open func matchChomp(_ seq: String)->Bool {
|
||||
open func matchChomp(_ seq: String) -> Bool {
|
||||
if (matches(seq)) {
|
||||
pos += seq.characters.count
|
||||
return true
|
||||
|
@ -134,7 +133,7 @@ open class TokenQueue
|
|||
Tests if queue starts with a whitespace character.
|
||||
@return if starts with whitespace
|
||||
*/
|
||||
open func matchesWhitespace()->Bool {
|
||||
open func matchesWhitespace() -> Bool {
|
||||
return !isEmpty() && StringUtil.isWhitespace(queue.charAt(pos))
|
||||
}
|
||||
|
||||
|
@ -142,7 +141,7 @@ open class TokenQueue
|
|||
Test if the queue matches a word character (letter or digit).
|
||||
@return if matches a word character
|
||||
*/
|
||||
open func matchesWord()->Bool {
|
||||
open func matchesWord() -> Bool {
|
||||
return !isEmpty() && (Character.isLetterOrDigit(queue.charAt(pos)))
|
||||
}
|
||||
|
||||
|
@ -158,7 +157,7 @@ open class TokenQueue
|
|||
* Consume one character off queue.
|
||||
* @return first character on queue.
|
||||
*/
|
||||
open func consume()->Character {
|
||||
open func consume() -> Character {
|
||||
let i = pos
|
||||
pos+=1
|
||||
return queue.charAt(i)
|
||||
|
@ -172,12 +171,12 @@ open class TokenQueue
|
|||
* @param seq sequence to remove from head of queue.
|
||||
*/
|
||||
open func consume(_ seq: String)throws {
|
||||
if (!matches(seq)){
|
||||
if (!matches(seq)) {
|
||||
//throw new IllegalStateException("Queue did not match expected sequence")
|
||||
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Queue did not match expected sequence")
|
||||
}
|
||||
let len = seq.characters.count
|
||||
if (len > remainingLength()){
|
||||
if (len > remainingLength()) {
|
||||
//throw new IllegalStateException("Queue not long enough to consume sequence")
|
||||
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Queue not long enough to consume sequence")
|
||||
}
|
||||
|
@ -191,7 +190,7 @@ open class TokenQueue
|
|||
* @return The matched data consumed from queue.
|
||||
*/
|
||||
@discardableResult
|
||||
open func consumeTo(_ seq: String)->String {
|
||||
open func consumeTo(_ seq: String) -> String {
|
||||
let offset = queue.indexOf(seq, pos)
|
||||
if (offset != -1) {
|
||||
let consumed = queue.substring(pos, offset-pos)
|
||||
|
@ -203,25 +202,24 @@ open class TokenQueue
|
|||
return ""
|
||||
}
|
||||
|
||||
open func consumeToIgnoreCase(_ seq: String)->String {
|
||||
open func consumeToIgnoreCase(_ seq: String) -> String {
|
||||
let start = pos
|
||||
let first = seq.substring(0, 1)
|
||||
let canScan = first.lowercased() == first.uppercased() // if first is not cased, use index of
|
||||
while (!isEmpty()) {
|
||||
if (matches(seq)){
|
||||
if (matches(seq)) {
|
||||
break
|
||||
}
|
||||
if (canScan)
|
||||
{
|
||||
if (canScan) {
|
||||
let skip = queue.indexOf(first, pos) - pos
|
||||
if (skip == 0){ // this char is the skip char, but not match, so force advance of pos
|
||||
if (skip == 0) { // this char is the skip char, but not match, so force advance of pos
|
||||
pos+=1
|
||||
}else if (skip < 0){ // no chance of finding, grab to end
|
||||
} else if (skip < 0) { // no chance of finding, grab to end
|
||||
pos = queue.characters.count
|
||||
}else{
|
||||
} else {
|
||||
pos += skip
|
||||
}
|
||||
} else{
|
||||
} else {
|
||||
pos+=1
|
||||
}
|
||||
}
|
||||
|
@ -236,10 +234,10 @@ open class TokenQueue
|
|||
*/
|
||||
// todo: method name. not good that consumeTo cares for case, and consume to any doesn't. And the only use for this
|
||||
// is is a case sensitive time...
|
||||
open func consumeToAny(_ seq: String...)->String {
|
||||
open func consumeToAny(_ seq: String...) -> String {
|
||||
return consumeToAny(seq)
|
||||
}
|
||||
open func consumeToAny(_ seq: [String])->String {
|
||||
open func consumeToAny(_ seq: [String]) -> String {
|
||||
let start = pos
|
||||
while (!isEmpty() && !matchesAny(seq)) {
|
||||
pos+=1
|
||||
|
@ -255,13 +253,13 @@ open class TokenQueue
|
|||
* @param seq String to match up to, and not include in return, and to pull off queue. <b>Case sensitive.</b>
|
||||
* @return Data matched from queue.
|
||||
*/
|
||||
open func chompTo(_ seq: String)->String {
|
||||
open func chompTo(_ seq: String) -> String {
|
||||
let data = consumeTo(seq)
|
||||
matchChomp(seq)
|
||||
return data
|
||||
}
|
||||
|
||||
open func chompToIgnoreCase(_ seq: String)->String {
|
||||
open func chompToIgnoreCase(_ seq: String) -> String {
|
||||
let data = consumeToIgnoreCase(seq) // case insensitive scan
|
||||
matchChomp(seq)
|
||||
return data
|
||||
|
@ -276,35 +274,34 @@ open class TokenQueue
|
|||
* @param close closer
|
||||
* @return data matched from the queue
|
||||
*/
|
||||
open func chompBalanced(_ open:Character, _ close: Character)->String {
|
||||
open func chompBalanced(_ open: Character, _ close: Character) -> String {
|
||||
var start = -1
|
||||
var end = -1
|
||||
var depth = 0
|
||||
var last : Character = Character(UnicodeScalar(0))
|
||||
var last: Character = Character(UnicodeScalar(0))
|
||||
var inQuote = false
|
||||
|
||||
repeat {
|
||||
if (isEmpty()){break}
|
||||
if (isEmpty()) {break}
|
||||
let c = consume()
|
||||
if (last.unicodeScalar.value == 0 || last != TokenQueue.ESC) {
|
||||
if ((c=="'" || c=="\"") && c != open){
|
||||
if ((c=="'" || c=="\"") && c != open) {
|
||||
inQuote = !inQuote
|
||||
}
|
||||
if (inQuote){
|
||||
if (inQuote) {
|
||||
continue
|
||||
}
|
||||
if (c==open) {
|
||||
depth+=1
|
||||
if (start == -1){
|
||||
if (start == -1) {
|
||||
start = pos
|
||||
}
|
||||
}
|
||||
else if (c==close){
|
||||
} else if (c==close) {
|
||||
depth-=1
|
||||
}
|
||||
}
|
||||
|
||||
if (depth > 0 && last.unicodeScalar.value != 0){
|
||||
if (depth > 0 && last.unicodeScalar.value != 0) {
|
||||
end = pos // don't include the outer match pair in the return
|
||||
}
|
||||
last = c
|
||||
|
@ -317,17 +314,15 @@ open class TokenQueue
|
|||
* @param in backslash escaped string
|
||||
* @return unescaped string
|
||||
*/
|
||||
open static func unescape(_ input: String)->String {
|
||||
open static func unescape(_ input: String) -> String {
|
||||
let out = StringBuilder()
|
||||
var last = Character(UnicodeScalar(0))
|
||||
for c in input.characters
|
||||
{
|
||||
for c in input.characters {
|
||||
if (c == ESC) {
|
||||
if (last.unicodeScalar.value != 0 && last == TokenQueue.ESC){
|
||||
if (last.unicodeScalar.value != 0 && last == TokenQueue.ESC) {
|
||||
out.append(c)
|
||||
}
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
out.append(c)
|
||||
}
|
||||
last = c
|
||||
|
@ -340,7 +335,7 @@ open class TokenQueue
|
|||
* @return Whether consuming whitespace or not
|
||||
*/
|
||||
@discardableResult
|
||||
open func consumeWhitespace()->Bool {
|
||||
open func consumeWhitespace() -> Bool {
|
||||
var seen = false
|
||||
while (matchesWhitespace()) {
|
||||
pos+=1
|
||||
|
@ -354,9 +349,9 @@ open class TokenQueue
|
|||
* @return String of word characters from queue, or empty string if none.
|
||||
*/
|
||||
@discardableResult
|
||||
open func consumeWord()->String {
|
||||
open func consumeWord() -> String {
|
||||
let start = pos
|
||||
while (matchesWord()){
|
||||
while (matchesWord()) {
|
||||
pos+=1
|
||||
}
|
||||
return queue.substring(start, pos-start)
|
||||
|
@ -367,9 +362,9 @@ open class TokenQueue
|
|||
*
|
||||
* @return tag name
|
||||
*/
|
||||
open func consumeTagName()->String {
|
||||
open func consumeTagName() -> String {
|
||||
let start = pos
|
||||
while (!isEmpty() && (matchesWord() || matchesAny(":", "_", "-"))){
|
||||
while (!isEmpty() && (matchesWord() || matchesAny(":", "_", "-"))) {
|
||||
pos+=1
|
||||
}
|
||||
|
||||
|
@ -381,9 +376,9 @@ open class TokenQueue
|
|||
*
|
||||
* @return tag name
|
||||
*/
|
||||
open func consumeElementSelector()->String {
|
||||
open func consumeElementSelector() -> String {
|
||||
let start = pos
|
||||
while (!isEmpty() && (matchesWord() || matchesAny("*|","|", "_", "-"))){
|
||||
while (!isEmpty() && (matchesWord() || matchesAny("*|", "|", "_", "-"))) {
|
||||
pos+=1
|
||||
}
|
||||
|
||||
|
@ -395,9 +390,9 @@ open class TokenQueue
|
|||
http://www.w3.org/TR/CSS2/syndata.html#value-def-identifier
|
||||
@return identifier
|
||||
*/
|
||||
open func consumeCssIdentifier()->String {
|
||||
open func consumeCssIdentifier() -> String {
|
||||
let start = pos
|
||||
while (!isEmpty() && (matchesWord() || matchesAny("-", "_"))){
|
||||
while (!isEmpty() && (matchesWord() || matchesAny("-", "_"))) {
|
||||
pos+=1
|
||||
}
|
||||
|
||||
|
@ -408,9 +403,9 @@ open class TokenQueue
|
|||
Consume an attribute key off the queue (letter, digit, -, _, :")
|
||||
@return attribute key
|
||||
*/
|
||||
open func consumeAttributeKey()->String {
|
||||
open func consumeAttributeKey() -> String {
|
||||
let start = pos
|
||||
while (!isEmpty() && (matchesWord() || matchesAny("-", "_", ":"))){
|
||||
while (!isEmpty() && (matchesWord() || matchesAny("-", "_", ":"))) {
|
||||
pos+=1
|
||||
}
|
||||
|
||||
|
@ -421,14 +416,13 @@ open class TokenQueue
|
|||
Consume and return whatever is left on the queue.
|
||||
@return remained of queue.
|
||||
*/
|
||||
open func remainder()->String {
|
||||
open func remainder() -> String {
|
||||
let remainder = queue.substring(pos, queue.characters.count-pos)
|
||||
pos = queue.characters.count
|
||||
return remainder
|
||||
}
|
||||
|
||||
|
||||
open func toString()->String {
|
||||
open func toString() -> String {
|
||||
return queue.substring(pos)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,23 +8,22 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
final class Tokeniser
|
||||
{
|
||||
static let replacementChar : UnicodeScalar = "\u{FFFD}" // replaces null character
|
||||
private static let notCharRefCharsSorted : [UnicodeScalar] = ["\t", "\n", "\r",UnicodeScalar.BackslashF, " ", "<", "&"].sorted()
|
||||
final class Tokeniser {
|
||||
static let replacementChar: UnicodeScalar = "\u{FFFD}" // replaces null character
|
||||
private static let notCharRefCharsSorted: [UnicodeScalar] = ["\t", "\n", "\r", UnicodeScalar.BackslashF, " ", "<", "&"].sorted()
|
||||
|
||||
private let reader : CharacterReader // html input
|
||||
private let errors : ParseErrorList? // errors found while tokenising
|
||||
private let reader: CharacterReader // html input
|
||||
private let errors: ParseErrorList? // errors found while tokenising
|
||||
|
||||
private var state: TokeniserState = TokeniserState.Data // current tokenisation state
|
||||
private var emitPending: Token? // the token we are about to emit on next read
|
||||
private var isEmitPending : Bool = false
|
||||
private var charsString : String? = nil // 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>
|
||||
private var isEmitPending: Bool = false
|
||||
private var charsString: String? = nil // characters pending an emit. Will fall to charsBuilder if more than one
|
||||
private let charsBuilder: StringBuilder = StringBuilder(1024) // buffers characters to output as one token, if more than one emit per read
|
||||
let dataBuffer: StringBuilder = StringBuilder(1024) // buffers data looking for </script>
|
||||
|
||||
var tagPending : Token.Tag = Token.Tag() // tag we are building up
|
||||
let startPending : Token.StartTag = Token.StartTag()
|
||||
var tagPending: Token.Tag = Token.Tag() // tag we are building up
|
||||
let startPending: Token.StartTag = Token.StartTag()
|
||||
let endPending: Token.EndTag = Token.EndTag()
|
||||
let charPending: Token.Char = Token.Char()
|
||||
let doctypePending: Token.Doctype = Token.Doctype() // doctype building up
|
||||
|
@ -32,7 +31,6 @@ final class Tokeniser
|
|||
private var lastStartTag: String? // the last start tag emitted, to test appropriate end tag
|
||||
private var selfClosingFlagAcknowledged: Bool = true
|
||||
|
||||
|
||||
init(_ reader: CharacterReader, _ errors: ParseErrorList?) {
|
||||
self.reader = reader
|
||||
self.errors = errors
|
||||
|
@ -44,7 +42,7 @@ final class Tokeniser
|
|||
selfClosingFlagAcknowledged = true
|
||||
}
|
||||
|
||||
while (!isEmitPending){
|
||||
while (!isEmitPending) {
|
||||
try state.read(self, reader)
|
||||
}
|
||||
|
||||
|
@ -55,7 +53,7 @@ final class Tokeniser
|
|||
charsString = nil
|
||||
return charPending.data(str)
|
||||
} else if (charsString != nil) {
|
||||
let token : Token = charPending.data(charsString!)
|
||||
let token: Token = charPending.data(charsString!)
|
||||
charsString = nil
|
||||
return token
|
||||
} else {
|
||||
|
@ -71,14 +69,14 @@ final class Tokeniser
|
|||
isEmitPending = true
|
||||
|
||||
if (token.type == Token.TokenType.StartTag) {
|
||||
let startTag : Token.StartTag = token as! Token.StartTag
|
||||
let startTag: Token.StartTag = token as! Token.StartTag
|
||||
lastStartTag = startTag._tagName!
|
||||
if (startTag._selfClosing){
|
||||
if (startTag._selfClosing) {
|
||||
selfClosingFlagAcknowledged = false
|
||||
}
|
||||
} else if (token.type == Token.TokenType.EndTag) {
|
||||
let endTag : Token.EndTag = token as! Token.EndTag
|
||||
if (endTag._attributes.size() != 0){
|
||||
let endTag: Token.EndTag = token as! Token.EndTag
|
||||
if (endTag._attributes.size() != 0) {
|
||||
error("Attributes incorrectly present on end tag")
|
||||
}
|
||||
}
|
||||
|
@ -89,8 +87,7 @@ final class Tokeniser
|
|||
// does not set isEmitPending; read checks that
|
||||
if (charsString == nil) {
|
||||
charsString = str
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (charsBuilder.length == 0) { // switching to string builder as more than one emit before read
|
||||
charsBuilder.append(charsString!)
|
||||
}
|
||||
|
@ -99,7 +96,7 @@ final class Tokeniser
|
|||
}
|
||||
|
||||
func emit(_ chars: [UnicodeScalar]) {
|
||||
emit(String(chars.map{Character($0)}))
|
||||
emit(String(chars.map {Character($0)}))
|
||||
}
|
||||
|
||||
// func emit(_ codepoints: [Int]) {
|
||||
|
@ -110,7 +107,7 @@ final class Tokeniser
|
|||
emit(String(c))
|
||||
}
|
||||
|
||||
func getState()->TokeniserState {
|
||||
func getState() -> TokeniserState {
|
||||
return state
|
||||
}
|
||||
|
||||
|
@ -128,16 +125,16 @@ final class Tokeniser
|
|||
}
|
||||
|
||||
private var codepointHolder: [UnicodeScalar] = [UnicodeScalar(0)!] // holder to not have to keep creating arrays
|
||||
private var multipointHolder: [UnicodeScalar] = [UnicodeScalar(0)!,UnicodeScalar(0)!]
|
||||
private var multipointHolder: [UnicodeScalar] = [UnicodeScalar(0)!, UnicodeScalar(0)!]
|
||||
|
||||
func consumeCharacterReference(_ additionalAllowedCharacter: UnicodeScalar?, _ inAttribute: Bool)throws->[UnicodeScalar]? {
|
||||
if (reader.isEmpty()){
|
||||
if (reader.isEmpty()) {
|
||||
return nil
|
||||
}
|
||||
if (additionalAllowedCharacter != nil && additionalAllowedCharacter == reader.current()){
|
||||
if (additionalAllowedCharacter != nil && additionalAllowedCharacter == reader.current()) {
|
||||
return nil
|
||||
}
|
||||
if (reader.matchesAnySorted(Tokeniser.notCharRefCharsSorted)){
|
||||
if (reader.matchesAnySorted(Tokeniser.notCharRefCharsSorted)) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -151,18 +148,16 @@ final class Tokeniser
|
|||
reader.rewindToMark()
|
||||
return nil
|
||||
}
|
||||
if (!reader.matchConsume(";")){
|
||||
if (!reader.matchConsume(";")) {
|
||||
characterReferenceError("missing semicolon") // missing semi
|
||||
}
|
||||
var charval : Int = -1
|
||||
var charval: Int = -1
|
||||
|
||||
let base: Int = isHexMode ? 16 : 10
|
||||
if let num = Int(numRef,radix: base)
|
||||
{
|
||||
if let num = Int(numRef, radix: base) {
|
||||
charval = num
|
||||
}
|
||||
|
||||
|
||||
if (charval == -1 || (charval >= 0xD800 && charval <= 0xDFFF) || charval > 0x10FFFF) {
|
||||
characterReferenceError("character outside of valid range")
|
||||
codeRef[0] = Tokeniser.replacementChar
|
||||
|
@ -175,14 +170,14 @@ final class Tokeniser
|
|||
}
|
||||
} else { // named
|
||||
// get as many letters as possible, and look for matching entities.
|
||||
let nameRef : String = reader.consumeLetterThenDigitSequence()
|
||||
let nameRef: String = reader.consumeLetterThenDigitSequence()
|
||||
let looksLegit: Bool = reader.matches(";")
|
||||
// found if a base named entity without a ;, or an extended entity with the ;.
|
||||
let found: Bool = (Entities.isBaseNamedEntity(nameRef) || (Entities.isNamedEntity(nameRef) && looksLegit))
|
||||
|
||||
if (!found) {
|
||||
reader.rewindToMark()
|
||||
if (looksLegit){ // named with semicolon
|
||||
if (looksLegit) { // named with semicolon
|
||||
characterReferenceError("invalid named referenece '\(nameRef)'")
|
||||
}
|
||||
return nil
|
||||
|
@ -192,7 +187,7 @@ final class Tokeniser
|
|||
reader.rewindToMark()
|
||||
return nil
|
||||
}
|
||||
if (!reader.matchConsume(";")){
|
||||
if (!reader.matchConsume(";")) {
|
||||
characterReferenceError("missing semicolon") // missing semi
|
||||
}
|
||||
let numChars: Int = Entities.codepointsForName(nameRef, codepoints: &multipointHolder)
|
||||
|
@ -240,45 +235,45 @@ final class Tokeniser
|
|||
}
|
||||
|
||||
func isAppropriateEndTagToken()throws->Bool {
|
||||
if(lastStartTag != nil){
|
||||
if(lastStartTag != nil) {
|
||||
let s = try tagPending.name()
|
||||
return s.equalsIgnoreCase(string: lastStartTag!)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func appropriateEndTagName()->String? {
|
||||
if (lastStartTag == nil){
|
||||
func appropriateEndTagName() -> String? {
|
||||
if (lastStartTag == nil) {
|
||||
return nil
|
||||
}
|
||||
return lastStartTag
|
||||
}
|
||||
|
||||
func error(_ state: TokeniserState) {
|
||||
if (errors != nil && errors!.canAddError()){
|
||||
if (errors != nil && errors!.canAddError()) {
|
||||
errors?.add(ParseError(reader.getPos(), "Unexpected character '\(String(reader.current()))' in input state [\(state.description)]"))
|
||||
}
|
||||
}
|
||||
|
||||
func eofError(_ state: TokeniserState) {
|
||||
if (errors != nil && errors!.canAddError()){
|
||||
if (errors != nil && errors!.canAddError()) {
|
||||
errors?.add(ParseError(reader.getPos(), "Unexpectedly reached end of file (EOF) in input state [\(state.description)]"))
|
||||
}
|
||||
}
|
||||
|
||||
private func characterReferenceError(_ message: String) {
|
||||
if (errors != nil && errors!.canAddError()){
|
||||
if (errors != nil && errors!.canAddError()) {
|
||||
errors?.add(ParseError(reader.getPos(), "Invalid character reference: \(message)"))
|
||||
}
|
||||
}
|
||||
|
||||
private func error(_ errorMsg: String) {
|
||||
if (errors != nil && errors!.canAddError()){
|
||||
if (errors != nil && errors!.canAddError()) {
|
||||
errors?.add(ParseError(reader.getPos(), errorMsg))
|
||||
}
|
||||
}
|
||||
|
||||
func currentNodeInHtmlNS()->Bool {
|
||||
func currentNodeInHtmlNS() -> Bool {
|
||||
// todo: implement namespaces correctly
|
||||
return true
|
||||
// Element currentNode = currentNode()
|
||||
|
@ -291,22 +286,21 @@ final class Tokeniser
|
|||
* @return unescaped string from reader
|
||||
*/
|
||||
func unescapeEntities(_ inAttribute: Bool)throws->String {
|
||||
let builder : StringBuilder = StringBuilder()
|
||||
let builder: StringBuilder = StringBuilder()
|
||||
while (!reader.isEmpty()) {
|
||||
builder.append(reader.consumeTo("&"))
|
||||
if (reader.matches("&")) {
|
||||
reader.consume()
|
||||
if let c = try consumeCharacterReference(nil, inAttribute)
|
||||
{
|
||||
if (c.count==0){
|
||||
if let c = try consumeCharacterReference(nil, inAttribute) {
|
||||
if (c.count==0) {
|
||||
builder.append("&")
|
||||
}else {
|
||||
} else {
|
||||
builder.appendCodePoint(c[0])
|
||||
if (c.count == 2){
|
||||
if (c.count == 2) {
|
||||
builder.appendCodePoint(c[1])
|
||||
}
|
||||
}
|
||||
}else {
|
||||
} else {
|
||||
builder.append("&")
|
||||
}
|
||||
}
|
||||
|
@ -314,7 +308,4 @@ final class Tokeniser
|
|||
return builder.toString()
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -12,22 +12,20 @@ protocol TokeniserStateProtocol {
|
|||
func read(_ t: Tokeniser, _ r: CharacterReader)throws
|
||||
}
|
||||
|
||||
public class TokeniserStateVars
|
||||
{
|
||||
public static let nullScalr : UnicodeScalar = "\u{0000}"
|
||||
public class TokeniserStateVars {
|
||||
public static let nullScalr: UnicodeScalar = "\u{0000}"
|
||||
|
||||
static let attributeSingleValueCharsSorted = ["'", "&", nullScalr].sorted()
|
||||
static let attributeDoubleValueCharsSorted = ["\"", "&", nullScalr].sorted()
|
||||
static let attributeNameCharsSorted = ["\t", "\n", "\r", UnicodeScalar.BackslashF, " ", "/", "=", ">", nullScalr, "\"", "'", "<"].sorted()
|
||||
static let attributeValueUnquoted = ["\t", "\n", "\r", UnicodeScalar.BackslashF, " ", "&", ">", nullScalr, "\"", "'", "<", "=", "`"].sorted()
|
||||
|
||||
static let replacementChar : UnicodeScalar = Tokeniser.replacementChar
|
||||
static let replacementStr : String = String(Tokeniser.replacementChar)
|
||||
static let eof : UnicodeScalar = CharacterReader.EOF
|
||||
static let replacementChar: UnicodeScalar = Tokeniser.replacementChar
|
||||
static let replacementStr: String = String(Tokeniser.replacementChar)
|
||||
static let eof: UnicodeScalar = CharacterReader.EOF
|
||||
}
|
||||
|
||||
enum TokeniserState: TokeniserStateProtocol
|
||||
{
|
||||
enum TokeniserState: TokeniserStateProtocol {
|
||||
case Data
|
||||
case CharacterReferenceInData
|
||||
case Rcdata
|
||||
|
@ -96,10 +94,8 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
case BogusDoctype
|
||||
case CdataSection
|
||||
|
||||
internal func read(_ t: Tokeniser, _ r: CharacterReader)throws
|
||||
{
|
||||
switch self
|
||||
{
|
||||
internal func read(_ t: Tokeniser, _ r: CharacterReader)throws {
|
||||
switch self {
|
||||
case .Data:
|
||||
switch (r.current()) {
|
||||
case "&":
|
||||
|
@ -286,8 +282,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
return
|
||||
}
|
||||
|
||||
func anythingElse(_ t: Tokeniser, _ r: CharacterReader)
|
||||
{
|
||||
func anythingElse(_ t: Tokeniser, _ r: CharacterReader) {
|
||||
t.emit("</" + t.dataBuffer.toString())
|
||||
r.unconsume()
|
||||
t.transition(.Rcdata)
|
||||
|
@ -296,44 +291,44 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
let c = r.consume()
|
||||
switch (c) {
|
||||
case "\t":
|
||||
if (try t.isAppropriateEndTagToken()){
|
||||
if (try t.isAppropriateEndTagToken()) {
|
||||
t.transition(.BeforeAttributeName)
|
||||
}else{
|
||||
} else {
|
||||
anythingElse(t, r)
|
||||
}
|
||||
break
|
||||
case "\n":
|
||||
if (try t.isAppropriateEndTagToken()){
|
||||
if (try t.isAppropriateEndTagToken()) {
|
||||
t.transition(.BeforeAttributeName)
|
||||
}else{
|
||||
} else {
|
||||
anythingElse(t, r)
|
||||
}
|
||||
break
|
||||
case "\r":
|
||||
if (try t.isAppropriateEndTagToken()){
|
||||
if (try t.isAppropriateEndTagToken()) {
|
||||
t.transition(.BeforeAttributeName)
|
||||
}else{
|
||||
} else {
|
||||
anythingElse(t, r)
|
||||
}
|
||||
break
|
||||
case UnicodeScalar.BackslashF:
|
||||
if (try t.isAppropriateEndTagToken()){
|
||||
if (try t.isAppropriateEndTagToken()) {
|
||||
t.transition(.BeforeAttributeName)
|
||||
}else{
|
||||
} else {
|
||||
anythingElse(t, r)
|
||||
}
|
||||
break
|
||||
case " ":
|
||||
if (try t.isAppropriateEndTagToken()){
|
||||
if (try t.isAppropriateEndTagToken()) {
|
||||
t.transition(.BeforeAttributeName)
|
||||
}else{
|
||||
} else {
|
||||
anythingElse(t, r)
|
||||
}
|
||||
break
|
||||
case "/":
|
||||
if (try t.isAppropriateEndTagToken()){
|
||||
if (try t.isAppropriateEndTagToken()) {
|
||||
t.transition(.SelfClosingStartTag)
|
||||
}else{
|
||||
} else {
|
||||
anythingElse(t, r)
|
||||
}
|
||||
break
|
||||
|
@ -341,8 +336,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
if (try t.isAppropriateEndTagToken()) {
|
||||
try t.emitTagPending()
|
||||
t.transition(.Data)
|
||||
}
|
||||
else{anythingElse(t, r)}
|
||||
} else {anythingElse(t, r)}
|
||||
break
|
||||
default:
|
||||
anythingElse(t, r)
|
||||
|
@ -601,7 +595,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
}
|
||||
break
|
||||
case .ScriptDataDoubleEscapeEnd:
|
||||
TokeniserState.handleDataDoubleEscapeTag(t,r, .ScriptDataEscaped, .ScriptDataDoubleEscaped)
|
||||
TokeniserState.handleDataDoubleEscapeTag(t, r, .ScriptDataEscaped, .ScriptDataDoubleEscaped)
|
||||
break
|
||||
case .BeforeAttributeName:
|
||||
// from tagname <xxx
|
||||
|
@ -724,7 +718,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
case .AfterAttributeName:
|
||||
let c = r.consume()
|
||||
switch (c) {
|
||||
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
|
||||
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
|
||||
// ignore
|
||||
break
|
||||
case "/":
|
||||
|
@ -746,7 +740,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
t.eofError(self)
|
||||
t.transition(.Data)
|
||||
break
|
||||
case "\"","'","<":
|
||||
case "\"", "'", "<":
|
||||
t.error(self)
|
||||
try t.tagPending.newAttribute()
|
||||
t.tagPending.appendAttributeName(c)
|
||||
|
@ -761,7 +755,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
case .BeforeAttributeValue:
|
||||
let c = r.consume()
|
||||
switch (c) {
|
||||
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
|
||||
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
|
||||
// ignore
|
||||
break
|
||||
case "\"":
|
||||
|
@ -789,7 +783,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
try t.emitTagPending()
|
||||
t.transition(.Data)
|
||||
break
|
||||
case "<","=","`":
|
||||
case "<", "=", "`":
|
||||
t.error(self)
|
||||
t.tagPending.appendAttributeValue(c)
|
||||
t.transition(.AttributeValue_unquoted)
|
||||
|
@ -801,9 +795,9 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
break
|
||||
case .AttributeValue_doubleQuoted:
|
||||
let value = r.consumeToAny(TokeniserStateVars.attributeDoubleValueCharsSorted)
|
||||
if (value.characters.count > 0){
|
||||
if (value.characters.count > 0) {
|
||||
t.tagPending.appendAttributeValue(value)
|
||||
}else{
|
||||
} else {
|
||||
t.tagPending.setEmptyAttributeValue()
|
||||
}
|
||||
|
||||
|
@ -814,9 +808,9 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
break
|
||||
case "&":
|
||||
|
||||
if let ref = try t.consumeCharacterReference("\"", true){
|
||||
if let ref = try t.consumeCharacterReference("\"", true) {
|
||||
t.tagPending.appendAttributeValue(ref)
|
||||
}else{
|
||||
} else {
|
||||
t.tagPending.appendAttributeValue("&")
|
||||
}
|
||||
break
|
||||
|
@ -835,9 +829,9 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
break
|
||||
case .AttributeValue_singleQuoted:
|
||||
let value = r.consumeToAny(TokeniserStateVars.attributeSingleValueCharsSorted)
|
||||
if (value.characters.count > 0){
|
||||
if (value.characters.count > 0) {
|
||||
t.tagPending.appendAttributeValue(value)
|
||||
}else{
|
||||
} else {
|
||||
t.tagPending.setEmptyAttributeValue()
|
||||
}
|
||||
|
||||
|
@ -848,9 +842,9 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
break
|
||||
case "&":
|
||||
|
||||
if let ref = try t.consumeCharacterReference("'", true){
|
||||
if let ref = try t.consumeCharacterReference("'", true) {
|
||||
t.tagPending.appendAttributeValue(ref)
|
||||
}else{
|
||||
} else {
|
||||
t.tagPending.appendAttributeValue("&")
|
||||
}
|
||||
break
|
||||
|
@ -869,19 +863,19 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
break
|
||||
case .AttributeValue_unquoted:
|
||||
let value = r.consumeToAnySorted(TokeniserStateVars.attributeValueUnquoted)
|
||||
if (value.characters.count > 0){
|
||||
if (value.characters.count > 0) {
|
||||
t.tagPending.appendAttributeValue(value)
|
||||
}
|
||||
|
||||
let c = r.consume()
|
||||
switch (c) {
|
||||
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
|
||||
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
|
||||
t.transition(.BeforeAttributeName)
|
||||
break
|
||||
case "&":
|
||||
if let ref = try t.consumeCharacterReference(">", true){
|
||||
if let ref = try t.consumeCharacterReference(">", true) {
|
||||
t.tagPending.appendAttributeValue(ref)
|
||||
}else{
|
||||
} else {
|
||||
t.tagPending.appendAttributeValue("&")
|
||||
}
|
||||
break
|
||||
|
@ -897,7 +891,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
t.eofError(self)
|
||||
t.transition(.Data)
|
||||
break
|
||||
case "\"","'","<","=","`":
|
||||
case "\"", "'", "<", "=", "`":
|
||||
t.error(self)
|
||||
t.tagPending.appendAttributeValue(c)
|
||||
break
|
||||
|
@ -910,7 +904,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
// CharacterReferenceInAttributeValue state handled inline
|
||||
let c = r.consume()
|
||||
switch (c) {
|
||||
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
|
||||
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
|
||||
t.transition(.BeforeAttributeName)
|
||||
break
|
||||
case "/":
|
||||
|
@ -952,7 +946,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
// todo: handle bogus comment starting from eof. when does that trigger?
|
||||
// rewind to capture character that lead us here
|
||||
r.unconsume()
|
||||
let comment : Token.Comment = Token.Comment()
|
||||
let comment: Token.Comment = Token.Comment()
|
||||
comment.bogus = true
|
||||
comment.data.append(r.consumeTo(">"))
|
||||
// todo: replace nullChar with replaceChar
|
||||
|
@ -1128,7 +1122,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
case .Doctype:
|
||||
let c = r.consume()
|
||||
switch (c) {
|
||||
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
|
||||
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
|
||||
t.transition(.BeforeDoctypeName)
|
||||
break
|
||||
case TokeniserStateVars.eof:
|
||||
|
@ -1154,7 +1148,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
}
|
||||
let c = r.consume()
|
||||
switch (c) {
|
||||
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
|
||||
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
|
||||
break // ignore whitespace
|
||||
case TokeniserStateVars.nullScalr:
|
||||
t.error(self)
|
||||
|
@ -1187,7 +1181,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
try t.emitDoctypePending()
|
||||
t.transition(.Data)
|
||||
break
|
||||
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
|
||||
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
|
||||
t.transition(.AfterDoctypeName)
|
||||
break
|
||||
case TokeniserStateVars.nullScalr:
|
||||
|
@ -1212,9 +1206,9 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
t.transition(.Data)
|
||||
return
|
||||
}
|
||||
if (r.matchesAny("\t", "\n", "\r", UnicodeScalar.BackslashF, " ")){
|
||||
if (r.matchesAny("\t", "\n", "\r", UnicodeScalar.BackslashF, " ")) {
|
||||
r.advance() // ignore whitespace
|
||||
}else if (r.matches(">")) {
|
||||
} else if (r.matches(">")) {
|
||||
try t.emitDoctypePending()
|
||||
t.advanceTransition(.Data)
|
||||
} else if (r.matchConsumeIgnoreCase("PUBLIC")) {
|
||||
|
@ -1230,7 +1224,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
case .AfterDoctypePublicKeyword:
|
||||
let c = r.consume()
|
||||
switch (c) {
|
||||
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
|
||||
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
|
||||
t.transition(.BeforeDoctypePublicIdentifier)
|
||||
break
|
||||
case "\"":
|
||||
|
@ -1264,7 +1258,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
case .BeforeDoctypePublicIdentifier:
|
||||
let c = r.consume()
|
||||
switch (c) {
|
||||
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
|
||||
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
|
||||
break
|
||||
case "\"":
|
||||
// set public id to empty string
|
||||
|
@ -1347,7 +1341,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
case .AfterDoctypePublicIdentifier:
|
||||
let c = r.consume()
|
||||
switch (c) {
|
||||
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
|
||||
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
|
||||
t.transition(.BetweenDoctypePublicAndSystemIdentifiers)
|
||||
break
|
||||
case ">":
|
||||
|
@ -1379,7 +1373,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
case .BetweenDoctypePublicAndSystemIdentifiers:
|
||||
let c = r.consume()
|
||||
switch (c) {
|
||||
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
|
||||
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
|
||||
break
|
||||
case ">":
|
||||
try t.emitDoctypePending()
|
||||
|
@ -1410,7 +1404,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
case .AfterDoctypeSystemKeyword:
|
||||
let c = r.consume()
|
||||
switch (c) {
|
||||
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
|
||||
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
|
||||
t.transition(.BeforeDoctypeSystemIdentifier)
|
||||
break
|
||||
case ">":
|
||||
|
@ -1444,7 +1438,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
case .BeforeDoctypeSystemIdentifier:
|
||||
let c = r.consume()
|
||||
switch (c) {
|
||||
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
|
||||
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
|
||||
break
|
||||
case "\"":
|
||||
// set system id to empty string
|
||||
|
@ -1527,7 +1521,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
case .AfterDoctypeSystemIdentifier:
|
||||
let c = r.consume()
|
||||
switch (c) {
|
||||
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
|
||||
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
|
||||
break
|
||||
case ">":
|
||||
try t.emitDoctypePending()
|
||||
|
@ -1570,14 +1564,6 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var description: String {return String(describing: type(of: self))}
|
||||
/**
|
||||
* Handles RawtextEndTagName, ScriptDataEndTagName, and ScriptDataEscapedEndTagName. Same body impl, just
|
||||
|
@ -1595,7 +1581,7 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
if (try t.isAppropriateEndTagToken() && !r.isEmpty()) {
|
||||
let c = r.consume()
|
||||
switch (c) {
|
||||
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
|
||||
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
|
||||
t.transition(BeforeAttributeName)
|
||||
break
|
||||
case "/":
|
||||
|
@ -1641,9 +1627,9 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
|
||||
private static func readCharRef(_ t: Tokeniser, _ advance: TokeniserState)throws {
|
||||
let c = try t.consumeCharacterReference(nil, false)
|
||||
if (c == nil){
|
||||
if (c == nil) {
|
||||
t.emit("&")
|
||||
}else{
|
||||
} else {
|
||||
t.emit(c!)
|
||||
}
|
||||
t.transition(advance)
|
||||
|
@ -1669,10 +1655,10 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
|
||||
let c = r.consume()
|
||||
switch (c) {
|
||||
case "\t","\n","\r",UnicodeScalar.BackslashF," ","/",">":
|
||||
if (t.dataBuffer.toString() == "script"){
|
||||
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ", "/", ">":
|
||||
if (t.dataBuffer.toString() == "script") {
|
||||
t.transition(primary)
|
||||
}else{
|
||||
} else {
|
||||
t.transition(fallback)
|
||||
}
|
||||
t.emit(c)
|
||||
|
@ -1683,7 +1669,4 @@ enum TokeniserState: TokeniserStateProtocol
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -21,17 +21,16 @@ public class TreeBuilder {
|
|||
private let start: Token.StartTag = Token.StartTag() // start tag to process
|
||||
private let end: Token.EndTag = Token.EndTag()
|
||||
|
||||
public func defaultSettings()->ParseSettings{preconditionFailure("This method must be overridden")}
|
||||
|
||||
public func defaultSettings() -> ParseSettings {preconditionFailure("This method must be overridden")}
|
||||
|
||||
public init() {
|
||||
doc = Document("")
|
||||
reader = CharacterReader("")
|
||||
tokeniser = Tokeniser(reader,nil)
|
||||
tokeniser = Tokeniser(reader, nil)
|
||||
stack = Array<Element>()
|
||||
baseUri = ""
|
||||
errors = ParseErrorList(0,0)
|
||||
settings = ParseSettings(false,false)
|
||||
errors = ParseErrorList(0, 0)
|
||||
settings = ParseSettings(false, false)
|
||||
}
|
||||
|
||||
public func initialiseParse(_ input: String, _ baseUri: String, _ errors: ParseErrorList, _ settings: ParseSettings) {
|
||||
|
@ -44,7 +43,6 @@ public class TreeBuilder {
|
|||
self.baseUri = baseUri
|
||||
}
|
||||
|
||||
|
||||
func parse(_ input: String, _ baseUri: String, _ errors: ParseErrorList, _ settings: ParseSettings)throws->Document {
|
||||
initialiseParse(input, baseUri, errors, settings)
|
||||
try runParser()
|
||||
|
@ -57,14 +55,14 @@ public class TreeBuilder {
|
|||
try process(token)
|
||||
token.reset()
|
||||
|
||||
if (token.type == Token.TokenType.EOF){
|
||||
if (token.type == Token.TokenType.EOF) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func process(_ token: Token)throws->Bool{preconditionFailure("This method must be overridden")}
|
||||
public func process(_ token: Token)throws->Bool {preconditionFailure("This method must be overridden")}
|
||||
|
||||
@discardableResult
|
||||
public func processStartTag(_ name: String)throws->Bool {
|
||||
|
@ -93,8 +91,7 @@ public class TreeBuilder {
|
|||
return try process(end.reset().name(name))
|
||||
}
|
||||
|
||||
|
||||
public func currentElement()->Element? {
|
||||
public func currentElement() -> Element? {
|
||||
let size: Int = stack.count
|
||||
return size > 0 ? stack[size-1] : nil
|
||||
}
|
||||
|
|
|
@ -15,9 +15,8 @@ private let alphaNumericSet = CharacterSet.alphanumerics
|
|||
private let symbolSet = CharacterSet.symbols
|
||||
private let digitSet = CharacterSet.decimalDigits
|
||||
|
||||
extension UnicodeScalar
|
||||
{
|
||||
public static let BackslashF : UnicodeScalar = UnicodeScalar(12)
|
||||
extension UnicodeScalar {
|
||||
public static let BackslashF: UnicodeScalar = UnicodeScalar(12)
|
||||
|
||||
func isMemberOfCharacterSet(_ set: CharacterSet) -> Bool {
|
||||
return set.contains(self)
|
||||
|
@ -43,7 +42,7 @@ extension UnicodeScalar
|
|||
|
||||
switch self {
|
||||
|
||||
case " ", "\t", "\n", "\r" ,UnicodeScalar.BackslashF: return true
|
||||
case " ", "\t", "\n", "\r", UnicodeScalar.BackslashF: return true
|
||||
|
||||
case "\u{000C}", "\u{000B}", "\u{0085}": return true // Form Feed, vertical tab, next line (nel)
|
||||
|
||||
|
|
|
@ -8,15 +8,14 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
struct Validate
|
||||
{
|
||||
struct Validate {
|
||||
|
||||
/**
|
||||
* Validates that the object is not null
|
||||
* @param obj object to test
|
||||
*/
|
||||
public static func notNull(obj:Any?) throws {
|
||||
if (obj == nil){
|
||||
if (obj == nil) {
|
||||
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Object must not be null")
|
||||
}
|
||||
}
|
||||
|
@ -26,8 +25,8 @@ struct Validate
|
|||
* @param obj object to test
|
||||
* @param msg message to output if validation fails
|
||||
*/
|
||||
public static func notNull(obj:AnyObject?, msg:String) throws {
|
||||
if (obj == nil){
|
||||
public static func notNull(obj: AnyObject?, msg: String) throws {
|
||||
if (obj == nil) {
|
||||
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: msg)
|
||||
}
|
||||
}
|
||||
|
@ -36,8 +35,8 @@ struct Validate
|
|||
* Validates that the value is true
|
||||
* @param val object to test
|
||||
*/
|
||||
public static func isTrue(val:Bool) throws {
|
||||
if (!val){
|
||||
public static func isTrue(val: Bool) throws {
|
||||
if (!val) {
|
||||
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Must be true")
|
||||
}
|
||||
}
|
||||
|
@ -47,8 +46,8 @@ struct Validate
|
|||
* @param val object to test
|
||||
* @param msg message to output if validation fails
|
||||
*/
|
||||
public static func isTrue(val: Bool ,msg: String) throws {
|
||||
if (!val){
|
||||
public static func isTrue(val: Bool, msg: String) throws {
|
||||
if (!val) {
|
||||
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: msg)
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +57,7 @@ struct Validate
|
|||
* @param val object to test
|
||||
*/
|
||||
public static func isFalse(val: Bool) throws {
|
||||
if (val){
|
||||
if (val) {
|
||||
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Must be false")
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +68,7 @@ struct Validate
|
|||
* @param msg message to output if validation fails
|
||||
*/
|
||||
public static func isFalse(val: Bool, msg: String) throws {
|
||||
if (val){
|
||||
if (val) {
|
||||
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: msg)
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +78,7 @@ struct Validate
|
|||
* @param objects the array to test
|
||||
*/
|
||||
public static func noNullElements(objects: [AnyObject?]) throws {
|
||||
try noNullElements(objects: objects, msg: "Array must not contain any null objects");
|
||||
try noNullElements(objects: objects, msg: "Array must not contain any null objects")
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,7 +88,7 @@ struct Validate
|
|||
*/
|
||||
public static func noNullElements(objects: [AnyObject?], msg: String) throws {
|
||||
for obj in objects {
|
||||
if (obj == nil){
|
||||
if (obj == nil) {
|
||||
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: msg)
|
||||
}
|
||||
}
|
||||
|
@ -99,9 +98,8 @@ struct Validate
|
|||
* Validates that the string is not empty
|
||||
* @param string the string to test
|
||||
*/
|
||||
public static func notEmpty(string: String?) throws
|
||||
{
|
||||
if (string == nil || string?.characters.count == 0){
|
||||
public static func notEmpty(string: String?) throws {
|
||||
if (string == nil || string?.characters.count == 0) {
|
||||
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "String must not be empty")
|
||||
}
|
||||
|
||||
|
@ -113,7 +111,7 @@ struct Validate
|
|||
* @param msg message to output if validation fails
|
||||
*/
|
||||
public static func notEmpty(string: String?, msg: String ) throws {
|
||||
if (string == nil || string?.characters.count == 0){
|
||||
if (string == nil || string?.characters.count == 0) {
|
||||
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: msg)
|
||||
}
|
||||
}
|
||||
|
@ -126,7 +124,6 @@ struct Validate
|
|||
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: msg)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Helper
|
||||
*/
|
||||
|
|
|
@ -57,21 +57,19 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
|
||||
public class Whitelist {
|
||||
private var tagNames : Set<TagName> // tags allowed, lower case. e.g. [p, br, span]
|
||||
private var attributes : Dictionary<TagName, Set<AttributeKey>> // tag -> attribute[]. allowed attributes [href] for a tag.
|
||||
private var enforcedAttributes : Dictionary<TagName, Dictionary<AttributeKey, AttributeValue>> // always set these attribute values
|
||||
private var protocols : Dictionary<TagName, Dictionary<AttributeKey, Set<Protocol>>> // allowed URL protocols for attributes
|
||||
private var preserveRelativeLinks : Bool // option to preserve relative links
|
||||
|
||||
private var tagNames: Set<TagName> // tags allowed, lower case. e.g. [p, br, span]
|
||||
private var attributes: Dictionary<TagName, Set<AttributeKey>> // tag -> attribute[]. allowed attributes [href] for a tag.
|
||||
private var enforcedAttributes: Dictionary<TagName, Dictionary<AttributeKey, AttributeValue>> // always set these attribute values
|
||||
private var protocols: Dictionary<TagName, Dictionary<AttributeKey, Set<Protocol>>> // allowed URL protocols for attributes
|
||||
private var preserveRelativeLinks: Bool // option to preserve relative links
|
||||
|
||||
/**
|
||||
This whitelist allows only text nodes: all HTML will be stripped.
|
||||
|
||||
@return whitelist
|
||||
*/
|
||||
public static func none()->Whitelist {
|
||||
public static func none() -> Whitelist {
|
||||
return Whitelist()
|
||||
}
|
||||
|
||||
|
@ -172,10 +170,6 @@ public class Whitelist {
|
|||
.addProtocols("q", "cite", "http", "https")
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Create a new, empty whitelist. Generally it will be better to start with a default prepared whitelist instead.
|
||||
|
||||
|
@ -200,8 +194,7 @@ public class Whitelist {
|
|||
*/
|
||||
@discardableResult
|
||||
open func addTags(_ tags: String...)throws ->Whitelist {
|
||||
for tagName in tags
|
||||
{
|
||||
for tagName in tags {
|
||||
try Validate.notEmpty(string: tagName)
|
||||
tagNames.insert(TagName.valueOf(tagName))
|
||||
}
|
||||
|
@ -220,7 +213,7 @@ public class Whitelist {
|
|||
|
||||
for tag in tags {
|
||||
try Validate.notEmpty(string: tag)
|
||||
let tagName : TagName = TagName.valueOf(tag)
|
||||
let tagName: TagName = TagName.valueOf(tag)
|
||||
|
||||
if(tagNames.contains(tagName)) { // Only look in sub-maps if tag was allowed
|
||||
tagNames.remove(tagName)
|
||||
|
@ -253,19 +246,17 @@ public class Whitelist {
|
|||
try Validate.isTrue(val: keys.count > 0, msg: "No attributes supplied.")
|
||||
|
||||
let tagName = TagName.valueOf(tag)
|
||||
if (!tagNames.contains(tagName)){
|
||||
if (!tagNames.contains(tagName)) {
|
||||
tagNames.insert(tagName)
|
||||
}
|
||||
var attributeSet = Set<AttributeKey>()
|
||||
for key in keys
|
||||
{
|
||||
for key in keys {
|
||||
try Validate.notEmpty(string: key)
|
||||
attributeSet.insert(AttributeKey.valueOf(key))
|
||||
}
|
||||
|
||||
if var currentSet = attributes[tagName]
|
||||
{
|
||||
for at in attributeSet{
|
||||
if var currentSet = attributes[tagName] {
|
||||
for at in attributeSet {
|
||||
currentSet.insert(at)
|
||||
}
|
||||
attributes[tagName] = currentSet
|
||||
|
@ -296,39 +287,34 @@ public class Whitelist {
|
|||
try Validate.notEmpty(string: tag)
|
||||
try Validate.isTrue(val: keys.count > 0, msg: "No attributes supplied.")
|
||||
|
||||
let tagName : TagName = TagName.valueOf(tag)
|
||||
let tagName: TagName = TagName.valueOf(tag)
|
||||
var attributeSet = Set<AttributeKey>()
|
||||
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
|
||||
{
|
||||
if var currentSet = attributes[tagName] {
|
||||
for l in attributeSet {
|
||||
currentSet.remove(l)
|
||||
}
|
||||
attributes[tagName] = currentSet
|
||||
if(currentSet.isEmpty){ // Remove tag from attribute map if no attributes are allowed for tag
|
||||
if(currentSet.isEmpty) { // Remove tag from attribute map if no attributes are allowed for tag
|
||||
attributes.removeValue(forKey: tagName)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if(tag == ":all"){ // Attribute needs to be removed from all individually set tags
|
||||
for name in attributes.keys
|
||||
{
|
||||
var currentSet : Set<AttributeKey> = attributes[name]!
|
||||
for l in attributeSet{
|
||||
if(tag == ":all") { // Attribute needs to be removed from all individually set tags
|
||||
for name in attributes.keys {
|
||||
var currentSet: Set<AttributeKey> = attributes[name]!
|
||||
for l in attributeSet {
|
||||
currentSet.remove(l)
|
||||
}
|
||||
attributes[name] = currentSet
|
||||
if(currentSet.isEmpty){ // Remove tag from attribute map if no attributes are allowed for tag
|
||||
if(currentSet.isEmpty) { // Remove tag from attribute map if no attributes are allowed for tag
|
||||
attributes.removeValue(forKey: name)
|
||||
}
|
||||
}
|
||||
|
@ -355,17 +341,17 @@ public class Whitelist {
|
|||
try Validate.notEmpty(string: key)
|
||||
try Validate.notEmpty(string: value)
|
||||
|
||||
let tagName : TagName = TagName.valueOf(tag)
|
||||
if (!tagNames.contains(tagName)){
|
||||
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)
|
||||
let attrKey: AttributeKey = AttributeKey.valueOf(key)
|
||||
let attrVal: AttributeValue = AttributeValue.valueOf(value)
|
||||
|
||||
if (enforcedAttributes[tagName] != nil) {
|
||||
enforcedAttributes[tagName]?[attrKey] = attrVal
|
||||
} else {
|
||||
var attrMap : Dictionary<AttributeKey, AttributeValue> = Dictionary<AttributeKey, AttributeValue>()
|
||||
var attrMap: Dictionary<AttributeKey, AttributeValue> = Dictionary<AttributeKey, AttributeValue>()
|
||||
attrMap[attrKey] = attrVal
|
||||
enforcedAttributes[tagName] = attrMap
|
||||
}
|
||||
|
@ -384,14 +370,14 @@ public class Whitelist {
|
|||
try Validate.notEmpty(string: tag)
|
||||
try Validate.notEmpty(string: key)
|
||||
|
||||
let tagName : TagName = TagName.valueOf(tag)
|
||||
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]!
|
||||
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
|
||||
if(attrMap.isEmpty) { // Remove tag from enforced attribute map if no enforced attributes are present
|
||||
enforcedAttributes.removeValue(forKey: tagName)
|
||||
}
|
||||
}
|
||||
|
@ -414,7 +400,7 @@ public class Whitelist {
|
|||
* @see #addProtocols
|
||||
*/
|
||||
@discardableResult
|
||||
open func preserveRelativeLinks(_ preserve: Bool)->Whitelist {
|
||||
open func preserveRelativeLinks(_ preserve: Bool) -> Whitelist {
|
||||
preserveRelativeLinks = preserve
|
||||
return self
|
||||
}
|
||||
|
@ -441,10 +427,10 @@ public class Whitelist {
|
|||
try Validate.notEmpty(string: key)
|
||||
try Validate.notNull(obj: protocols)
|
||||
|
||||
let tagName : TagName = TagName.valueOf(tag)
|
||||
let attrKey : AttributeKey = AttributeKey.valueOf(key)
|
||||
var attrMap : Dictionary<AttributeKey, Set<Protocol>>
|
||||
var protSet : Set<Protocol>
|
||||
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]!
|
||||
|
@ -460,10 +446,9 @@ public class Whitelist {
|
|||
attrMap[attrKey] = protSet
|
||||
self.protocols[tagName] = attrMap
|
||||
}
|
||||
for ptl in protocols
|
||||
{
|
||||
for ptl in protocols {
|
||||
try Validate.notEmpty(string: ptl)
|
||||
let prot : Protocol = Protocol.valueOf(ptl)
|
||||
let prot: Protocol = Protocol.valueOf(ptl)
|
||||
protSet.insert(prot)
|
||||
}
|
||||
attrMap[attrKey] = protSet
|
||||
|
@ -487,24 +472,23 @@ public class Whitelist {
|
|||
try Validate.notEmpty(string: tag)
|
||||
try Validate.notEmpty(string: key)
|
||||
|
||||
let tagName : TagName = TagName.valueOf(tag)
|
||||
let attrKey : AttributeKey = AttributeKey.valueOf(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]!
|
||||
var attrMap: Dictionary<AttributeKey, Set<Protocol>>= self.protocols[tagName]!
|
||||
if(attrMap[attrKey] != nil) {
|
||||
var protSet : Set<Protocol> = attrMap[attrKey]!
|
||||
for ptl in protocols
|
||||
{
|
||||
var protSet: Set<Protocol> = attrMap[attrKey]!
|
||||
for ptl in protocols {
|
||||
try Validate.notEmpty(string: ptl)
|
||||
let prot : Protocol = Protocol.valueOf(ptl)
|
||||
let prot: Protocol = Protocol.valueOf(ptl)
|
||||
protSet.remove(prot)
|
||||
}
|
||||
attrMap[attrKey] = protSet
|
||||
|
||||
if(protSet.isEmpty) { // Remove protocol set if empty
|
||||
attrMap.removeValue(forKey: attrKey)
|
||||
if(attrMap.isEmpty){ // Remove entry for tag if empty
|
||||
if(attrMap.isEmpty) { // Remove entry for tag if empty
|
||||
self.protocols.removeValue(forKey: tagName)
|
||||
}
|
||||
|
||||
|
@ -520,7 +504,7 @@ public class Whitelist {
|
|||
* @param tag test tag
|
||||
* @return true if allowed
|
||||
*/
|
||||
public func isSafeTag(_ tag: String)->Bool {
|
||||
public func isSafeTag(_ tag: String) -> Bool {
|
||||
return tagNames.contains(TagName.valueOf(tag))
|
||||
}
|
||||
|
||||
|
@ -531,9 +515,9 @@ public class Whitelist {
|
|||
* @param attr attribute under test
|
||||
* @return true if allowed
|
||||
*/
|
||||
public func isSafeAttribute(_ tagName: String, _ el: Element, _ attr: Attribute)->Bool {
|
||||
let tag : TagName = TagName.valueOf(tagName)
|
||||
let key : AttributeKey = AttributeKey.valueOf(attr.getKey())
|
||||
public func isSafeAttribute(_ tagName: String, _ el: Element, _ attr: Attribute) -> Bool {
|
||||
let tag: TagName = TagName.valueOf(tagName)
|
||||
let key: AttributeKey = AttributeKey.valueOf(attr.getKey())
|
||||
|
||||
if (attributes[tag] != nil) {
|
||||
if (attributes[tag]?.contains(key))! {
|
||||
|
@ -554,16 +538,15 @@ public class Whitelist {
|
|||
private func testValidProtocol(_ el: Element, _ attr: Attribute, _ protocols: Set<Protocol>)throws->Bool {
|
||||
// try to resolve relative urls to abs, and optionally update the attribute so output html has abs.
|
||||
// rels without a baseuri get removed
|
||||
var value : String = try el.absUrl(attr.getKey())
|
||||
if (value.characters.count == 0){
|
||||
var value: String = try el.absUrl(attr.getKey())
|
||||
if (value.characters.count == 0) {
|
||||
value = attr.getValue() // if it could not be made abs, run as-is to allow custom unknown protocols
|
||||
if (!preserveRelativeLinks){
|
||||
if (!preserveRelativeLinks) {
|
||||
attr.setValue(value: value)
|
||||
}
|
||||
|
||||
for ptl in protocols
|
||||
{
|
||||
var prot : String = ptl.toString()
|
||||
for ptl in protocols {
|
||||
var prot: String = ptl.toString()
|
||||
|
||||
if (prot=="#") { // allows anchor links
|
||||
if (isValidAnchor(value)) {
|
||||
|
@ -584,18 +567,15 @@ public class Whitelist {
|
|||
return false
|
||||
}
|
||||
|
||||
private func isValidAnchor(_ value: String)->Bool
|
||||
{
|
||||
private func isValidAnchor(_ value: String) -> Bool {
|
||||
return value.startsWith("#") && !(Pattern(".*\\s.*").matcher(in: value).count > 0)
|
||||
}
|
||||
|
||||
public func getEnforcedAttributes(_ tagName: String)throws->Attributes {
|
||||
let attrs: Attributes = Attributes()
|
||||
let tag: TagName = TagName.valueOf(tagName)
|
||||
if let keyVals: Dictionary<AttributeKey, AttributeValue> = enforcedAttributes[tag]
|
||||
{
|
||||
for entry in keyVals
|
||||
{
|
||||
if let keyVals: Dictionary<AttributeKey, AttributeValue> = enforcedAttributes[tag] {
|
||||
for entry in keyVals {
|
||||
try attrs.put(entry.key.toString(), entry.value.toString())
|
||||
}
|
||||
}
|
||||
|
@ -604,61 +584,56 @@ public class Whitelist {
|
|||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// named types for config. All just hold strings, but here for my sanity.
|
||||
|
||||
open class TagName : TypedValue {
|
||||
open class TagName: TypedValue {
|
||||
override init(_ value: String) {
|
||||
super.init(value)
|
||||
}
|
||||
|
||||
static func valueOf(_ value: String)->TagName{
|
||||
static func valueOf(_ value: String) -> TagName {
|
||||
return TagName(value)
|
||||
}
|
||||
}
|
||||
|
||||
open class AttributeKey : TypedValue {
|
||||
open class AttributeKey: TypedValue {
|
||||
override init(_ value: String) {
|
||||
super.init(value)
|
||||
}
|
||||
|
||||
static func valueOf(_ value: String)->AttributeKey {
|
||||
static func valueOf(_ value: String) -> AttributeKey {
|
||||
return AttributeKey(value)
|
||||
}
|
||||
}
|
||||
|
||||
open class AttributeValue : TypedValue {
|
||||
open class AttributeValue: TypedValue {
|
||||
override init(_ value: String) {
|
||||
super.init(value)
|
||||
}
|
||||
|
||||
static func valueOf(_ value: String)->AttributeValue {
|
||||
static func valueOf(_ value: String) -> AttributeValue {
|
||||
return AttributeValue(value)
|
||||
}
|
||||
}
|
||||
|
||||
open class Protocol : TypedValue {
|
||||
open class Protocol: TypedValue {
|
||||
override init(_ value: String) {
|
||||
super.init(value)
|
||||
}
|
||||
|
||||
static func valueOf(_ value: String)->Protocol {
|
||||
static func valueOf(_ value: String) -> Protocol {
|
||||
return Protocol(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
open class TypedValue
|
||||
{
|
||||
fileprivate let value : String
|
||||
open class TypedValue {
|
||||
fileprivate let value: String
|
||||
|
||||
init(_ value: String) {
|
||||
self.value = value
|
||||
}
|
||||
|
||||
public func toString()->String {
|
||||
public func toString() -> String {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
@ -667,13 +642,12 @@ extension TypedValue: Hashable {
|
|||
public var hashValue: Int {
|
||||
let prime = 31
|
||||
var result = 1
|
||||
result = Int.addWithOverflow(Int.multiplyWithOverflow(prime,result).0, value.hash).0
|
||||
result = Int.addWithOverflow(Int.multiplyWithOverflow(prime, result).0, value.hash).0
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
public func == (lhs: TypedValue, rhs: TypedValue) -> Bool
|
||||
{
|
||||
if(lhs === rhs){return true}
|
||||
public func == (lhs: TypedValue, rhs: TypedValue) -> Bool {
|
||||
if(lhs === rhs) {return true}
|
||||
return lhs.value == rhs.value
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import Foundation
|
|||
An XML Declaration.
|
||||
|
||||
@author Jonathan Hedley, jonathan@hedley.net */
|
||||
public class XmlDeclaration : Node {
|
||||
public class XmlDeclaration: Node {
|
||||
private let _name: String
|
||||
private let isProcessingInstruction: Bool // <! if true, <? if false, declaration (and last data char should be ?)
|
||||
|
||||
|
@ -28,16 +28,15 @@ public class XmlDeclaration : Node {
|
|||
super.init(baseUri)
|
||||
}
|
||||
|
||||
public override func nodeName()->String {
|
||||
public override func nodeName() -> String {
|
||||
return "#declaration"
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the name of this declaration.
|
||||
* @return name of this declaration.
|
||||
*/
|
||||
public func name()->String {
|
||||
public func name() -> String {
|
||||
return _name
|
||||
}
|
||||
|
||||
|
@ -54,9 +53,9 @@ public class XmlDeclaration : Node {
|
|||
.append("<")
|
||||
.append(isProcessingInstruction ? "!" : "?")
|
||||
.append(_name)
|
||||
do{
|
||||
do {
|
||||
try attributes?.html(accum: accum, out: out)
|
||||
}catch{}
|
||||
} catch {}
|
||||
accum
|
||||
.append(isProcessingInstruction ? "!" : "?")
|
||||
.append(">")
|
||||
|
@ -64,26 +63,23 @@ public class XmlDeclaration : Node {
|
|||
|
||||
override func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {}
|
||||
|
||||
public override func toString()->String {
|
||||
do{
|
||||
public override func toString() -> String {
|
||||
do {
|
||||
return try outerHtml()
|
||||
}catch{}
|
||||
} catch {}
|
||||
return ""
|
||||
}
|
||||
|
||||
public override func copy(with zone: NSZone? = nil) -> Any
|
||||
{
|
||||
let clone = XmlDeclaration(_name,baseUri!,isProcessingInstruction)
|
||||
public override func copy(with zone: NSZone? = nil) -> Any {
|
||||
let clone = XmlDeclaration(_name, baseUri!, isProcessingInstruction)
|
||||
return copy(clone: clone)
|
||||
}
|
||||
|
||||
public override func copy(parent: Node?)->Node
|
||||
{
|
||||
let clone = XmlDeclaration(_name,baseUri!,isProcessingInstruction)
|
||||
return copy(clone: clone,parent: parent)
|
||||
public override func copy(parent: Node?) -> Node {
|
||||
let clone = XmlDeclaration(_name, baseUri!, isProcessingInstruction)
|
||||
return copy(clone: clone, parent: parent)
|
||||
}
|
||||
public override func copy(clone: Node, parent: Node?)->Node
|
||||
{
|
||||
return super.copy(clone: clone,parent: parent)
|
||||
public override func copy(clone: Node, parent: Node?) -> Node {
|
||||
return super.copy(clone: clone, parent: parent)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,18 +14,16 @@ import Foundation
|
|||
* <p>Usage example: {@code Document xmlDoc = Jsoup.parse(html, baseUrl, Parser.xmlParser())}</p>
|
||||
*
|
||||
*/
|
||||
public class XmlTreeBuilder : TreeBuilder {
|
||||
public class XmlTreeBuilder: TreeBuilder {
|
||||
|
||||
public override init(){
|
||||
public override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
|
||||
public override func defaultSettings()->ParseSettings {
|
||||
public override func defaultSettings() -> ParseSettings {
|
||||
return ParseSettings.preserveCase
|
||||
}
|
||||
|
||||
|
||||
public func parse(_ input: String, _ baseUri: String)throws->Document {
|
||||
return try parse(input, baseUri, ParseErrorList.noTracking(), ParseSettings.preserveCase)
|
||||
}
|
||||
|
@ -36,7 +34,6 @@ public class XmlTreeBuilder : TreeBuilder {
|
|||
doc.outputSettings().syntax(syntax: OutputSettings.Syntax.xml)
|
||||
}
|
||||
|
||||
|
||||
override public func process(_ token: Token)throws->Bool {
|
||||
// start tag, end tag, doctype, comment, character, eof
|
||||
switch (token.type) {
|
||||
|
@ -121,24 +118,21 @@ public class XmlTreeBuilder : TreeBuilder {
|
|||
let elName: String = try endTag.name()
|
||||
var firstFound: Element? = nil
|
||||
|
||||
for pos in (0..<stack.count).reversed()
|
||||
{
|
||||
for pos in (0..<stack.count).reversed() {
|
||||
let next: Element = stack[pos]
|
||||
if (next.nodeName().equals(elName)) {
|
||||
firstFound = next
|
||||
break
|
||||
}
|
||||
}
|
||||
if (firstFound == nil){
|
||||
if (firstFound == nil) {
|
||||
return // not found, skip
|
||||
}
|
||||
|
||||
|
||||
for pos in (0..<stack.count).reversed()
|
||||
{
|
||||
for pos in (0..<stack.count).reversed() {
|
||||
let next: Element = stack[pos]
|
||||
stack.remove(at: pos)
|
||||
if (next == firstFound!){
|
||||
if (next == firstFound!) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
// Copyright © 2016 Nabil Chatbi. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
import XCTest
|
||||
@testable import SwiftSoupTests
|
||||
|
||||
|
|
|
@ -58,13 +58,13 @@ class AttributeParseTest: XCTestCase {
|
|||
let html: String = "<a id=1 href='?foo=bar&mid<=true'>One</a> <a id=2 href='?foo=bar<qux&lg=1'>Two</a>"
|
||||
let els: Elements = try SwiftSoup.parse(html).select("a")
|
||||
XCTAssertEqual("?foo=bar&mid<=true", try els.first()!.attr("href"))
|
||||
XCTAssertEqual("?foo=bar<qux&lg=1",try els.last()!.attr("href"))
|
||||
XCTAssertEqual("?foo=bar<qux&lg=1", try els.last()!.attr("href"))
|
||||
}
|
||||
|
||||
func testmoreAttributeUnescapes()throws {
|
||||
let html: String = "<a href='&wr_id=123&mid-size=true&ok=&wr'>Check</a>"
|
||||
let els: Elements = try SwiftSoup.parse(html).select("a")
|
||||
XCTAssertEqual("&wr_id=123&mid-size=true&ok=&wr",try els.first()!.attr("href"))
|
||||
XCTAssertEqual("&wr_id=123&mid-size=true&ok=&wr", try els.first()!.attr("href"))
|
||||
}
|
||||
|
||||
func testparsesBooleanAttributes()throws {
|
||||
|
@ -76,12 +76,12 @@ class AttributeParseTest: XCTestCase {
|
|||
XCTAssertEqual("", try el.attr("empty"))
|
||||
|
||||
let attributes: Array<Attribute> = el.getAttributes()!.asList()
|
||||
XCTAssertEqual(3, attributes.count,"There should be 3 attribute present")
|
||||
XCTAssertEqual(3, attributes.count, "There should be 3 attribute present")
|
||||
|
||||
// Assuming the list order always follows the parsed html
|
||||
XCTAssertFalse((attributes[0] as? BooleanAttribute) != nil,"'normal' attribute should not be boolean")
|
||||
XCTAssertTrue((attributes[1] as? BooleanAttribute) != nil,"'boolean' attribute should be boolean")
|
||||
XCTAssertFalse((attributes[2] as? BooleanAttribute) != nil,"'empty' attribute should not be boolean")
|
||||
XCTAssertFalse((attributes[0] as? BooleanAttribute) != nil, "'normal' attribute should not be boolean")
|
||||
XCTAssertTrue((attributes[1] as? BooleanAttribute) != nil, "'boolean' attribute should be boolean")
|
||||
XCTAssertFalse((attributes[2] as? BooleanAttribute) != nil, "'empty' attribute should not be boolean")
|
||||
|
||||
XCTAssertEqual(html, try el.outerHtml())
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ class AttributeParseTest: XCTestCase {
|
|||
func testdropsSlashFromAttributeName()throws {
|
||||
let html: String = "<img /onerror='doMyJob'/>"
|
||||
var doc: Document = try SwiftSoup.parse(html)
|
||||
XCTAssertTrue(try doc.select("img[onerror]").size() != 0,"SelfClosingStartTag ignores last character")
|
||||
XCTAssertTrue(try doc.select("img[onerror]").size() != 0, "SelfClosingStartTag ignores last character")
|
||||
XCTAssertEqual("<img onerror=\"doMyJob\">", try doc.body()!.html())
|
||||
|
||||
doc = try SwiftSoup.parse(html, "", Parser.xmlParser())
|
||||
|
@ -98,14 +98,14 @@ class AttributeParseTest: XCTestCase {
|
|||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testparsesRoughAttributeString" , testparsesRoughAttributeString),
|
||||
("testhandlesNewLinesAndReturns" , testhandlesNewLinesAndReturns),
|
||||
("testparsesEmptyString" , testparsesEmptyString),
|
||||
("testcanStartWithEq" , testcanStartWithEq),
|
||||
("teststrictAttributeUnescapes" , teststrictAttributeUnescapes),
|
||||
("testmoreAttributeUnescapes" , testmoreAttributeUnescapes),
|
||||
("testparsesBooleanAttributes" , testparsesBooleanAttributes),
|
||||
("testdropsSlashFromAttributeName" , testdropsSlashFromAttributeName),
|
||||
("testparsesRoughAttributeString", testparsesRoughAttributeString),
|
||||
("testhandlesNewLinesAndReturns", testhandlesNewLinesAndReturns),
|
||||
("testparsesEmptyString", testparsesEmptyString),
|
||||
("testcanStartWithEq", testcanStartWithEq),
|
||||
("teststrictAttributeUnescapes", teststrictAttributeUnescapes),
|
||||
("testmoreAttributeUnescapes", testmoreAttributeUnescapes),
|
||||
("testparsesBooleanAttributes", testparsesBooleanAttributes),
|
||||
("testdropsSlashFromAttributeName", testdropsSlashFromAttributeName),
|
||||
]
|
||||
}()
|
||||
|
||||
|
|
|
@ -10,8 +10,7 @@ import XCTest
|
|||
@testable import SwiftSoup
|
||||
class AttributeTest: XCTestCase {
|
||||
|
||||
func testHtml()
|
||||
{
|
||||
func testHtml() {
|
||||
let attr = try! Attribute(key: "key", value: "value &")
|
||||
XCTAssertEqual("key=\"value &\"", attr.html())
|
||||
XCTAssertEqual(attr.html(), attr.toString())
|
||||
|
@ -26,8 +25,8 @@ class AttributeTest: XCTestCase {
|
|||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testHtml" , testHtml),
|
||||
("testWithSupplementaryCharacterInAttributeKeyAndValue" , testWithSupplementaryCharacterInAttributeKeyAndValue)
|
||||
("testHtml", testHtml),
|
||||
("testWithSupplementaryCharacterInAttributeKeyAndValue", testWithSupplementaryCharacterInAttributeKeyAndValue)
|
||||
]
|
||||
}()
|
||||
|
||||
|
|
|
@ -13,12 +13,11 @@ class AttributesTest: XCTestCase {
|
|||
|
||||
func testHtml() {
|
||||
let a: Attributes = Attributes()
|
||||
do{
|
||||
do {
|
||||
try a.put("Tot", "a&p")
|
||||
try a.put("Hello", "There")
|
||||
try a.put("data-name", "Jsoup")
|
||||
}catch{}
|
||||
|
||||
} catch {}
|
||||
|
||||
XCTAssertEqual(3, a.size())
|
||||
XCTAssertTrue(a.hasKey(key: "Tot"))
|
||||
|
@ -26,7 +25,7 @@ class AttributesTest: XCTestCase {
|
|||
XCTAssertTrue(a.hasKey(key: "data-name"))
|
||||
XCTAssertFalse(a.hasKey(key: "tot"))
|
||||
XCTAssertTrue(a.hasKeyIgnoreCase(key: "tot"))
|
||||
XCTAssertEqual("There",try a.getIgnoreCase(key: "hEllo"))
|
||||
XCTAssertEqual("There", try a.getIgnoreCase(key: "hEllo"))
|
||||
|
||||
XCTAssertEqual(1, a.dataset().count)
|
||||
XCTAssertEqual("Jsoup", a.dataset()["name"])
|
||||
|
@ -53,10 +52,9 @@ class AttributesTest: XCTestCase {
|
|||
// XCTAssertEqual(2, a.size())
|
||||
// }
|
||||
|
||||
|
||||
func testIterator() {
|
||||
let a: Attributes = Attributes()
|
||||
let datas: [[String]] = [["Tot", "raul"],["Hello", "pismuth"],["data-name", "Jsoup"]]
|
||||
let datas: [[String]] = [["Tot", "raul"], ["Hello", "pismuth"], ["data-name", "Jsoup"]]
|
||||
|
||||
for atts in datas {
|
||||
try! a.put(atts[0], atts[1])
|
||||
|
@ -82,9 +80,9 @@ class AttributesTest: XCTestCase {
|
|||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testHtml" , testHtml),
|
||||
("testIterator" , testIterator),
|
||||
("testIteratorEmpty" , testIteratorEmpty)
|
||||
("testHtml", testHtml),
|
||||
("testIterator", testIterator),
|
||||
("testIteratorEmpty", testIteratorEmpty)
|
||||
]
|
||||
}()
|
||||
}
|
||||
|
|
|
@ -167,7 +167,6 @@ class CharacterReaderTest: XCTestCase {
|
|||
XCTAssertFalse(r.matches("ne"))
|
||||
}
|
||||
|
||||
|
||||
func testMatchesIgnoreCase() {
|
||||
let r = CharacterReader("One Two Three")
|
||||
XCTAssertTrue(r.matchesIgnoreCase("O"))
|
||||
|
@ -245,25 +244,25 @@ class CharacterReaderTest: XCTestCase {
|
|||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testConsume" , testConsume),
|
||||
("testUnconsume" , testUnconsume),
|
||||
("testMark" , testMark),
|
||||
("testConsumeToEnd" , testConsumeToEnd),
|
||||
("testNextIndexOfChar" , testNextIndexOfChar),
|
||||
("testNextIndexOfString" , testNextIndexOfString),
|
||||
("testNextIndexOfUnmatched" , testNextIndexOfUnmatched),
|
||||
("testConsumeToChar" , testConsumeToChar),
|
||||
("testConsumeToString" , testConsumeToString),
|
||||
("testAdvance" , testAdvance),
|
||||
("testConsumeToAny" , testConsumeToAny),
|
||||
("testConsumeLetterSequence" , testConsumeLetterSequence),
|
||||
("testConsumeLetterThenDigitSequence" , testConsumeLetterThenDigitSequence),
|
||||
("testMatches" , testMatches),
|
||||
("testMatchesIgnoreCase" , testMatchesIgnoreCase),
|
||||
("testContainsIgnoreCase" , testContainsIgnoreCase),
|
||||
("testMatchesAny" , testMatchesAny),
|
||||
("testCachesStrings" , testCachesStrings),
|
||||
("testRangeEquals" , testRangeEquals)
|
||||
("testConsume", testConsume),
|
||||
("testUnconsume", testUnconsume),
|
||||
("testMark", testMark),
|
||||
("testConsumeToEnd", testConsumeToEnd),
|
||||
("testNextIndexOfChar", testNextIndexOfChar),
|
||||
("testNextIndexOfString", testNextIndexOfString),
|
||||
("testNextIndexOfUnmatched", testNextIndexOfUnmatched),
|
||||
("testConsumeToChar", testConsumeToChar),
|
||||
("testConsumeToString", testConsumeToString),
|
||||
("testAdvance", testAdvance),
|
||||
("testConsumeToAny", testConsumeToAny),
|
||||
("testConsumeLetterSequence", testConsumeLetterSequence),
|
||||
("testConsumeLetterThenDigitSequence", testConsumeLetterThenDigitSequence),
|
||||
("testMatches", testMatches),
|
||||
("testMatchesIgnoreCase", testMatchesIgnoreCase),
|
||||
("testContainsIgnoreCase", testContainsIgnoreCase),
|
||||
("testMatchesAny", testMatchesAny),
|
||||
("testCachesStrings", testCachesStrings),
|
||||
("testRangeEquals", testRangeEquals)
|
||||
]
|
||||
}()
|
||||
|
||||
|
|
|
@ -19,13 +19,13 @@ class CssTest: XCTestCase {
|
|||
let sb: StringBuilder = StringBuilder(string:"<html><head></head><body>")
|
||||
|
||||
sb.append("<div id='pseudo'>")
|
||||
for i in 1...10{
|
||||
for i in 1...10 {
|
||||
sb.append("<p>\(i)</p>")
|
||||
}
|
||||
sb.append("</div>")
|
||||
|
||||
sb.append("<div id='type'>")
|
||||
for i in 1...10{
|
||||
for i in 1...10 {
|
||||
sb.append("<p>\(i)</p>")
|
||||
sb.append("<span>\(i)</span>")
|
||||
sb.append("<em>\(i)</em>")
|
||||
|
@ -45,7 +45,6 @@ class CssTest: XCTestCase {
|
|||
html = try! SwiftSoup.parse(htmlString)
|
||||
}
|
||||
|
||||
|
||||
func testFirstChild()throws {
|
||||
try check(html.select("#pseudo :first-child"), "1")
|
||||
try check(html.select("html:first-child"))
|
||||
|
@ -82,7 +81,7 @@ class CssTest: XCTestCase {
|
|||
|
||||
func testNthLastOfType_simple()throws {
|
||||
for i in 1...10 {
|
||||
try check(html.select("#type :nth-last-of-type(\(i))"), "\(11-i)","\(11-i)","\(11-i)","\(11-i)")
|
||||
try check(html.select("#type :nth-last-of-type(\(i))"), "\(11-i)", "\(11-i)", "\(11-i)", "\(11-i)")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,7 +111,6 @@ class CssTest: XCTestCase {
|
|||
try check(html.select("#type :nth-of-type(+5)"), "5", "5", "5", "5")
|
||||
}
|
||||
|
||||
|
||||
func testNthLastChild_advanced()throws {
|
||||
try check(html.select("#pseudo :nth-last-child(-5)"))
|
||||
try check(html.select("#pseudo :nth-last-child(odd)"), "2", "4", "6", "8", "10")
|
||||
|
@ -177,9 +175,8 @@ class CssTest: XCTestCase {
|
|||
XCTAssertEqual("em", sel.get(5).tagName())
|
||||
}
|
||||
|
||||
|
||||
func check(_ resut: Elements, _ expectedContent: String... ) {
|
||||
check(resut,expectedContent)
|
||||
check(resut, expectedContent)
|
||||
}
|
||||
|
||||
func check(_ result: Elements, _ expectedContent: [String] ) {
|
||||
|
@ -202,28 +199,25 @@ class CssTest: XCTestCase {
|
|||
try XCTAssertEqual(Tag.valueOf("body"), sel2.get(0).tag())
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testFirstChild" , testFirstChild),
|
||||
("testLastChild" , testLastChild),
|
||||
("testNthChild_simple" , testNthChild_simple),
|
||||
("testNthOfType_unknownTag" , testNthOfType_unknownTag),
|
||||
("testNthLastChild_simple" , testNthLastChild_simple),
|
||||
("testNthOfType_simple" , testNthOfType_simple),
|
||||
("testNthLastOfType_simple" , testNthLastOfType_simple),
|
||||
("testNthChild_advanced" , testNthChild_advanced),
|
||||
("testNthOfType_advanced" , testNthOfType_advanced),
|
||||
("testNthLastChild_advanced" , testNthLastChild_advanced),
|
||||
("testNthLastOfType_advanced" , testNthLastOfType_advanced),
|
||||
("testFirstOfType" , testFirstOfType),
|
||||
("testLastOfType" , testLastOfType),
|
||||
("testEmpty" , testEmpty),
|
||||
("testOnlyChild" , testOnlyChild),
|
||||
("testOnlyOfType" , testOnlyOfType),
|
||||
("testRoot" , testRoot)
|
||||
("testFirstChild", testFirstChild),
|
||||
("testLastChild", testLastChild),
|
||||
("testNthChild_simple", testNthChild_simple),
|
||||
("testNthOfType_unknownTag", testNthOfType_unknownTag),
|
||||
("testNthLastChild_simple", testNthLastChild_simple),
|
||||
("testNthOfType_simple", testNthOfType_simple),
|
||||
("testNthLastOfType_simple", testNthLastOfType_simple),
|
||||
("testNthChild_advanced", testNthChild_advanced),
|
||||
("testNthOfType_advanced", testNthOfType_advanced),
|
||||
("testNthLastChild_advanced", testNthLastChild_advanced),
|
||||
("testNthLastOfType_advanced", testNthLastOfType_advanced),
|
||||
("testFirstOfType", testFirstOfType),
|
||||
("testLastOfType", testLastOfType),
|
||||
("testEmpty", testEmpty),
|
||||
("testOnlyChild", testOnlyChild),
|
||||
("testOnlyOfType", testOnlyOfType),
|
||||
("testRoot", testRoot)
|
||||
]
|
||||
}()
|
||||
}
|
||||
|
|
|
@ -15,19 +15,19 @@ class DocumentTest: XCTestCase {
|
|||
private static let charsetIso8859 = String.Encoding.iso2022JP //"ISO-8859-1"
|
||||
|
||||
func testSetTextPreservesDocumentStructure() {
|
||||
do{
|
||||
do {
|
||||
let doc: Document = try SwiftSoup.parse("<p>Hello</p>")
|
||||
try doc.text("Replaced")
|
||||
XCTAssertEqual("Replaced", try doc.text())
|
||||
XCTAssertEqual("Replaced", try doc.body()!.text())
|
||||
XCTAssertEqual(1, try doc.select("head").size())
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func testTitles() {
|
||||
do{
|
||||
do {
|
||||
let noTitle: Document = try SwiftSoup.parse("<p>Hello</p>")
|
||||
let withTitle: Document = try SwiftSoup.parse("<title>First</title><title>Ignore</title><p>Hello</p>")
|
||||
|
||||
|
@ -43,35 +43,34 @@ class DocumentTest: XCTestCase {
|
|||
|
||||
let normaliseTitle: Document = try SwiftSoup.parse("<title> Hello\nthere \n now \n")
|
||||
XCTAssertEqual("Hello there now", try normaliseTitle.title())
|
||||
}catch{
|
||||
} catch {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func testOutputEncoding() {
|
||||
do{
|
||||
do {
|
||||
let doc: Document = try SwiftSoup.parse("<p title=π>π & < > </p>")
|
||||
// default is utf-8
|
||||
XCTAssertEqual("<p title=\"π\">π & < > </p>", try doc.body()?.html())
|
||||
XCTAssertEqual("UTF-8",doc.outputSettings().charset().displayName())
|
||||
XCTAssertEqual("UTF-8", doc.outputSettings().charset().displayName())
|
||||
|
||||
doc.outputSettings().charset(String.Encoding.ascii)
|
||||
XCTAssertEqual(Entities.EscapeMode.base,doc.outputSettings().escapeMode())
|
||||
XCTAssertEqual(Entities.EscapeMode.base, doc.outputSettings().escapeMode())
|
||||
XCTAssertEqual("<p title=\"π\">π & < > </p>", try doc.body()?.html())
|
||||
|
||||
doc.outputSettings().escapeMode(Entities.EscapeMode.extended)
|
||||
XCTAssertEqual("<p title=\"π\">π & < > </p>", try doc.body()?.html())
|
||||
}catch
|
||||
{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func testXhtmlReferences() {
|
||||
let doc: Document = try! SwiftSoup.parse("< > & " ' ×")
|
||||
doc.outputSettings().escapeMode(Entities.EscapeMode.xhtml)
|
||||
XCTAssertEqual("< > & \" ' ×",try! doc.body()?.html())
|
||||
XCTAssertEqual("< > & \" ' ×", try! doc.body()?.html())
|
||||
}
|
||||
|
||||
func testNormalisesStructure() {
|
||||
|
@ -83,7 +82,7 @@ class DocumentTest: XCTestCase {
|
|||
let doc: Document = try! SwiftSoup.parse("<title>Hello</title> <p>One<p>Two")
|
||||
let clone: Document = doc.copy() as! Document
|
||||
|
||||
XCTAssertEqual("<html><head><title>Hello</title> </head><body><p>One</p><p>Two</p></body></html>",try! TextUtil.stripNewlines(clone.html()))
|
||||
XCTAssertEqual("<html><head><title>Hello</title> </head><body><p>One</p><p>Two</p></body></html>", try! TextUtil.stripNewlines(clone.html()))
|
||||
try! clone.title("Hello there")
|
||||
try! clone.select("p").first()!.text("One more").attr("id", "1")
|
||||
XCTAssertEqual("<html><head><title>Hello there</title> </head><body><p id=\"1\">One more</p><p>Two</p></body></html>", try! TextUtil.stripNewlines(clone.html()))
|
||||
|
@ -191,13 +190,12 @@ class DocumentTest: XCTestCase {
|
|||
func testMetaCharsetUpdateUtf8() {
|
||||
let doc: Document = createHtmlDocument("changeThis")
|
||||
doc.updateMetaCharsetElement(true)
|
||||
do{
|
||||
do {
|
||||
try doc.charset(DocumentTest.charsetUtf8)
|
||||
}catch{
|
||||
} catch {
|
||||
print("")
|
||||
}
|
||||
|
||||
|
||||
let htmlCharsetUTF8: String = "<html>\n" + " <head>\n" + " <meta charset=\"" + "UTF-8" + "\">\n" + " </head>\n" + " <body></body>\n" + "</html>"
|
||||
XCTAssertEqual(htmlCharsetUTF8, try! doc.toString())
|
||||
|
||||
|
@ -219,7 +217,7 @@ class DocumentTest: XCTestCase {
|
|||
" </head>\n" +
|
||||
" <body></body>\n" +
|
||||
"</html>"
|
||||
XCTAssertEqual(htmlCharsetISO,try doc.toString())
|
||||
XCTAssertEqual(htmlCharsetISO, try doc.toString())
|
||||
|
||||
let selectedElement: Element = try doc.select("meta[charset]").first()!
|
||||
XCTAssertEqual(String.Encoding.isoLatin2.displayName(), doc.charset().displayName())
|
||||
|
@ -376,8 +374,7 @@ class DocumentTest: XCTestCase {
|
|||
XCTAssertFalse(doc.updateMetaCharsetElement())
|
||||
}
|
||||
|
||||
|
||||
private func createHtmlDocument(_ charset: String)->Document {
|
||||
private func createHtmlDocument(_ charset: String) -> Document {
|
||||
let doc: Document = Document.createShell("")
|
||||
try! doc.head()?.appendElement("meta").attr("charset", charset)
|
||||
try! doc.head()?.appendElement("meta").attr("name", "charset").attr("content", charset)
|
||||
|
@ -423,31 +420,31 @@ class DocumentTest: XCTestCase {
|
|||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testSetTextPreservesDocumentStructure" , testSetTextPreservesDocumentStructure),
|
||||
("testTitles" , testTitles),
|
||||
("testOutputEncoding" , testOutputEncoding),
|
||||
("testXhtmlReferences" , testXhtmlReferences),
|
||||
("testNormalisesStructure" , testNormalisesStructure),
|
||||
("testClone" , testClone),
|
||||
("testClonesDeclarations" , testClonesDeclarations),
|
||||
("testHtmlAndXmlSyntax" , testHtmlAndXmlSyntax),
|
||||
("testHtmlParseDefaultsToHtmlOutputSyntax" , testHtmlParseDefaultsToHtmlOutputSyntax),
|
||||
("testHtmlAppendable" , testHtmlAppendable),
|
||||
("testDocumentsWithSameContentAreEqual" , testDocumentsWithSameContentAreEqual),
|
||||
("testDocumentsWithSameContentAreVerifialbe" , testDocumentsWithSameContentAreVerifialbe),
|
||||
("testMetaCharsetUpdateUtf8" , testMetaCharsetUpdateUtf8),
|
||||
("testMetaCharsetUpdateIsoLatin2" , testMetaCharsetUpdateIsoLatin2),
|
||||
("testMetaCharsetUpdateNoCharset" , testMetaCharsetUpdateNoCharset),
|
||||
("testMetaCharsetUpdateDisabled" , testMetaCharsetUpdateDisabled),
|
||||
("testMetaCharsetUpdateDisabledNoChanges" , testMetaCharsetUpdateDisabledNoChanges),
|
||||
("testMetaCharsetUpdateEnabledAfterCharsetChange" , testMetaCharsetUpdateEnabledAfterCharsetChange),
|
||||
("testMetaCharsetUpdateCleanup" , testMetaCharsetUpdateCleanup),
|
||||
("testMetaCharsetUpdateXmlUtf8" , testMetaCharsetUpdateXmlUtf8),
|
||||
("testMetaCharsetUpdateXmlIso2022JP" , testMetaCharsetUpdateXmlIso2022JP),
|
||||
("testMetaCharsetUpdateXmlNoCharset" , testMetaCharsetUpdateXmlNoCharset),
|
||||
("testMetaCharsetUpdateXmlDisabled" , testMetaCharsetUpdateXmlDisabled),
|
||||
("testMetaCharsetUpdateXmlDisabledNoChanges" , testMetaCharsetUpdateXmlDisabledNoChanges),
|
||||
("testMetaCharsetUpdatedDisabledPerDefault" , testMetaCharsetUpdatedDisabledPerDefault)
|
||||
("testSetTextPreservesDocumentStructure", testSetTextPreservesDocumentStructure),
|
||||
("testTitles", testTitles),
|
||||
("testOutputEncoding", testOutputEncoding),
|
||||
("testXhtmlReferences", testXhtmlReferences),
|
||||
("testNormalisesStructure", testNormalisesStructure),
|
||||
("testClone", testClone),
|
||||
("testClonesDeclarations", testClonesDeclarations),
|
||||
("testHtmlAndXmlSyntax", testHtmlAndXmlSyntax),
|
||||
("testHtmlParseDefaultsToHtmlOutputSyntax", testHtmlParseDefaultsToHtmlOutputSyntax),
|
||||
("testHtmlAppendable", testHtmlAppendable),
|
||||
("testDocumentsWithSameContentAreEqual", testDocumentsWithSameContentAreEqual),
|
||||
("testDocumentsWithSameContentAreVerifialbe", testDocumentsWithSameContentAreVerifialbe),
|
||||
("testMetaCharsetUpdateUtf8", testMetaCharsetUpdateUtf8),
|
||||
("testMetaCharsetUpdateIsoLatin2", testMetaCharsetUpdateIsoLatin2),
|
||||
("testMetaCharsetUpdateNoCharset", testMetaCharsetUpdateNoCharset),
|
||||
("testMetaCharsetUpdateDisabled", testMetaCharsetUpdateDisabled),
|
||||
("testMetaCharsetUpdateDisabledNoChanges", testMetaCharsetUpdateDisabledNoChanges),
|
||||
("testMetaCharsetUpdateEnabledAfterCharsetChange", testMetaCharsetUpdateEnabledAfterCharsetChange),
|
||||
("testMetaCharsetUpdateCleanup", testMetaCharsetUpdateCleanup),
|
||||
("testMetaCharsetUpdateXmlUtf8", testMetaCharsetUpdateXmlUtf8),
|
||||
("testMetaCharsetUpdateXmlIso2022JP", testMetaCharsetUpdateXmlIso2022JP),
|
||||
("testMetaCharsetUpdateXmlNoCharset", testMetaCharsetUpdateXmlNoCharset),
|
||||
("testMetaCharsetUpdateXmlDisabled", testMetaCharsetUpdateXmlDisabled),
|
||||
("testMetaCharsetUpdateXmlDisabledNoChanges", testMetaCharsetUpdateXmlDisabledNoChanges),
|
||||
("testMetaCharsetUpdatedDisabledPerDefault", testMetaCharsetUpdatedDisabledPerDefault)
|
||||
]
|
||||
}()
|
||||
|
||||
|
|
|
@ -12,11 +12,10 @@ import SwiftSoup
|
|||
class DocumentTypeTest: XCTestCase {
|
||||
|
||||
func testConstructorValidationOkWithBlankName() {
|
||||
let fail: DocumentType? = DocumentType("","", "", "")
|
||||
let fail: DocumentType? = DocumentType("", "", "", "")
|
||||
XCTAssertTrue(fail != nil)
|
||||
}
|
||||
|
||||
|
||||
func testConstructorValidationThrowsExceptionOnNulls() {
|
||||
let fail: DocumentType? = DocumentType("html", "", "", "")
|
||||
XCTAssertTrue(fail != nil)
|
||||
|
@ -43,10 +42,10 @@ class DocumentTypeTest: XCTestCase {
|
|||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testConstructorValidationOkWithBlankName" , testConstructorValidationOkWithBlankName),
|
||||
("testConstructorValidationThrowsExceptionOnNulls" , testConstructorValidationThrowsExceptionOnNulls),
|
||||
("testConstructorValidationOkWithBlankPublicAndSystemIds" , testConstructorValidationOkWithBlankPublicAndSystemIds),
|
||||
("testOuterHtmlGeneration" , testOuterHtmlGeneration),
|
||||
("testConstructorValidationOkWithBlankName", testConstructorValidationOkWithBlankName),
|
||||
("testConstructorValidationThrowsExceptionOnNulls", testConstructorValidationThrowsExceptionOnNulls),
|
||||
("testConstructorValidationOkWithBlankPublicAndSystemIds", testConstructorValidationOkWithBlankPublicAndSystemIds),
|
||||
("testOuterHtmlGeneration", testOuterHtmlGeneration),
|
||||
]
|
||||
}()
|
||||
}
|
||||
|
|
|
@ -354,8 +354,7 @@ class ElementTest: XCTestCase {
|
|||
|
||||
// check sibling index (with short circuit on reindexChildren):
|
||||
let ps: Elements = try doc.select("p")
|
||||
for i in 0..<ps.size()
|
||||
{
|
||||
for i in 0..<ps.size() {
|
||||
XCTAssertEqual(i, ps.get(i).siblingIndex)
|
||||
}
|
||||
}
|
||||
|
@ -414,7 +413,7 @@ class ElementTest: XCTestCase {
|
|||
let doc: Document = try SwiftSoup.parse("<div id=1><p>Hello</p></div>")
|
||||
let div: Element = try doc.getElementById("1")!
|
||||
try div.appendText(" there & now >")
|
||||
XCTAssertEqual("<p>Hello</p> there & now >",try TextUtil.stripNewlines(div.html()))
|
||||
XCTAssertEqual("<p>Hello</p> there & now >", try TextUtil.stripNewlines(div.html()))
|
||||
}
|
||||
|
||||
func testPrependText()throws {
|
||||
|
@ -447,7 +446,7 @@ class ElementTest: XCTestCase {
|
|||
|
||||
// check sibling index (no reindexChildren):
|
||||
let ps: Elements = try doc.select("p")
|
||||
for i in 0..<ps.size(){
|
||||
for i in 0..<ps.size() {
|
||||
XCTAssertEqual(i, ps.get(i).siblingIndex)
|
||||
}
|
||||
}
|
||||
|
@ -460,7 +459,7 @@ class ElementTest: XCTestCase {
|
|||
|
||||
// check sibling index (reindexChildren):
|
||||
let ps: Elements = try doc.select("p")
|
||||
for i in 0..<ps.size(){
|
||||
for i in 0..<ps.size() {
|
||||
XCTAssertEqual(i, ps.get(i).siblingIndex)
|
||||
}
|
||||
}
|
||||
|
@ -516,7 +515,7 @@ class ElementTest: XCTestCase {
|
|||
XCTAssertEqual("<div><p>Hello</p><div>one</div><div>two</div><p>There</p></div>", try TextUtil.stripNewlines(doc.body()!.html()))
|
||||
|
||||
try doc.select("p").last()?.after("<p>Three</p><!-- four -->")
|
||||
XCTAssertEqual("<div><p>Hello</p><div>one</div><div>two</div><p>There</p><p>Three</p><!-- four --></div>",TextUtil.stripNewlines(try doc.body()!.html()))
|
||||
XCTAssertEqual("<div><p>Hello</p><div>one</div><div>two</div><p>There</p><p>Three</p><!-- four --></div>", TextUtil.stripNewlines(try doc.body()!.html()))
|
||||
}
|
||||
|
||||
func testWrapWithRemainder()throws {
|
||||
|
@ -592,12 +591,12 @@ class ElementTest: XCTestCase {
|
|||
XCTAssertNotNil(p.parent())
|
||||
|
||||
try clone.append("<span>Three")
|
||||
XCTAssertEqual("<p><span>Two</span><span>Three</span></p>",try TextUtil.stripNewlines(clone.outerHtml()))
|
||||
XCTAssertEqual("<div><p>One</p><p><span>Two</span></p></div>",try TextUtil.stripNewlines(doc.body()!.html())) // not modified
|
||||
XCTAssertEqual("<p><span>Two</span><span>Three</span></p>", try TextUtil.stripNewlines(clone.outerHtml()))
|
||||
XCTAssertEqual("<div><p>One</p><p><span>Two</span></p></div>", try TextUtil.stripNewlines(doc.body()!.html())) // not modified
|
||||
|
||||
try doc.body()?.appendChild(clone) // adopt
|
||||
XCTAssertNotNil(clone.parent())
|
||||
XCTAssertEqual("<div><p>One</p><p><span>Two</span></p></div><p><span>Two</span><span>Three</span></p>",try TextUtil.stripNewlines(doc.body()!.html()))
|
||||
XCTAssertEqual("<div><p>One</p><p><span>Two</span></p></div><p><span>Two</span><span>Three</span></p>", try TextUtil.stripNewlines(doc.body()!.html()))
|
||||
}
|
||||
|
||||
func testClonesClassnames()throws {
|
||||
|
@ -718,7 +717,7 @@ class ElementTest: XCTestCase {
|
|||
XCTAssertEqual(0, children.count) // children is backed by div1.childNodes, moved, so should be 0 now
|
||||
XCTAssertEqual(0, div1.childNodeSize())
|
||||
XCTAssertEqual(4, div2.childNodeSize())
|
||||
XCTAssertEqual("<div id=\"1\"></div>\n<div id=\"2\">\n Text \n <p>One</p> Text \n <p>Two</p>\n</div>",try doc.body()!.html())
|
||||
XCTAssertEqual("<div id=\"1\"></div>\n<div id=\"2\">\n Text \n <p>One</p> Text \n <p>Two</p>\n</div>", try doc.body()!.html())
|
||||
}
|
||||
|
||||
func testInsertChildrenArgumentValidation()throws {
|
||||
|
@ -729,13 +728,13 @@ class ElementTest: XCTestCase {
|
|||
|
||||
do {
|
||||
try div2.insertChildren(6, children)
|
||||
XCTAssertEqual(0,1)
|
||||
} catch{}
|
||||
XCTAssertEqual(0, 1)
|
||||
} catch {}
|
||||
|
||||
do {
|
||||
try div2.insertChildren(-5, children)
|
||||
XCTAssertEqual(0,1)
|
||||
} catch{
|
||||
XCTAssertEqual(0, 1)
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -820,7 +819,6 @@ class ElementTest: XCTestCase {
|
|||
|
||||
try div.classNames(newSet)
|
||||
|
||||
|
||||
XCTAssertEqual("c1 c2 c3", try div.className())
|
||||
|
||||
let set2 = try div.classNames()
|
||||
|
@ -932,75 +930,75 @@ class ElementTest: XCTestCase {
|
|||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testGetElementsByTagName" , testGetElementsByTagName),
|
||||
("testGetNamespacedElementsByTag" , testGetNamespacedElementsByTag),
|
||||
("testGetElementById" , testGetElementById),
|
||||
("testGetText" , testGetText),
|
||||
("testGetChildText" , testGetChildText),
|
||||
("testNormalisesText" , testNormalisesText),
|
||||
("testKeepsPreText" , testKeepsPreText),
|
||||
("testKeepsPreTextInCode" , testKeepsPreTextInCode),
|
||||
("testBrHasSpace" , testBrHasSpace),
|
||||
("testGetSiblings" , testGetSiblings),
|
||||
("testGetSiblingsWithDuplicateContent" , testGetSiblingsWithDuplicateContent),
|
||||
("testGetParents" , testGetParents),
|
||||
("testElementSiblingIndex" , testElementSiblingIndex),
|
||||
("testElementSiblingIndexSameContent" , testElementSiblingIndexSameContent),
|
||||
("testGetElementsWithClass" , testGetElementsWithClass),
|
||||
("testGetElementsWithAttribute" , testGetElementsWithAttribute),
|
||||
("testGetElementsWithAttributeDash" , testGetElementsWithAttributeDash),
|
||||
("testGetElementsWithAttributeValue" , testGetElementsWithAttributeValue),
|
||||
("testClassDomMethods" , testClassDomMethods),
|
||||
("testHasClassDomMethods" , testHasClassDomMethods),
|
||||
("testClassUpdates" , testClassUpdates),
|
||||
("testOuterHtml" , testOuterHtml),
|
||||
("testInnerHtml" , testInnerHtml),
|
||||
("testFormatHtml" , testFormatHtml),
|
||||
("testFormatOutline" , testFormatOutline),
|
||||
("testSetIndent" , testSetIndent),
|
||||
("testNotPretty" , testNotPretty),
|
||||
("testEmptyElementFormatHtml" , testEmptyElementFormatHtml),
|
||||
("testNoIndentOnScriptAndStyle" , testNoIndentOnScriptAndStyle),
|
||||
("testContainerOutput" , testContainerOutput),
|
||||
("testSetText" , testSetText),
|
||||
("testAddNewElement" , testAddNewElement),
|
||||
("testAddBooleanAttribute" , testAddBooleanAttribute),
|
||||
("testAppendRowToTable" , testAppendRowToTable),
|
||||
("testPrependRowToTable" , testPrependRowToTable),
|
||||
("testPrependElement" , testPrependElement),
|
||||
("testAddNewText" , testAddNewText),
|
||||
("testPrependText" , testPrependText),
|
||||
("testAddNewHtml" , testAddNewHtml),
|
||||
("testPrependNewHtml" , testPrependNewHtml),
|
||||
("testSetHtml" , testSetHtml),
|
||||
("testSetHtmlTitle" , testSetHtmlTitle),
|
||||
("testWrap" , testWrap),
|
||||
("testBefore" , testBefore),
|
||||
("testAfter" , testAfter),
|
||||
("testWrapWithRemainder" , testWrapWithRemainder),
|
||||
("testHasText" , testHasText),
|
||||
("testDataset" , testDataset),
|
||||
("testpParentlessToString" , testpParentlessToString),
|
||||
("testClone" , testClone),
|
||||
("testClonesClassnames" , testClonesClassnames),
|
||||
("testTagNameSet" , testTagNameSet),
|
||||
("testHtmlContainsOuter" , testHtmlContainsOuter),
|
||||
("testGetTextNodes" , testGetTextNodes),
|
||||
("testManipulateTextNodes" , testManipulateTextNodes),
|
||||
("testGetDataNodes" , testGetDataNodes),
|
||||
("testElementIsNotASiblingOfItself" , testElementIsNotASiblingOfItself),
|
||||
("testChildThrowsIndexOutOfBoundsOnMissing" , testChildThrowsIndexOutOfBoundsOnMissing),
|
||||
("testMoveByAppend" , testMoveByAppend),
|
||||
("testInsertChildrenArgumentValidation" , testInsertChildrenArgumentValidation),
|
||||
("testInsertChildrenAtPosition" , testInsertChildrenAtPosition),
|
||||
("testInsertChildrenAsCopy" , testInsertChildrenAsCopy),
|
||||
("testCssPath" , testCssPath),
|
||||
("testClassNames" , testClassNames),
|
||||
("testHashAndEqualsAndValue" , testHashAndEqualsAndValue),
|
||||
("testRelativeUrls" , testRelativeUrls),
|
||||
("testAppendMustCorrectlyMoveChildrenInsideOneParentElement" , testAppendMustCorrectlyMoveChildrenInsideOneParentElement),
|
||||
("testHashcodeIsStableWithContentChanges" , testHashcodeIsStableWithContentChanges),
|
||||
("testNamespacedElements" , testNamespacedElements)
|
||||
("testGetElementsByTagName", testGetElementsByTagName),
|
||||
("testGetNamespacedElementsByTag", testGetNamespacedElementsByTag),
|
||||
("testGetElementById", testGetElementById),
|
||||
("testGetText", testGetText),
|
||||
("testGetChildText", testGetChildText),
|
||||
("testNormalisesText", testNormalisesText),
|
||||
("testKeepsPreText", testKeepsPreText),
|
||||
("testKeepsPreTextInCode", testKeepsPreTextInCode),
|
||||
("testBrHasSpace", testBrHasSpace),
|
||||
("testGetSiblings", testGetSiblings),
|
||||
("testGetSiblingsWithDuplicateContent", testGetSiblingsWithDuplicateContent),
|
||||
("testGetParents", testGetParents),
|
||||
("testElementSiblingIndex", testElementSiblingIndex),
|
||||
("testElementSiblingIndexSameContent", testElementSiblingIndexSameContent),
|
||||
("testGetElementsWithClass", testGetElementsWithClass),
|
||||
("testGetElementsWithAttribute", testGetElementsWithAttribute),
|
||||
("testGetElementsWithAttributeDash", testGetElementsWithAttributeDash),
|
||||
("testGetElementsWithAttributeValue", testGetElementsWithAttributeValue),
|
||||
("testClassDomMethods", testClassDomMethods),
|
||||
("testHasClassDomMethods", testHasClassDomMethods),
|
||||
("testClassUpdates", testClassUpdates),
|
||||
("testOuterHtml", testOuterHtml),
|
||||
("testInnerHtml", testInnerHtml),
|
||||
("testFormatHtml", testFormatHtml),
|
||||
("testFormatOutline", testFormatOutline),
|
||||
("testSetIndent", testSetIndent),
|
||||
("testNotPretty", testNotPretty),
|
||||
("testEmptyElementFormatHtml", testEmptyElementFormatHtml),
|
||||
("testNoIndentOnScriptAndStyle", testNoIndentOnScriptAndStyle),
|
||||
("testContainerOutput", testContainerOutput),
|
||||
("testSetText", testSetText),
|
||||
("testAddNewElement", testAddNewElement),
|
||||
("testAddBooleanAttribute", testAddBooleanAttribute),
|
||||
("testAppendRowToTable", testAppendRowToTable),
|
||||
("testPrependRowToTable", testPrependRowToTable),
|
||||
("testPrependElement", testPrependElement),
|
||||
("testAddNewText", testAddNewText),
|
||||
("testPrependText", testPrependText),
|
||||
("testAddNewHtml", testAddNewHtml),
|
||||
("testPrependNewHtml", testPrependNewHtml),
|
||||
("testSetHtml", testSetHtml),
|
||||
("testSetHtmlTitle", testSetHtmlTitle),
|
||||
("testWrap", testWrap),
|
||||
("testBefore", testBefore),
|
||||
("testAfter", testAfter),
|
||||
("testWrapWithRemainder", testWrapWithRemainder),
|
||||
("testHasText", testHasText),
|
||||
("testDataset", testDataset),
|
||||
("testpParentlessToString", testpParentlessToString),
|
||||
("testClone", testClone),
|
||||
("testClonesClassnames", testClonesClassnames),
|
||||
("testTagNameSet", testTagNameSet),
|
||||
("testHtmlContainsOuter", testHtmlContainsOuter),
|
||||
("testGetTextNodes", testGetTextNodes),
|
||||
("testManipulateTextNodes", testManipulateTextNodes),
|
||||
("testGetDataNodes", testGetDataNodes),
|
||||
("testElementIsNotASiblingOfItself", testElementIsNotASiblingOfItself),
|
||||
("testChildThrowsIndexOutOfBoundsOnMissing", testChildThrowsIndexOutOfBoundsOnMissing),
|
||||
("testMoveByAppend", testMoveByAppend),
|
||||
("testInsertChildrenArgumentValidation", testInsertChildrenArgumentValidation),
|
||||
("testInsertChildrenAtPosition", testInsertChildrenAtPosition),
|
||||
("testInsertChildrenAsCopy", testInsertChildrenAsCopy),
|
||||
("testCssPath", testCssPath),
|
||||
("testClassNames", testClassNames),
|
||||
("testHashAndEqualsAndValue", testHashAndEqualsAndValue),
|
||||
("testRelativeUrls", testRelativeUrls),
|
||||
("testAppendMustCorrectlyMoveChildrenInsideOneParentElement", testAppendMustCorrectlyMoveChildrenInsideOneParentElement),
|
||||
("testHashcodeIsStableWithContentChanges", testHashcodeIsStableWithContentChanges),
|
||||
("testNamespacedElements", testNamespacedElements)
|
||||
]
|
||||
}()
|
||||
}
|
||||
|
|
|
@ -160,7 +160,7 @@ class ElementsTest: XCTestCase {
|
|||
let h = "<p><b>This</b> is <b>jsoup</b>.</p> <p>How do you like it?</p>"
|
||||
let doc: Document = try SwiftSoup.parse(h)
|
||||
try doc.select("p").wrap("<div></div>")
|
||||
XCTAssertEqual("<div><p><b>This</b> is <b>jsoup</b>.</p></div> <div><p>How do you like it?</p></div>",try TextUtil.stripNewlines(doc.body()!.html()))
|
||||
XCTAssertEqual("<div><p><b>This</b> is <b>jsoup</b>.</p></div> <div><p>How do you like it?</p></div>", try TextUtil.stripNewlines(doc.body()!.html()))
|
||||
}
|
||||
|
||||
func testUnwrap()throws {
|
||||
|
@ -248,7 +248,7 @@ class ElementsTest: XCTestCase {
|
|||
let doc: Document = try SwiftSoup.parse("<div><p>Hello</p></div><div>There</div>")
|
||||
let accum: StringBuilder = StringBuilder()
|
||||
|
||||
class nv : NodeVisitor {
|
||||
class nv: NodeVisitor {
|
||||
let accum: StringBuilder
|
||||
init(_ accum: StringBuilder) {
|
||||
self.accum = accum
|
||||
|
@ -284,39 +284,38 @@ class ElementsTest: XCTestCase {
|
|||
try XCTAssertEqual("Check", els.text())
|
||||
}
|
||||
|
||||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testFilter" , testFilter),
|
||||
("testAttributes" , testAttributes),
|
||||
("testHasAttr" , testHasAttr),
|
||||
("testHasAbsAttr" , testHasAbsAttr),
|
||||
("testAttr" , testAttr),
|
||||
("testAbsAttr" , testAbsAttr),
|
||||
("testClasses" , testClasses),
|
||||
("testText" , testText),
|
||||
("testHasText" , testHasText),
|
||||
("testHtml" , testHtml),
|
||||
("testOuterHtml" , testOuterHtml),
|
||||
("testSetHtml" , testSetHtml),
|
||||
("testVal" , testVal),
|
||||
("testBefore" , testBefore),
|
||||
("testAfter" , testAfter),
|
||||
("testWrap" , testWrap),
|
||||
("testWrapDiv" , testWrapDiv),
|
||||
("testUnwrap" , testUnwrap),
|
||||
("testUnwrapP" , testUnwrapP),
|
||||
("testUnwrapKeepsSpace" , testUnwrapKeepsSpace),
|
||||
("testEmpty" , testEmpty),
|
||||
("testRemove" , testRemove),
|
||||
("testEq" , testEq),
|
||||
("testIs" , testIs),
|
||||
("testParents" , testParents),
|
||||
("testNot" , testNot),
|
||||
("testTagNameSet" , testTagNameSet),
|
||||
("testTraverse" , testTraverse),
|
||||
("testForms" , testForms),
|
||||
("testClassWithHyphen" , testClassWithHyphen)
|
||||
("testFilter", testFilter),
|
||||
("testAttributes", testAttributes),
|
||||
("testHasAttr", testHasAttr),
|
||||
("testHasAbsAttr", testHasAbsAttr),
|
||||
("testAttr", testAttr),
|
||||
("testAbsAttr", testAbsAttr),
|
||||
("testClasses", testClasses),
|
||||
("testText", testText),
|
||||
("testHasText", testHasText),
|
||||
("testHtml", testHtml),
|
||||
("testOuterHtml", testOuterHtml),
|
||||
("testSetHtml", testSetHtml),
|
||||
("testVal", testVal),
|
||||
("testBefore", testBefore),
|
||||
("testAfter", testAfter),
|
||||
("testWrap", testWrap),
|
||||
("testWrapDiv", testWrapDiv),
|
||||
("testUnwrap", testUnwrap),
|
||||
("testUnwrapP", testUnwrapP),
|
||||
("testUnwrapKeepsSpace", testUnwrapKeepsSpace),
|
||||
("testEmpty", testEmpty),
|
||||
("testRemove", testRemove),
|
||||
("testEq", testEq),
|
||||
("testIs", testIs),
|
||||
("testParents", testParents),
|
||||
("testNot", testNot),
|
||||
("testTagNameSet", testTagNameSet),
|
||||
("testTraverse", testTraverse),
|
||||
("testForms", testForms),
|
||||
("testClassWithHyphen", testClassWithHyphen)
|
||||
]
|
||||
}()
|
||||
}
|
||||
|
|
|
@ -15,15 +15,15 @@ class EntitiesTest: XCTestCase {
|
|||
func testEscape()throws {
|
||||
let text = "Hello &<> Å å π 新 there ¾ © »"
|
||||
|
||||
let escapedAscii = Entities.escape(text,OutputSettings().encoder(String.Encoding.ascii).escapeMode(Entities.EscapeMode.base))
|
||||
let escapedAscii = Entities.escape(text, OutputSettings().encoder(String.Encoding.ascii).escapeMode(Entities.EscapeMode.base))
|
||||
let escapedAsciiFull = Entities.escape(text, OutputSettings().charset(String.Encoding.ascii).escapeMode(Entities.EscapeMode.extended))
|
||||
let escapedAsciiXhtml = Entities.escape(text, OutputSettings().charset(String.Encoding.ascii).escapeMode(Entities.EscapeMode.xhtml))
|
||||
let escapedUtfFull = Entities.escape(text, OutputSettings().charset(String.Encoding.utf8).escapeMode(Entities.EscapeMode.extended))
|
||||
let escapedUtfMin = Entities.escape(text, OutputSettings().charset(String.Encoding.utf8).escapeMode(Entities.EscapeMode.xhtml))
|
||||
|
||||
XCTAssertEqual("Hello &<> Å å π 新 there ¾ © »", escapedAscii);
|
||||
XCTAssertEqual("Hello &<> Å å π 新 there ¾ © »", escapedAsciiFull);
|
||||
XCTAssertEqual("Hello &<> Å å π 新 there ¾ © »", escapedAsciiXhtml);
|
||||
XCTAssertEqual("Hello &<> Å å π 新 there ¾ © »", escapedAscii)
|
||||
XCTAssertEqual("Hello &<> Å å π 新 there ¾ © »", escapedAsciiFull)
|
||||
XCTAssertEqual("Hello &<> Å å π 新 there ¾ © »", escapedAsciiXhtml)
|
||||
XCTAssertEqual("Hello &<> Å å π 新 there ¾ © »", escapedUtfFull)
|
||||
XCTAssertEqual("Hello &<> Å å π 新 there ¾ © »", escapedUtfMin)
|
||||
// odd that it's defined as aring in base but angst in full
|
||||
|
@ -36,8 +36,6 @@ class EntitiesTest: XCTestCase {
|
|||
XCTAssertEqual(text, try Entities.unescape(escapedUtfMin))
|
||||
}
|
||||
|
||||
|
||||
|
||||
func testXhtml() {
|
||||
//let text = "& > < "";
|
||||
XCTAssertEqual(38, Entities.EscapeMode.xhtml.codepointForName("amp"))
|
||||
|
@ -92,7 +90,6 @@ class EntitiesTest: XCTestCase {
|
|||
XCTAssertEqual("Hello &= &", try Entities.unescape(string: text, strict: false))
|
||||
}
|
||||
|
||||
|
||||
func testCaseSensitive()throws {
|
||||
let unescaped: String = "Ü ü & &"
|
||||
XCTAssertEqual("Ü ü & &",
|
||||
|
@ -128,7 +125,6 @@ class EntitiesTest: XCTestCase {
|
|||
func testUscapesGtInXmlAttributesButNotInHtml()throws {
|
||||
// https://github.com/jhy/jsoup/issues/528 - < is OK in HTML attribute values, but not in XML
|
||||
|
||||
|
||||
let docHtml: String = "<a title='<p>One</p>'>One</a>"
|
||||
let doc: Document = try SwiftSoup.parse(docHtml)
|
||||
let element: Element = try doc.select("a").first()!
|
||||
|
@ -137,25 +133,24 @@ class EntitiesTest: XCTestCase {
|
|||
XCTAssertEqual("<a title=\"<p>One</p>\">One</a>", try element.outerHtml())
|
||||
|
||||
doc.outputSettings().escapeMode(Entities.EscapeMode.xhtml)
|
||||
XCTAssertEqual("<a title=\"<p>One</p>\">One</a>",try element.outerHtml())
|
||||
XCTAssertEqual("<a title=\"<p>One</p>\">One</a>", try element.outerHtml())
|
||||
}
|
||||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testEscape" , testEscape),
|
||||
("testXhtml" , testXhtml),
|
||||
("testGetByName" , testGetByName),
|
||||
("testEscapeSupplementaryCharacter" , testEscapeSupplementaryCharacter),
|
||||
("testNotMissingMultis" , testNotMissingMultis),
|
||||
("testnotMissingSupplementals" , testnotMissingSupplementals),
|
||||
("testUnescape" , testUnescape),
|
||||
("testStrictUnescape" , testStrictUnescape),
|
||||
("testCaseSensitive" , testCaseSensitive),
|
||||
("testQuoteReplacements" , testQuoteReplacements),
|
||||
("testLetterDigitEntities" , testLetterDigitEntities),
|
||||
("testNoSpuriousDecodes" , testNoSpuriousDecodes),
|
||||
("testUscapesGtInXmlAttributesButNotInHtml" , testUscapesGtInXmlAttributesButNotInHtml)
|
||||
("testEscape", testEscape),
|
||||
("testXhtml", testXhtml),
|
||||
("testGetByName", testGetByName),
|
||||
("testEscapeSupplementaryCharacter", testEscapeSupplementaryCharacter),
|
||||
("testNotMissingMultis", testNotMissingMultis),
|
||||
("testnotMissingSupplementals", testnotMissingSupplementals),
|
||||
("testUnescape", testUnescape),
|
||||
("testStrictUnescape", testStrictUnescape),
|
||||
("testCaseSensitive", testCaseSensitive),
|
||||
("testQuoteReplacements", testQuoteReplacements),
|
||||
("testLetterDigitEntities", testLetterDigitEntities),
|
||||
("testNoSpuriousDecodes", testNoSpuriousDecodes),
|
||||
("testUscapesGtInXmlAttributesButNotInHtml", testUscapesGtInXmlAttributesButNotInHtml)
|
||||
]
|
||||
}()
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,6 @@ class FormElementTest: XCTestCase {
|
|||
// assertEquals("http://example.com/", con.request().url().toExternalForm());
|
||||
// }
|
||||
|
||||
|
||||
//TODO:
|
||||
// @Test public void actionWithNoBaseUri() {
|
||||
// String html = "<form><input name='q'></form>";
|
||||
|
@ -152,9 +151,9 @@ class FormElementTest: XCTestCase {
|
|||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testHasAssociatedControls" , testHasAssociatedControls),
|
||||
("testFormsAddedAfterParseAreFormElements" , testFormsAddedAfterParseAreFormElements),
|
||||
("testControlsAddedAfterParseAreLinkedWithForms" , testControlsAddedAfterParseAreLinkedWithForms)
|
||||
("testHasAssociatedControls", testHasAssociatedControls),
|
||||
("testFormsAddedAfterParseAreFormElements", testFormsAddedAfterParseAreFormElements),
|
||||
("testControlsAddedAfterParseAreLinkedWithForms", testControlsAddedAfterParseAreLinkedWithForms)
|
||||
]
|
||||
}()
|
||||
}
|
||||
|
|
|
@ -121,11 +121,11 @@ class HtmlParserTest: XCTestCase {
|
|||
XCTAssertEqual(3, head.children().size())
|
||||
XCTAssertEqual(1, body.children().size())
|
||||
|
||||
XCTAssertEqual("keywords",try head.getElementsByTag("meta").get(0).attr("name"))
|
||||
XCTAssertEqual("keywords", try head.getElementsByTag("meta").get(0).attr("name"))
|
||||
XCTAssertEqual(0, try body.getElementsByTag("meta").size())
|
||||
XCTAssertEqual("jsoup",try doc.title())
|
||||
XCTAssertEqual("Hello world",try body.text())
|
||||
XCTAssertEqual("Hello world",try body.children().get(0).text())
|
||||
XCTAssertEqual("jsoup", try doc.title())
|
||||
XCTAssertEqual("Hello world", try body.text())
|
||||
XCTAssertEqual("Hello world", try body.children().get(0).text())
|
||||
}
|
||||
|
||||
func testCreatesStructureFromBodySnippet()throws {
|
||||
|
@ -133,7 +133,7 @@ class HtmlParserTest: XCTestCase {
|
|||
// needs to move into the start of the body
|
||||
let html = "foo <b>bar</b> baz"
|
||||
let doc = try SwiftSoup.parse(html)
|
||||
XCTAssertEqual("foo bar baz",try doc.text())
|
||||
XCTAssertEqual("foo bar baz", try doc.text())
|
||||
|
||||
}
|
||||
|
||||
|
@ -167,8 +167,8 @@ class HtmlParserTest: XCTestCase {
|
|||
func testHandlesTextArea()throws {
|
||||
let doc: Document = try SwiftSoup.parse("<textarea>Hello</textarea>")
|
||||
let els: Elements = try doc.select("textarea")
|
||||
XCTAssertEqual("Hello",try els.text())
|
||||
XCTAssertEqual("Hello",try els.val())
|
||||
XCTAssertEqual("Hello", try els.text())
|
||||
XCTAssertEqual("Hello", try els.val())
|
||||
}
|
||||
|
||||
func testPreservesSpaceInTextArea()throws {
|
||||
|
@ -179,7 +179,7 @@ class HtmlParserTest: XCTestCase {
|
|||
XCTAssertEqual(expect, try el.text())
|
||||
XCTAssertEqual(expect, try el.val())
|
||||
XCTAssertEqual(expect, try el.html())
|
||||
XCTAssertEqual("<textarea>\n\t" + expect + "\n</textarea>",try el.outerHtml()) // but preserved in round-trip html
|
||||
XCTAssertEqual("<textarea>\n\t" + expect + "\n</textarea>", try el.outerHtml()) // but preserved in round-trip html
|
||||
}
|
||||
|
||||
func testPreservesSpaceInScript()throws {
|
||||
|
@ -363,7 +363,7 @@ class HtmlParserTest: XCTestCase {
|
|||
XCTAssertEqual(4, try doc.select("dt, dd").size())
|
||||
let dts: Elements = try doc.select("dt")
|
||||
XCTAssertEqual(2, dts.size())
|
||||
XCTAssertEqual("Zug",try dts.get(1).nextElementSibling()?.text())
|
||||
XCTAssertEqual("Zug", try dts.get(1).nextElementSibling()?.text())
|
||||
}
|
||||
|
||||
func testHandlesBlocksInDefinitions()throws {
|
||||
|
@ -372,7 +372,7 @@ class HtmlParserTest: XCTestCase {
|
|||
let doc = try SwiftSoup.parse(h)
|
||||
XCTAssertEqual("dt", try doc.select("#1").first()!.parent()!.tagName())
|
||||
XCTAssertEqual("dd", try doc.select("#2").first()!.parent()!.tagName())
|
||||
XCTAssertEqual("<dl><dt><div id=\"1\">Term</div></dt><dd><div id=\"2\">Def</div></dd></dl>",try TextUtil.stripNewlines(doc.body()!.html()))
|
||||
XCTAssertEqual("<dl><dt><div id=\"1\">Term</div></dt><dd><div id=\"2\">Def</div></dd></dl>", try TextUtil.stripNewlines(doc.body()!.html()))
|
||||
}
|
||||
|
||||
func testHandlesFrames()throws {
|
||||
|
@ -386,7 +386,7 @@ class HtmlParserTest: XCTestCase {
|
|||
func testIgnoresContentAfterFrameset()throws {
|
||||
let h = "<html><head><title>One</title></head><frameset><frame /><frame /></frameset><table></table></html>"
|
||||
let doc = try SwiftSoup.parse(h)
|
||||
XCTAssertEqual("<html><head><title>One</title></head><frameset><frame><frame></frameset></html>",try TextUtil.stripNewlines(doc.html()))
|
||||
XCTAssertEqual("<html><head><title>One</title></head><frameset><frame><frame></frameset></html>", try TextUtil.stripNewlines(doc.html()))
|
||||
// no body, no table. No crash!
|
||||
}
|
||||
|
||||
|
@ -404,7 +404,7 @@ class HtmlParserTest: XCTestCase {
|
|||
let doc = try SwiftSoup.parse(h, "http://example.com/")
|
||||
let a: Element = try doc.select("a").first()!
|
||||
XCTAssertEqual("/foo", try a.attr("href"))
|
||||
XCTAssertEqual("http://example.com/foo",try a.attr("abs:href"))
|
||||
XCTAssertEqual("http://example.com/foo", try a.attr("abs:href"))
|
||||
}
|
||||
|
||||
func testNormalisesDocument()throws {
|
||||
|
@ -444,43 +444,43 @@ class HtmlParserTest: XCTestCase {
|
|||
XCTAssertEqual("<h1>Hello </h1><h2>There <hgroup><h1>Another</h1><h2>headline</h2></hgroup> <hgroup><h1>More</h1><p>stuff</p></hgroup></h2>", try TextUtil.stripNewlines(doc.body()!.html()))
|
||||
}
|
||||
|
||||
func testRelaxedTags()throws{
|
||||
func testRelaxedTags()throws {
|
||||
let doc = try SwiftSoup.parse("<abc_def id=1>Hello</abc_def> <abc-def>There</abc-def>")
|
||||
XCTAssertEqual("<abc_def id=\"1\">Hello</abc_def> <abc-def>There</abc-def>", try TextUtil.stripNewlines(doc.body()!.html()))
|
||||
}
|
||||
|
||||
func testHeaderContents()throws{
|
||||
func testHeaderContents()throws {
|
||||
// h* tags (h1 .. h9) in browsers can handle any internal content other than other h*. which is not per any
|
||||
// spec, which defines them as containing phrasing content only. so, reality over theory.
|
||||
let doc = try SwiftSoup.parse("<h1>Hello <div>There</div> now</h1> <h2>More <h3>Content</h3></h2>")
|
||||
XCTAssertEqual("<h1>Hello <div>There</div> now</h1> <h2>More </h2><h3>Content</h3>", try TextUtil.stripNewlines(doc.body()!.html()))
|
||||
}
|
||||
|
||||
func testSpanContents()throws{
|
||||
func testSpanContents()throws {
|
||||
// like h1 tags, the spec says SPAN is phrasing only, but browsers and publisher treat span as a block tag
|
||||
let doc = try SwiftSoup.parse("<span>Hello <div>there</div> <span>now</span></span>")
|
||||
XCTAssertEqual("<span>Hello <div>there</div> <span>now</span></span>", try TextUtil.stripNewlines(doc.body()!.html()))
|
||||
}
|
||||
|
||||
func testNoImagesInNoScriptInHead()throws{
|
||||
func testNoImagesInNoScriptInHead()throws {
|
||||
// jsoup used to allow, but against spec if parsing with noscript
|
||||
let doc = try SwiftSoup.parse("<html><head><noscript><img src='foo'></noscript></head><body><p>Hello</p></body></html>")
|
||||
XCTAssertEqual("<html><head><noscript><img src=\"foo\"></noscript></head><body><p>Hello</p></body></html>", try TextUtil.stripNewlines(doc.html()))
|
||||
}
|
||||
|
||||
func testAFlowContents()throws{
|
||||
func testAFlowContents()throws {
|
||||
// html5 has <a> as either phrasing or block
|
||||
let doc = try SwiftSoup.parse("<a>Hello <div>there</div> <span>now</span></a>")
|
||||
XCTAssertEqual("<a>Hello <div>there</div> <span>now</span></a>", try TextUtil.stripNewlines(doc.body()!.html()))
|
||||
}
|
||||
|
||||
func testFontFlowContents()throws{
|
||||
func testFontFlowContents()throws {
|
||||
// html5 has no definition of <font>; often used as flow
|
||||
let doc = try SwiftSoup.parse("<font>Hello <div>there</div> <span>now</span></font>")
|
||||
XCTAssertEqual("<font>Hello <div>there</div> <span>now</span></font>", try TextUtil.stripNewlines(doc.body()!.html()))
|
||||
}
|
||||
|
||||
func testhandlesMisnestedTagsBI()throws{
|
||||
func testhandlesMisnestedTagsBI()throws {
|
||||
// whatwg: <b><i></b></i>
|
||||
let h = "<p>1<b>2<i>3</b>4</i>5</p>"
|
||||
let doc = try SwiftSoup.parse(h)
|
||||
|
@ -488,7 +488,7 @@ class HtmlParserTest: XCTestCase {
|
|||
// adoption agency on </b>, reconstruction of formatters on 4.
|
||||
}
|
||||
|
||||
func testhandlesMisnestedTagsBP()throws{
|
||||
func testhandlesMisnestedTagsBP()throws {
|
||||
// whatwg: <b><p></b></p>
|
||||
let h = "<b>1<p>2</b>3</p>"
|
||||
let doc = try SwiftSoup.parse(h)
|
||||
|
@ -587,73 +587,73 @@ class HtmlParserTest: XCTestCase {
|
|||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testParsesSimpleDocument" , testParsesSimpleDocument),
|
||||
("testParsesRoughAttributes" , testParsesRoughAttributes),
|
||||
("testParsesQuiteRoughAttributes" , testParsesQuiteRoughAttributes),
|
||||
("testParsesComments" , testParsesComments),
|
||||
("testParsesUnterminatedComments" , testParsesUnterminatedComments),
|
||||
("testDropsUnterminatedTag" , testDropsUnterminatedTag),
|
||||
("testDropsUnterminatedAttribute" , testDropsUnterminatedAttribute),
|
||||
("testParsesUnterminatedTextarea" , testParsesUnterminatedTextarea),
|
||||
("testParsesUnterminatedOption" , testParsesUnterminatedOption),
|
||||
("testSpaceAfterTag" , testSpaceAfterTag),
|
||||
("testCreatesDocumentStructure" , testCreatesDocumentStructure),
|
||||
("testCreatesStructureFromBodySnippet" , testCreatesStructureFromBodySnippet),
|
||||
("testHandlesEscapedData" , testHandlesEscapedData),
|
||||
("testHandlesDataOnlyTags" , testHandlesDataOnlyTags),
|
||||
("testHandlesTextAfterData" , testHandlesTextAfterData),
|
||||
("testHandlesTextArea" , testHandlesTextArea),
|
||||
("testPreservesSpaceInTextArea" , testPreservesSpaceInTextArea),
|
||||
("testPreservesSpaceInScript" , testPreservesSpaceInScript),
|
||||
("testDoesNotCreateImplicitLists" , testDoesNotCreateImplicitLists),
|
||||
("testDiscardsNakedTds" , testDiscardsNakedTds),
|
||||
("testHandlesNestedImplicitTable" , testHandlesNestedImplicitTable),
|
||||
("testHandlesWhatWgExpensesTableExample" , testHandlesWhatWgExpensesTableExample),
|
||||
("testHandlesTbodyTable" , testHandlesTbodyTable),
|
||||
("testHandlesImplicitCaptionClose" , testHandlesImplicitCaptionClose),
|
||||
("testNoTableDirectInTable" , testNoTableDirectInTable),
|
||||
("testIgnoresDupeEndTrTag" , testIgnoresDupeEndTrTag),
|
||||
("testHandlesBaseTags" , testHandlesBaseTags),
|
||||
("testHandlesProtocolRelativeUrl" , testHandlesProtocolRelativeUrl),
|
||||
("testHandlesCdata" , testHandlesCdata),
|
||||
("testHandlesUnclosedCdataAtEOF" , testHandlesUnclosedCdataAtEOF),
|
||||
("testHandlesInvalidStartTags" , testHandlesInvalidStartTags),
|
||||
("testHandlesUnknownTags" , testHandlesUnknownTags),
|
||||
("testHandlesUnknownInlineTags" , testHandlesUnknownInlineTags),
|
||||
("testParsesBodyFragment" , testParsesBodyFragment),
|
||||
("testHandlesUnknownNamespaceTags" , testHandlesUnknownNamespaceTags),
|
||||
("testHandlesKnownEmptyBlocks" , testHandlesKnownEmptyBlocks),
|
||||
("testHandlesSolidusAtAttributeEnd" , testHandlesSolidusAtAttributeEnd),
|
||||
("testHandlesMultiClosingBody" , testHandlesMultiClosingBody),
|
||||
("testHandlesUnclosedDefinitionLists" , testHandlesUnclosedDefinitionLists),
|
||||
("testHandlesBlocksInDefinitions" , testHandlesBlocksInDefinitions),
|
||||
("testHandlesFrames" , testHandlesFrames),
|
||||
("testIgnoresContentAfterFrameset" , testIgnoresContentAfterFrameset),
|
||||
("testHandlesJavadocFont" , testHandlesJavadocFont),
|
||||
("testHandlesBaseWithoutHref" , testHandlesBaseWithoutHref),
|
||||
("testNormalisesDocument" , testNormalisesDocument),
|
||||
("testNormalisesEmptyDocument" , testNormalisesEmptyDocument),
|
||||
("testNormalisesHeadlessBody" , testNormalisesHeadlessBody),
|
||||
("testNormalisedBodyAfterContent" , testNormalisedBodyAfterContent),
|
||||
("testfindsCharsetInMalformedMeta" , testfindsCharsetInMalformedMeta),
|
||||
("testHgroup" , testHgroup),
|
||||
("testRelaxedTags" , testRelaxedTags),
|
||||
("testHeaderContents" , testHeaderContents),
|
||||
("testSpanContents" , testSpanContents),
|
||||
("testNoImagesInNoScriptInHead" , testNoImagesInNoScriptInHead),
|
||||
("testAFlowContents" , testAFlowContents),
|
||||
("testFontFlowContents" , testFontFlowContents),
|
||||
("testhandlesMisnestedTagsBI" , testhandlesMisnestedTagsBI),
|
||||
("testhandlesMisnestedTagsBP" , testhandlesMisnestedTagsBP),
|
||||
("testhandlesUnexpectedMarkupInTables" , testhandlesUnexpectedMarkupInTables),
|
||||
("testHandlesUnclosedFormattingElements" , testHandlesUnclosedFormattingElements),
|
||||
("testhandlesUnclosedAnchors" , testhandlesUnclosedAnchors),
|
||||
("testreconstructFormattingElements" , testreconstructFormattingElements),
|
||||
("testreconstructFormattingElementsInTable" , testreconstructFormattingElementsInTable),
|
||||
("testcommentBeforeHtml" , testcommentBeforeHtml),
|
||||
("testemptyTdTag" , testemptyTdTag),
|
||||
("testhandlesSolidusInA" , testhandlesSolidusInA),
|
||||
("testhandlesSpanInTbody" , testhandlesSpanInTbody)
|
||||
("testParsesSimpleDocument", testParsesSimpleDocument),
|
||||
("testParsesRoughAttributes", testParsesRoughAttributes),
|
||||
("testParsesQuiteRoughAttributes", testParsesQuiteRoughAttributes),
|
||||
("testParsesComments", testParsesComments),
|
||||
("testParsesUnterminatedComments", testParsesUnterminatedComments),
|
||||
("testDropsUnterminatedTag", testDropsUnterminatedTag),
|
||||
("testDropsUnterminatedAttribute", testDropsUnterminatedAttribute),
|
||||
("testParsesUnterminatedTextarea", testParsesUnterminatedTextarea),
|
||||
("testParsesUnterminatedOption", testParsesUnterminatedOption),
|
||||
("testSpaceAfterTag", testSpaceAfterTag),
|
||||
("testCreatesDocumentStructure", testCreatesDocumentStructure),
|
||||
("testCreatesStructureFromBodySnippet", testCreatesStructureFromBodySnippet),
|
||||
("testHandlesEscapedData", testHandlesEscapedData),
|
||||
("testHandlesDataOnlyTags", testHandlesDataOnlyTags),
|
||||
("testHandlesTextAfterData", testHandlesTextAfterData),
|
||||
("testHandlesTextArea", testHandlesTextArea),
|
||||
("testPreservesSpaceInTextArea", testPreservesSpaceInTextArea),
|
||||
("testPreservesSpaceInScript", testPreservesSpaceInScript),
|
||||
("testDoesNotCreateImplicitLists", testDoesNotCreateImplicitLists),
|
||||
("testDiscardsNakedTds", testDiscardsNakedTds),
|
||||
("testHandlesNestedImplicitTable", testHandlesNestedImplicitTable),
|
||||
("testHandlesWhatWgExpensesTableExample", testHandlesWhatWgExpensesTableExample),
|
||||
("testHandlesTbodyTable", testHandlesTbodyTable),
|
||||
("testHandlesImplicitCaptionClose", testHandlesImplicitCaptionClose),
|
||||
("testNoTableDirectInTable", testNoTableDirectInTable),
|
||||
("testIgnoresDupeEndTrTag", testIgnoresDupeEndTrTag),
|
||||
("testHandlesBaseTags", testHandlesBaseTags),
|
||||
("testHandlesProtocolRelativeUrl", testHandlesProtocolRelativeUrl),
|
||||
("testHandlesCdata", testHandlesCdata),
|
||||
("testHandlesUnclosedCdataAtEOF", testHandlesUnclosedCdataAtEOF),
|
||||
("testHandlesInvalidStartTags", testHandlesInvalidStartTags),
|
||||
("testHandlesUnknownTags", testHandlesUnknownTags),
|
||||
("testHandlesUnknownInlineTags", testHandlesUnknownInlineTags),
|
||||
("testParsesBodyFragment", testParsesBodyFragment),
|
||||
("testHandlesUnknownNamespaceTags", testHandlesUnknownNamespaceTags),
|
||||
("testHandlesKnownEmptyBlocks", testHandlesKnownEmptyBlocks),
|
||||
("testHandlesSolidusAtAttributeEnd", testHandlesSolidusAtAttributeEnd),
|
||||
("testHandlesMultiClosingBody", testHandlesMultiClosingBody),
|
||||
("testHandlesUnclosedDefinitionLists", testHandlesUnclosedDefinitionLists),
|
||||
("testHandlesBlocksInDefinitions", testHandlesBlocksInDefinitions),
|
||||
("testHandlesFrames", testHandlesFrames),
|
||||
("testIgnoresContentAfterFrameset", testIgnoresContentAfterFrameset),
|
||||
("testHandlesJavadocFont", testHandlesJavadocFont),
|
||||
("testHandlesBaseWithoutHref", testHandlesBaseWithoutHref),
|
||||
("testNormalisesDocument", testNormalisesDocument),
|
||||
("testNormalisesEmptyDocument", testNormalisesEmptyDocument),
|
||||
("testNormalisesHeadlessBody", testNormalisesHeadlessBody),
|
||||
("testNormalisedBodyAfterContent", testNormalisedBodyAfterContent),
|
||||
("testfindsCharsetInMalformedMeta", testfindsCharsetInMalformedMeta),
|
||||
("testHgroup", testHgroup),
|
||||
("testRelaxedTags", testRelaxedTags),
|
||||
("testHeaderContents", testHeaderContents),
|
||||
("testSpanContents", testSpanContents),
|
||||
("testNoImagesInNoScriptInHead", testNoImagesInNoScriptInHead),
|
||||
("testAFlowContents", testAFlowContents),
|
||||
("testFontFlowContents", testFontFlowContents),
|
||||
("testhandlesMisnestedTagsBI", testhandlesMisnestedTagsBI),
|
||||
("testhandlesMisnestedTagsBP", testhandlesMisnestedTagsBP),
|
||||
("testhandlesUnexpectedMarkupInTables", testhandlesUnexpectedMarkupInTables),
|
||||
("testHandlesUnclosedFormattingElements", testHandlesUnclosedFormattingElements),
|
||||
("testhandlesUnclosedAnchors", testhandlesUnclosedAnchors),
|
||||
("testreconstructFormattingElements", testreconstructFormattingElements),
|
||||
("testreconstructFormattingElementsInTable", testreconstructFormattingElementsInTable),
|
||||
("testcommentBeforeHtml", testcommentBeforeHtml),
|
||||
("testemptyTdTag", testemptyTdTag),
|
||||
("testhandlesSolidusInA", testhandlesSolidusInA),
|
||||
("testhandlesSpanInTbody", testhandlesSpanInTbody)
|
||||
]
|
||||
}()
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import SwiftSoup
|
|||
class NodeTest: XCTestCase {
|
||||
|
||||
func testHandlesBaseUri() {
|
||||
do{
|
||||
do {
|
||||
let tag: Tag = try Tag.valueOf("a")
|
||||
let attribs: Attributes = Attributes()
|
||||
try attribs.put("relHref", "/foo")
|
||||
|
@ -31,14 +31,14 @@ class NodeTest: XCTestCase {
|
|||
XCTAssertEqual("http://bar/qux", try dodgyBase.absUrl("absHref")) // base fails, but href good, so get that
|
||||
//TODO:Nabil in swift an url with scheme wtf is valid , find a method to validate schemes
|
||||
//XCTAssertEqual("", try dodgyBase.absUrl("relHref")); // base fails, only rel href, so return nothing
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func testSetBaseUriIsRecursive() {
|
||||
do{
|
||||
do {
|
||||
let doc: Document = try SwiftSoup.parse("<div><p></p></div>")
|
||||
let baseUri: String = "https://jsoup.org"
|
||||
try doc.setBaseUri(baseUri)
|
||||
|
@ -46,37 +46,37 @@ class NodeTest: XCTestCase {
|
|||
XCTAssertEqual(baseUri, doc.getBaseUri())
|
||||
XCTAssertEqual(baseUri, try doc.select("div").first()?.getBaseUri())
|
||||
XCTAssertEqual(baseUri, try doc.select("p").first()?.getBaseUri())
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func testHandlesAbsPrefix() {
|
||||
do{
|
||||
do {
|
||||
let doc: Document = try SwiftSoup.parse("<a href=/foo>Hello</a>", "https://jsoup.org/")
|
||||
let a: Element? = try doc.select("a").first()
|
||||
XCTAssertEqual("/foo", try a?.attr("href"))
|
||||
XCTAssertEqual("https://jsoup.org/foo", try a?.attr("abs:href"))
|
||||
//XCTAssertTrue(a!.hasAttr("abs:href"));//TODO:nabil
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func testHandlesAbsOnImage() {
|
||||
do{
|
||||
do {
|
||||
let doc: Document = try SwiftSoup.parse("<p><img src=\"/rez/osi_logo.png\" /></p>", "https://jsoup.org/")
|
||||
let img: Element? = try doc.select("img").first()
|
||||
XCTAssertEqual("https://jsoup.org/rez/osi_logo.png", try img?.attr("abs:src"))
|
||||
XCTAssertEqual(try img?.absUrl("src"), try img?.attr("abs:src"))
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func testHandlesAbsPrefixOnHasAttr() {
|
||||
do{
|
||||
do {
|
||||
// 1: no abs url; 2: has abs url
|
||||
let doc: Document = try SwiftSoup.parse("<a id=1 href='/foo'>One</a> <a id=2 href='https://jsoup.org/'>Two</a>")
|
||||
let one: Element = try doc.select("#1").first()!
|
||||
|
@ -89,22 +89,21 @@ class NodeTest: XCTestCase {
|
|||
XCTAssertTrue(two.hasAttr("abs:href"))
|
||||
XCTAssertTrue(two.hasAttr("href"))
|
||||
XCTAssertEqual("https://jsoup.org/", try two.absUrl("href"))
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
func testLiteralAbsPrefix() {
|
||||
do{
|
||||
do {
|
||||
// if there is a literal attribute "abs:xxx", don't try and make absolute.
|
||||
let doc: Document = try SwiftSoup.parse("<a abs:href='odd'>One</a>")
|
||||
let el: Element = try doc.select("a").first()!
|
||||
XCTAssertTrue(el.hasAttr("abs:href"))
|
||||
XCTAssertEqual("odd", try el.attr("abs:href"))
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -123,17 +122,17 @@ class NodeTest: XCTestCase {
|
|||
}
|
||||
*/
|
||||
func testHandleAbsOnLocalhostFileUris() {
|
||||
do{
|
||||
do {
|
||||
let doc: Document = try SwiftSoup.parse("<a href='password'>One/a><a href='/var/log/messages'>Two</a>", "file://localhost/etc/")
|
||||
let one: Element? = try doc.select("a").first()
|
||||
XCTAssertEqual("file://localhost/etc/password", try one?.absUrl("href"))
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func testHandlesAbsOnProtocolessAbsoluteUris() {
|
||||
do{
|
||||
do {
|
||||
let doc1: Document = try SwiftSoup.parse("<a href='//example.net/foo'>One</a>", "http://example.com/")
|
||||
let doc2: Document = try SwiftSoup.parse("<a href='//example.net/foo'>One</a>", "https://example.com/")
|
||||
|
||||
|
@ -145,13 +144,13 @@ class NodeTest: XCTestCase {
|
|||
|
||||
let doc3: Document = try SwiftSoup.parse("<img src=//www.google.com/images/errors/logo_sm.gif alt=Google>", "https://google.com")
|
||||
XCTAssertEqual("https://www.google.com/images/errors/logo_sm.gif", try doc3.select("img").attr("abs:src"))
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func testAbsHandlesRelativeQuery() {
|
||||
do{
|
||||
do {
|
||||
let doc: Document = try SwiftSoup.parse("<a href='?foo'>One</a> <a href='bar.html?foo'>Two</a>", "https://jsoup.org/path/file?bar")
|
||||
|
||||
let a1: Element? = try doc.select("a").first()
|
||||
|
@ -159,61 +158,61 @@ class NodeTest: XCTestCase {
|
|||
|
||||
let a2: Element? = try doc.select("a").get(1)
|
||||
XCTAssertEqual("https://jsoup.org/path/bar.html?foo", try a2?.absUrl("href"))
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func testAbsHandlesDotFromIndex() {
|
||||
do{
|
||||
do {
|
||||
let doc: Document = try SwiftSoup.parse("<a href='./one/two.html'>One</a>", "http://example.com")
|
||||
let a1: Element? = try doc.select("a").first()
|
||||
XCTAssertEqual("http://example.com/one/two.html", try a1?.absUrl("href"))
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func testRemove() {
|
||||
do{
|
||||
do {
|
||||
let doc: Document = try SwiftSoup.parse("<p>One <span>two</span> three</p>")
|
||||
let p: Element? = try doc.select("p").first()
|
||||
try p?.childNode(0).remove()
|
||||
|
||||
XCTAssertEqual("two three", try p?.text())
|
||||
XCTAssertEqual("<span>two</span> three", TextUtil.stripNewlines(try p!.html()))
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func testReplace() {
|
||||
do{
|
||||
do {
|
||||
let doc: Document = try SwiftSoup.parse("<p>One <span>two</span> three</p>")
|
||||
let p: Element? = try doc.select("p").first()
|
||||
let insert: Element = try doc.createElement("em").text("foo")
|
||||
try p?.childNode(1).replaceWith(insert)
|
||||
|
||||
XCTAssertEqual("One <em>foo</em> three", try p?.html())
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func testOwnerDocument() {
|
||||
do{
|
||||
do {
|
||||
let doc: Document = try SwiftSoup.parse("<p>Hello")
|
||||
let p: Element? = try doc.select("p").first()
|
||||
XCTAssertTrue(p?.ownerDocument() == doc)
|
||||
XCTAssertTrue(doc.ownerDocument() == doc)
|
||||
XCTAssertNil(doc.parent())
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func testBefore() {
|
||||
do{
|
||||
do {
|
||||
let doc: Document = try SwiftSoup.parse("<p>One <b>two</b> three</p>")
|
||||
let newNode: Element = Element(try Tag.valueOf("em"), "")
|
||||
try newNode.appendText("four")
|
||||
|
@ -223,13 +222,13 @@ class NodeTest: XCTestCase {
|
|||
|
||||
try doc.select("b").first()?.before("<i>five</i>")
|
||||
XCTAssertEqual("<p>One <em>four</em><i>five</i><b>two</b> three</p>", try doc.body()?.html())
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func testAfter() {
|
||||
do{
|
||||
do {
|
||||
let doc: Document = try SwiftSoup.parse("<p>One <b>two</b> three</p>")
|
||||
let newNode: Element = Element(try Tag.valueOf("em"), "")
|
||||
try newNode.appendText("four")
|
||||
|
@ -239,14 +238,14 @@ class NodeTest: XCTestCase {
|
|||
|
||||
try doc.select("b").first()?.after("<i>five</i>")
|
||||
XCTAssertEqual("<p>One <b>two</b><i>five</i><em>four</em> three</p>", try doc.body()?.html())
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func testUnwrap() {
|
||||
do{
|
||||
do {
|
||||
let doc: Document = try SwiftSoup.parse("<div>One <span>Two <b>Three</b></span> Four</div>")
|
||||
let span: Element? = try doc.select("span").first()
|
||||
let twoText: Node? = span?.childNode(0)
|
||||
|
@ -257,49 +256,49 @@ class NodeTest: XCTestCase {
|
|||
XCTAssertEqual("Two ", (node as? TextNode)?.text())
|
||||
XCTAssertEqual(node, twoText)
|
||||
XCTAssertEqual(node?.parent(), try doc.select("div").first())
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func testUnwrapNoChildren() {
|
||||
do{
|
||||
do {
|
||||
let doc: Document = try SwiftSoup.parse("<div>One <span></span> Two</div>")
|
||||
let span: Element? = try doc.select("span").first()
|
||||
let node: Node? = try span?.unwrap()
|
||||
XCTAssertEqual("<div>One Two</div>", TextUtil.stripNewlines(try doc.body()!.html()))
|
||||
XCTAssertTrue(node == nil)
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func testTraverse() {
|
||||
do{
|
||||
do {
|
||||
let doc: Document = try SwiftSoup.parse("<div><p>Hello</p></div><div>There</div>")
|
||||
let accum: StringBuilder = StringBuilder()
|
||||
class nv : NodeVisitor{
|
||||
class nv: NodeVisitor {
|
||||
let accum: StringBuilder
|
||||
init (_ accum: StringBuilder){
|
||||
init (_ accum: StringBuilder) {
|
||||
self.accum = accum
|
||||
}
|
||||
func head(_ node: Node, _ depth: Int)throws{
|
||||
func head(_ node: Node, _ depth: Int)throws {
|
||||
accum.append("<" + node.nodeName() + ">")
|
||||
}
|
||||
func tail(_ node: Node, _ depth: Int)throws{
|
||||
func tail(_ node: Node, _ depth: Int)throws {
|
||||
accum.append("</" + node.nodeName() + ">")
|
||||
}
|
||||
}
|
||||
try doc.select("div").first()?.traverse(nv(accum))
|
||||
XCTAssertEqual("<div><p><#text></#text></p></div>", accum.toString())
|
||||
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func testOrphanNodeReturnsNullForSiblingElements() {
|
||||
do{
|
||||
do {
|
||||
let node: Node = Element(try Tag.valueOf("p"), "")
|
||||
let el: Element = Element(try Tag.valueOf("p"), "")
|
||||
|
||||
|
@ -312,13 +311,13 @@ class NodeTest: XCTestCase {
|
|||
XCTAssertEqual(0, el.siblingElements().size())
|
||||
XCTAssertNil(try el.previousElementSibling())
|
||||
XCTAssertNil(try el.nextElementSibling())
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func testNodeIsNotASiblingOfItself() {
|
||||
do{
|
||||
do {
|
||||
let doc: Document = try SwiftSoup.parse("<div><p>One<p>Two<p>Three</div>")
|
||||
let p2: Element = try doc.select("p").get(1)
|
||||
|
||||
|
@ -327,13 +326,13 @@ class NodeTest: XCTestCase {
|
|||
XCTAssertEqual(2, nodes.count)
|
||||
XCTAssertEqual("<p>One</p>", try nodes[0].outerHtml())
|
||||
XCTAssertEqual("<p>Three</p>", try nodes[1].outerHtml())
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func testChildNodesCopy() {
|
||||
do{
|
||||
do {
|
||||
let doc: Document = try SwiftSoup.parse("<div id=1>Text 1 <p>One</p> Text 2 <p>Two<p>Three</div><div id=2>")
|
||||
let div1: Element? = try doc.select("#1").first()
|
||||
let div2: Element? = try doc.select("#2").first()
|
||||
|
@ -345,13 +344,13 @@ class NodeTest: XCTestCase {
|
|||
XCTAssertEqual("Text 1 ", tn1?.text())
|
||||
try div2?.insertChildren(-1, divChildren!)
|
||||
XCTAssertEqual("<div id=\"1\">Text 1 <p>One</p> Text 2 <p>Two</p><p>Three</p></div><div id=\"2\">Text 1 updated"+"<p>One</p> Text 2 <p>Two</p><p>Three</p></div>", TextUtil.stripNewlines(try doc.body()!.html()))
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func testSupportsClone() {
|
||||
do{
|
||||
do {
|
||||
let doc: Document = try SwiftSoup.parse("<div class=foo>Text</div>")
|
||||
let el: Element = try doc.select("div").first()!
|
||||
XCTAssertTrue(el.hasClass("foo"))
|
||||
|
@ -366,35 +365,35 @@ class NodeTest: XCTestCase {
|
|||
XCTAssertTrue(elClone.hasClass("foo"))
|
||||
XCTAssertTrue(try el.text() == "None")
|
||||
XCTAssertTrue(try elClone.text()=="Text")
|
||||
}catch{
|
||||
XCTAssertEqual(1,2)
|
||||
} catch {
|
||||
XCTAssertEqual(1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testHandlesBaseUri" , testHandlesBaseUri),
|
||||
("testSetBaseUriIsRecursive" , testSetBaseUriIsRecursive),
|
||||
("testHandlesAbsPrefix" , testHandlesAbsPrefix),
|
||||
("testHandlesAbsOnImage" , testHandlesAbsOnImage),
|
||||
("testHandlesAbsPrefixOnHasAttr" , testHandlesAbsPrefixOnHasAttr),
|
||||
("testLiteralAbsPrefix" , testLiteralAbsPrefix),
|
||||
("testHandleAbsOnLocalhostFileUris" , testHandleAbsOnLocalhostFileUris),
|
||||
("testHandlesAbsOnProtocolessAbsoluteUris" , testHandlesAbsOnProtocolessAbsoluteUris),
|
||||
("testAbsHandlesRelativeQuery" , testAbsHandlesRelativeQuery),
|
||||
("testAbsHandlesDotFromIndex" , testAbsHandlesDotFromIndex),
|
||||
("testRemove" , testRemove),
|
||||
("testReplace" , testReplace),
|
||||
("testOwnerDocument" , testOwnerDocument),
|
||||
("testBefore" , testBefore),
|
||||
("testAfter" , testAfter),
|
||||
("testUnwrap" , testUnwrap),
|
||||
("testUnwrapNoChildren" , testUnwrapNoChildren),
|
||||
("testTraverse" , testTraverse),
|
||||
("testOrphanNodeReturnsNullForSiblingElements" , testOrphanNodeReturnsNullForSiblingElements),
|
||||
("testNodeIsNotASiblingOfItself" , testNodeIsNotASiblingOfItself),
|
||||
("testChildNodesCopy" , testChildNodesCopy),
|
||||
("testSupportsClone" , testSupportsClone)
|
||||
("testHandlesBaseUri", testHandlesBaseUri),
|
||||
("testSetBaseUriIsRecursive", testSetBaseUriIsRecursive),
|
||||
("testHandlesAbsPrefix", testHandlesAbsPrefix),
|
||||
("testHandlesAbsOnImage", testHandlesAbsOnImage),
|
||||
("testHandlesAbsPrefixOnHasAttr", testHandlesAbsPrefixOnHasAttr),
|
||||
("testLiteralAbsPrefix", testLiteralAbsPrefix),
|
||||
("testHandleAbsOnLocalhostFileUris", testHandleAbsOnLocalhostFileUris),
|
||||
("testHandlesAbsOnProtocolessAbsoluteUris", testHandlesAbsOnProtocolessAbsoluteUris),
|
||||
("testAbsHandlesRelativeQuery", testAbsHandlesRelativeQuery),
|
||||
("testAbsHandlesDotFromIndex", testAbsHandlesDotFromIndex),
|
||||
("testRemove", testRemove),
|
||||
("testReplace", testReplace),
|
||||
("testOwnerDocument", testOwnerDocument),
|
||||
("testBefore", testBefore),
|
||||
("testAfter", testAfter),
|
||||
("testUnwrap", testUnwrap),
|
||||
("testUnwrapNoChildren", testUnwrapNoChildren),
|
||||
("testTraverse", testTraverse),
|
||||
("testOrphanNodeReturnsNullForSiblingElements", testOrphanNodeReturnsNullForSiblingElements),
|
||||
("testNodeIsNotASiblingOfItself", testNodeIsNotASiblingOfItself),
|
||||
("testChildNodesCopy", testChildNodesCopy),
|
||||
("testSupportsClone", testSupportsClone)
|
||||
]
|
||||
}()
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ class ParseSettingsTest: XCTestCase {
|
|||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testCaseSupport" , testCaseSupport)
|
||||
("testCaseSupport", testCaseSupport)
|
||||
]
|
||||
}()
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ class QueryParserTest: XCTestCase {
|
|||
XCTAssertTrue((eval as? CombiningEvaluator.Or) != nil)
|
||||
let or: CombiningEvaluator.Or = eval as! CombiningEvaluator.Or
|
||||
XCTAssertEqual(3, or.evaluators.count)
|
||||
for innerEval:Evaluator in or.evaluators {
|
||||
for innerEval: Evaluator in or.evaluators {
|
||||
XCTAssertTrue((innerEval as? CombiningEvaluator.And) != nil)
|
||||
let and: CombiningEvaluator.And = innerEval as! CombiningEvaluator.And
|
||||
XCTAssertEqual(2, and.evaluators.count)
|
||||
|
@ -44,8 +44,8 @@ class QueryParserTest: XCTestCase {
|
|||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testOrGetsCorrectPrecedence" , testOrGetsCorrectPrecedence),
|
||||
("testParsesMultiCorrectly" , testParsesMultiCorrectly)
|
||||
("testOrGetsCorrectPrecedence", testOrGetsCorrectPrecedence),
|
||||
("testParsesMultiCorrectly", testParsesMultiCorrectly)
|
||||
]
|
||||
}()
|
||||
|
||||
|
|
|
@ -695,58 +695,58 @@ class SelectorTest: XCTestCase {
|
|||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testByTag" , testByTag),
|
||||
("testById" , testById),
|
||||
("testByClass" , testByClass),
|
||||
("testByAttribute" , testByAttribute),
|
||||
("testNamespacedTag" , testNamespacedTag),
|
||||
("testWildcardNamespacedTag" , testWildcardNamespacedTag),
|
||||
("testByAttributeStarting" , testByAttributeStarting),
|
||||
("testByAttributeRegex" , testByAttributeRegex),
|
||||
("testByAttributeRegexCharacterClass" , testByAttributeRegexCharacterClass),
|
||||
("testByAttributeRegexCombined" , testByAttributeRegexCombined),
|
||||
("testCombinedWithContains" , testCombinedWithContains),
|
||||
("testAllElements" , testAllElements),
|
||||
("testAllWithClass" , testAllWithClass),
|
||||
("testGroupOr" , testGroupOr),
|
||||
("testGroupOrAttribute" , testGroupOrAttribute),
|
||||
("testDescendant" , testDescendant),
|
||||
("testAnd" , testAnd),
|
||||
("testDeeperDescendant" , testDeeperDescendant),
|
||||
("testParentChildElement" , testParentChildElement),
|
||||
("testParentWithClassChild" , testParentWithClassChild),
|
||||
("testParentChildStar" , testParentChildStar),
|
||||
("testMultiChildDescent" , testMultiChildDescent),
|
||||
("testCaseInsensitive" , testCaseInsensitive),
|
||||
("testAdjacentSiblings" , testAdjacentSiblings),
|
||||
("testAdjacentSiblingsWithId" , testAdjacentSiblingsWithId),
|
||||
("testNotAdjacent" , testNotAdjacent),
|
||||
("testMixCombinator" , testMixCombinator),
|
||||
("testMixCombinatorGroup" , testMixCombinatorGroup),
|
||||
("testGeneralSiblings" , testGeneralSiblings),
|
||||
("testCharactersInIdAndClass" , testCharactersInIdAndClass),
|
||||
("testSupportsLeadingCombinator" , testSupportsLeadingCombinator),
|
||||
("testPseudoLessThan" , testPseudoLessThan),
|
||||
("testPseudoGreaterThan" , testPseudoGreaterThan),
|
||||
("testPseudoEquals" , testPseudoEquals),
|
||||
("testPseudoBetween" , testPseudoBetween),
|
||||
("testPseudoCombined" , testPseudoCombined),
|
||||
("testPseudoHas" , testPseudoHas),
|
||||
("testNestedHas" , testNestedHas),
|
||||
("testPseudoContains" , testPseudoContains),
|
||||
("testPsuedoContainsWithParentheses" , testPsuedoContainsWithParentheses),
|
||||
("testContainsOwn" , testContainsOwn),
|
||||
("testMatches" , testMatches),
|
||||
("testMatchesOwn" , testMatchesOwn),
|
||||
("testRelaxedTags" , testRelaxedTags),
|
||||
("testNotParas" , testNotParas),
|
||||
("testNotAll" , testNotAll),
|
||||
("testNotClass" , testNotClass),
|
||||
("testHandlesCommasInSelector" , testHandlesCommasInSelector),
|
||||
("testSelectSupplementaryCharacter" , testSelectSupplementaryCharacter),
|
||||
("testSelectClassWithSpace" , testSelectClassWithSpace),
|
||||
("testSelectSameElements" , testSelectSameElements),
|
||||
("testAttributeWithBrackets" , testAttributeWithBrackets)
|
||||
("testByTag", testByTag),
|
||||
("testById", testById),
|
||||
("testByClass", testByClass),
|
||||
("testByAttribute", testByAttribute),
|
||||
("testNamespacedTag", testNamespacedTag),
|
||||
("testWildcardNamespacedTag", testWildcardNamespacedTag),
|
||||
("testByAttributeStarting", testByAttributeStarting),
|
||||
("testByAttributeRegex", testByAttributeRegex),
|
||||
("testByAttributeRegexCharacterClass", testByAttributeRegexCharacterClass),
|
||||
("testByAttributeRegexCombined", testByAttributeRegexCombined),
|
||||
("testCombinedWithContains", testCombinedWithContains),
|
||||
("testAllElements", testAllElements),
|
||||
("testAllWithClass", testAllWithClass),
|
||||
("testGroupOr", testGroupOr),
|
||||
("testGroupOrAttribute", testGroupOrAttribute),
|
||||
("testDescendant", testDescendant),
|
||||
("testAnd", testAnd),
|
||||
("testDeeperDescendant", testDeeperDescendant),
|
||||
("testParentChildElement", testParentChildElement),
|
||||
("testParentWithClassChild", testParentWithClassChild),
|
||||
("testParentChildStar", testParentChildStar),
|
||||
("testMultiChildDescent", testMultiChildDescent),
|
||||
("testCaseInsensitive", testCaseInsensitive),
|
||||
("testAdjacentSiblings", testAdjacentSiblings),
|
||||
("testAdjacentSiblingsWithId", testAdjacentSiblingsWithId),
|
||||
("testNotAdjacent", testNotAdjacent),
|
||||
("testMixCombinator", testMixCombinator),
|
||||
("testMixCombinatorGroup", testMixCombinatorGroup),
|
||||
("testGeneralSiblings", testGeneralSiblings),
|
||||
("testCharactersInIdAndClass", testCharactersInIdAndClass),
|
||||
("testSupportsLeadingCombinator", testSupportsLeadingCombinator),
|
||||
("testPseudoLessThan", testPseudoLessThan),
|
||||
("testPseudoGreaterThan", testPseudoGreaterThan),
|
||||
("testPseudoEquals", testPseudoEquals),
|
||||
("testPseudoBetween", testPseudoBetween),
|
||||
("testPseudoCombined", testPseudoCombined),
|
||||
("testPseudoHas", testPseudoHas),
|
||||
("testNestedHas", testNestedHas),
|
||||
("testPseudoContains", testPseudoContains),
|
||||
("testPsuedoContainsWithParentheses", testPsuedoContainsWithParentheses),
|
||||
("testContainsOwn", testContainsOwn),
|
||||
("testMatches", testMatches),
|
||||
("testMatchesOwn", testMatchesOwn),
|
||||
("testRelaxedTags", testRelaxedTags),
|
||||
("testNotParas", testNotParas),
|
||||
("testNotAll", testNotAll),
|
||||
("testNotClass", testNotClass),
|
||||
("testHandlesCommasInSelector", testHandlesCommasInSelector),
|
||||
("testSelectSupplementaryCharacter", testSelectSupplementaryCharacter),
|
||||
("testSelectClassWithSpace", testSelectClassWithSpace),
|
||||
("testSelectSameElements", testSelectSameElements),
|
||||
("testAttributeWithBrackets", testAttributeWithBrackets)
|
||||
]
|
||||
}()
|
||||
|
||||
|
|
|
@ -42,9 +42,9 @@ class StringUtilTest: XCTestCase {
|
|||
// }
|
||||
|
||||
func testJoin() {
|
||||
XCTAssertEqual("",StringUtil.join([""], sep: " "))
|
||||
XCTAssertEqual("one",StringUtil.join(["one"], sep: " "))
|
||||
XCTAssertEqual("one two three",StringUtil.join(["one", "two", "three"], sep: " "))
|
||||
XCTAssertEqual("", StringUtil.join([""], sep: " "))
|
||||
XCTAssertEqual("one", StringUtil.join(["one"], sep: " "))
|
||||
XCTAssertEqual("one two three", StringUtil.join(["one", "two", "three"], sep: " "))
|
||||
}
|
||||
|
||||
func testPadding() {
|
||||
|
@ -75,7 +75,6 @@ class StringUtilTest: XCTestCase {
|
|||
XCTAssertTrue(StringUtil.isNumeric("1234"))
|
||||
}
|
||||
|
||||
|
||||
func testIsWhitespace() {
|
||||
XCTAssertTrue(StringUtil.isWhitespace("\t"))
|
||||
XCTAssertTrue(StringUtil.isWhitespace("\n"))
|
||||
|
|
|
@ -72,15 +72,15 @@ class TagTest: XCTestCase {
|
|||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testIsCaseSensitive" , testIsCaseSensitive),
|
||||
("testCanBeInsensitive" , testCanBeInsensitive),
|
||||
("testTrims" , testTrims),
|
||||
("testEquality" , testEquality),
|
||||
("testDivSemantics" , testDivSemantics),
|
||||
("testPSemantics" , testPSemantics),
|
||||
("testImgSemantics" , testImgSemantics),
|
||||
("testDefaultSemantics" , testDefaultSemantics),
|
||||
("testValueOfChecksNotEmpty" , testValueOfChecksNotEmpty),
|
||||
("testIsCaseSensitive", testIsCaseSensitive),
|
||||
("testCanBeInsensitive", testCanBeInsensitive),
|
||||
("testTrims", testTrims),
|
||||
("testEquality", testEquality),
|
||||
("testDivSemantics", testDivSemantics),
|
||||
("testPSemantics", testPSemantics),
|
||||
("testImgSemantics", testImgSemantics),
|
||||
("testDefaultSemantics", testDefaultSemantics),
|
||||
("testValueOfChecksNotEmpty", testValueOfChecksNotEmpty),
|
||||
]
|
||||
}()
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ class TextNodeTest: XCTestCase {
|
|||
|
||||
func testTextBean()throws {
|
||||
let doc = try SwiftSoup.parse("<p>One <span>two &</span> three &</p>")
|
||||
let p : Element = try doc.select("p").first()!
|
||||
let p: Element = try doc.select("p").first()!
|
||||
|
||||
let span: Element = try doc.select("span").first()!
|
||||
XCTAssertEqual("two &", try span.text())
|
||||
|
@ -67,7 +67,7 @@ class TextNodeTest: XCTestCase {
|
|||
XCTAssertEqual("Hello <b>there</b>", TextUtil.stripNewlines(try div.html())) // not great that we get \n<b>there there... must correct
|
||||
}
|
||||
|
||||
func testWithSupplementaryCharacter()throws{
|
||||
func testWithSupplementaryCharacter()throws {
|
||||
#if !os(Linux)
|
||||
let doc: Document = try SwiftSoup.parse(String(Character(UnicodeScalar(135361)!)))
|
||||
let t: TextNode = doc.body()!.textNodes()[0]
|
||||
|
@ -77,12 +77,11 @@ class TextNodeTest: XCTestCase {
|
|||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testBlank" , testBlank),
|
||||
("testTextBean" , testTextBean),
|
||||
("testSplitText" , testSplitText),
|
||||
("testSplitAnEmbolden" , testSplitAnEmbolden),
|
||||
("testWithSupplementaryCharacter" , testWithSupplementaryCharacter)
|
||||
("testBlank", testBlank),
|
||||
("testTextBean", testTextBean),
|
||||
("testSplitText", testSplitText),
|
||||
("testSplitAnEmbolden", testSplitAnEmbolden),
|
||||
("testWithSupplementaryCharacter", testWithSupplementaryCharacter)
|
||||
]
|
||||
}()
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import Foundation
|
|||
@testable import SwiftSoup
|
||||
|
||||
class TextUtil {
|
||||
public static func stripNewlines(_ text: String)->String {
|
||||
public static func stripNewlines(_ text: String) -> String {
|
||||
let regex = try! NCRegularExpression(pattern: "\\n\\s*", options: .caseInsensitive)
|
||||
var str = text
|
||||
str = regex.stringByReplacingMatches(in: str, options: [], range: NSRange(0..<str.utf16.count), withTemplate: "")
|
||||
|
|
|
@ -65,12 +65,12 @@ class TokenQueueTest: XCTestCase {
|
|||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testChompBalanced" , testChompBalanced),
|
||||
("testChompEscapedBalanced" , testChompEscapedBalanced),
|
||||
("testChompBalancedMatchesAsMuchAsPossible" , testChompBalancedMatchesAsMuchAsPossible),
|
||||
("testUnescape" , testUnescape),
|
||||
("testChompToIgnoreCase" , testChompToIgnoreCase),
|
||||
("testAddFirst" , testAddFirst)
|
||||
("testChompBalanced", testChompBalanced),
|
||||
("testChompEscapedBalanced", testChompEscapedBalanced),
|
||||
("testChompBalancedMatchesAsMuchAsPossible", testChompBalancedMatchesAsMuchAsPossible),
|
||||
("testUnescape", testUnescape),
|
||||
("testChompToIgnoreCase", testChompToIgnoreCase),
|
||||
("testAddFirst", testAddFirst)
|
||||
]
|
||||
}()
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ class XmlTreeBuilderTest: XCTestCase {
|
|||
let xml = "<doc id=2 href='/bar'>Foo <br /><link>One</link><link>Two</link></doc>"
|
||||
let tb: XmlTreeBuilder = XmlTreeBuilder()
|
||||
let doc: Document = try tb.parse(xml, "http://foo.com/")
|
||||
XCTAssertEqual("<doc id=\"2\" href=\"/bar\">Foo <br /><link>One</link><link>Two</link></doc>",try TextUtil.stripNewlines(doc.html()))
|
||||
XCTAssertEqual("<doc id=\"2\" href=\"/bar\">Foo <br /><link>One</link><link>Two</link></doc>", try TextUtil.stripNewlines(doc.html()))
|
||||
XCTAssertEqual(try doc.getElementById("2")?.absUrl("href"), "http://foo.com/bar")
|
||||
}
|
||||
|
||||
|
@ -24,20 +24,20 @@ class XmlTreeBuilderTest: XCTestCase {
|
|||
let xml = "<doc><val>One<val>Two</val></bar>Three</doc>"
|
||||
let tb: XmlTreeBuilder = XmlTreeBuilder()
|
||||
let doc = try tb.parse(xml, "http://foo.com/")
|
||||
XCTAssertEqual("<doc><val>One<val>Two</val>Three</val></doc>",try TextUtil.stripNewlines(doc.html()))
|
||||
XCTAssertEqual("<doc><val>One<val>Two</val>Three</val></doc>", try TextUtil.stripNewlines(doc.html()))
|
||||
}
|
||||
|
||||
func testCommentAndDocType()throws {
|
||||
let xml = "<!DOCTYPE HTML><!-- a comment -->One <qux />Two"
|
||||
let tb: XmlTreeBuilder = XmlTreeBuilder()
|
||||
let doc = try tb.parse(xml, "http://foo.com/")
|
||||
XCTAssertEqual("<!DOCTYPE HTML><!-- a comment -->One <qux />Two",try TextUtil.stripNewlines(doc.html()))
|
||||
XCTAssertEqual("<!DOCTYPE HTML><!-- a comment -->One <qux />Two", try TextUtil.stripNewlines(doc.html()))
|
||||
}
|
||||
|
||||
func testSupplyParserToJsoupClass()throws {
|
||||
let xml = "<doc><val>One<val>Two</val></bar>Three</doc>"
|
||||
let doc = try SwiftSoup.parse(xml, "http://foo.com/", Parser.xmlParser())
|
||||
try XCTAssertEqual("<doc><val>One<val>Two</val>Three</val></doc>",TextUtil.stripNewlines(doc.html()))
|
||||
try XCTAssertEqual("<doc><val>One<val>Two</val>Three</val></doc>", TextUtil.stripNewlines(doc.html()))
|
||||
}
|
||||
|
||||
//TODO: nabil
|
||||
|
@ -69,20 +69,19 @@ class XmlTreeBuilderTest: XCTestCase {
|
|||
// TextUtil.stripNewlines(doc.html()));
|
||||
// }
|
||||
|
||||
|
||||
func testDoesNotForceSelfClosingKnownTags()throws {
|
||||
// html will force "<br>one</br>" to logically "<br />One<br />". XML should be stay "<br>one</br> -- don't recognise tag.
|
||||
let htmlDoc = try SwiftSoup.parse("<br>one</br>")
|
||||
XCTAssertEqual("<br>one\n<br>", try htmlDoc.body()?.html())
|
||||
|
||||
let xmlDoc = try SwiftSoup.parse("<br>one</br>", "",Parser.xmlParser())
|
||||
let xmlDoc = try SwiftSoup.parse("<br>one</br>", "", Parser.xmlParser())
|
||||
XCTAssertEqual("<br>one</br>", try xmlDoc.html())
|
||||
}
|
||||
|
||||
func testHandlesXmlDeclarationAsDeclaration()throws {
|
||||
let html = "<?xml encoding='UTF-8' ?><body>One</body><!-- comment -->"
|
||||
let doc = try SwiftSoup.parse(html, "", Parser.xmlParser())
|
||||
try XCTAssertEqual("<?xml encoding=\"UTF-8\"?> <body> One </body> <!-- comment -->",StringUtil.normaliseWhitespace(doc.outerHtml()))
|
||||
try XCTAssertEqual("<?xml encoding=\"UTF-8\"?> <body> One </body> <!-- comment -->", StringUtil.normaliseWhitespace(doc.outerHtml()))
|
||||
XCTAssertEqual("#declaration", doc.childNode(0).nodeName())
|
||||
XCTAssertEqual("#comment", doc.childNode(2).nodeName())
|
||||
}
|
||||
|
@ -159,21 +158,21 @@ class XmlTreeBuilderTest: XCTestCase {
|
|||
|
||||
static var allTests = {
|
||||
return [
|
||||
("testSimpleXmlParse" , testSimpleXmlParse),
|
||||
("testPopToClose" , testPopToClose),
|
||||
("testCommentAndDocType" , testCommentAndDocType),
|
||||
("testSupplyParserToJsoupClass" , testSupplyParserToJsoupClass),
|
||||
("testDoesNotForceSelfClosingKnownTags" , testDoesNotForceSelfClosingKnownTags),
|
||||
("testSimpleXmlParse", testSimpleXmlParse),
|
||||
("testPopToClose", testPopToClose),
|
||||
("testCommentAndDocType", testCommentAndDocType),
|
||||
("testSupplyParserToJsoupClass", testSupplyParserToJsoupClass),
|
||||
("testDoesNotForceSelfClosingKnownTags", testDoesNotForceSelfClosingKnownTags),
|
||||
|
||||
("testHandlesXmlDeclarationAsDeclaration" , testHandlesXmlDeclarationAsDeclaration),
|
||||
("testXmlFragment" , testXmlFragment),
|
||||
("testXmlParseDefaultsToHtmlOutputSyntax" , testXmlParseDefaultsToHtmlOutputSyntax),
|
||||
("testDoesHandleEOFInTag" , testDoesHandleEOFInTag),
|
||||
("testParseDeclarationAttributes" , testParseDeclarationAttributes),
|
||||
("testCaseSensitiveDeclaration" , testCaseSensitiveDeclaration),
|
||||
("testCreatesValidProlog" , testCreatesValidProlog),
|
||||
("preservesCaseByDefault" , preservesCaseByDefault),
|
||||
("canNormalizeCase" , canNormalizeCase)
|
||||
("testHandlesXmlDeclarationAsDeclaration", testHandlesXmlDeclarationAsDeclaration),
|
||||
("testXmlFragment", testXmlFragment),
|
||||
("testXmlParseDefaultsToHtmlOutputSyntax", testXmlParseDefaultsToHtmlOutputSyntax),
|
||||
("testDoesHandleEOFInTag", testDoesHandleEOFInTag),
|
||||
("testParseDeclarationAttributes", testParseDeclarationAttributes),
|
||||
("testCaseSensitiveDeclaration", testCaseSensitiveDeclaration),
|
||||
("testCreatesValidProlog", testCreatesValidProlog),
|
||||
("preservesCaseByDefault", preservesCaseByDefault),
|
||||
("canNormalizeCase", canNormalizeCase)
|
||||
]
|
||||
}()
|
||||
|
||||
|
|
Loading…
Reference in New Issue