diff --git a/Sources/Element.swift b/Sources/Element.swift index 8b4c67b..63e3cd1 100644 --- a/Sources/Element.swift +++ b/Sources/Element.swift @@ -16,9 +16,6 @@ open class Element: Node { private static let idString = "id".utf8Array private static let rootString = "#root".utf8Array - //private static let classSplit : Pattern = Pattern("\\s+") - private static let classSplit = "\\s+" - /** * Create a new, standalone Element. (Standalone in that is has no parent.) * @@ -1112,6 +1109,15 @@ open class Element: Node { public func className() throws -> String { return try String(decoding: attr(Element.classString).trim(), as: UTF8.self) } + + /** + * Gets the literal value of this element's "class" attribute, which may include multiple class names, space + * separated. (E.g. on <div class="header gray"> returns, "header gray") + * @return The literal class attribute, or empty string if no class attribute set. + */ + public func classNameUTF8() throws -> [UInt8] { + return try attr(Element.classString).trim() + } /** * Get all of the element's class names. E.g. on element {@code
}, @@ -1119,13 +1125,36 @@ open class Element: Node { * the backing {@code class} attribute; use the {@link #classNames(java.util.Set)} method to persist them. * @return set of classnames, empty if no class attribute */ - public func classNames() throws -> OrderedSet { - let fitted = try className().replaceAll(of: Element.classSplit, with: " ", options: .caseInsensitive) - let names: [String] = fitted.components(separatedBy: " ") - let classNames: OrderedSet = OrderedSet(sequence: names) - classNames.remove("") // if classNames() was empty, would include an empty class - return classNames - } + public func classNames() throws -> OrderedSet { + let utf8ClassName = try classNameUTF8() + var classNames = OrderedSet() + var currentStartIndex: Int? = nil + + for (i, byte) in utf8ClassName.enumerated() { + if byte.isWhitespace { + if let start = currentStartIndex { + let classBytes = utf8ClassName[start..String { + public func html() throws -> String { let accum: StringBuilder = StringBuilder() try html2(accum) return getOutputSettings().prettyPrint() ? accum.toString().trim() : accum.toString() } - private func html2(_ accum: StringBuilder)throws { + private func html2(_ accum: StringBuilder) throws { for node in childNodes { try node.outerHtml(accum) } diff --git a/Sources/Node.swift b/Sources/Node.swift index 56e2701..bc5a785 100644 --- a/Sources/Node.swift +++ b/Sources/Node.swift @@ -533,12 +533,14 @@ open class Node: Equatable, Hashable { * Replace this node in the DOM with the supplied node. * @param in the node that will will replace the existing node. */ + @inlinable public func replaceWith(_ input: Node) throws { try Validate.notNull(obj: input) try Validate.notNull(obj: parentNode) try parentNode?.replaceChild(self, input) } + @inlinable public func setParentNode(_ parentNode: Node) throws { if (self.parentNode != nil) { try self.parentNode?.removeChild(self) @@ -546,6 +548,7 @@ open class Node: Equatable, Hashable { self.parentNode = parentNode } + @inlinable public func replaceChild(_ out: Node, _ input: Node) throws { try Validate.isTrue(val: out.parentNode === self) try Validate.notNull(obj: input) @@ -554,17 +557,16 @@ open class Node: Equatable, Hashable { } let index: Int = out.siblingIndex - let replacing = childNodes[index] childNodes[index] = input input.parentNode = self input.setSiblingIndex(index) out.parentNode = nil } + @inlinable public func removeChild(_ out: Node) throws { try Validate.isTrue(val: out.parentNode === self) let index: Int = out.siblingIndex - let removing = childNodes[index] childNodes.remove(at: index) reindexChildren(index) out.parentNode = nil @@ -586,10 +588,12 @@ open class Node: Equatable, Hashable { } } + @inlinable public func addChildren(_ index: Int, _ children: Node...) throws { try addChildren(index, children) } + @inlinable public func addChildren(_ index: Int, _ children: [Node]) throws { for i in (0..