PureXML/Sources/HTML/HTMLFosterParenting.swift

48 lines
1.9 KiB
Swift

extension HTMLDocument {
/// The elements the table insertion modes place directly (so they are never
/// foster-parented out of a table).
private static let tableScope: Set<String> = [
"table", "caption", "colgroup", "col", "tbody", "thead", "tfoot", "tr", "td", "th",
]
/// The table-structural elements that, when current, mean a stray non-table
/// node must be foster-parented rather than nested.
private static let tableContexts: Set<String> = ["table", "tbody", "thead", "tfoot", "tr"]
/// Inserts an opened element, foster-parenting it before the table when it is a
/// non-table element appearing in table context (the HTML5 rule for stray
/// flow content inside a table).
func placeElement(_ element: PureXML.Model.TreeNode, name: String) {
if !Self.tableScope.contains(name), inTableContext() {
fosterParent(element)
} else {
openBody.last?.append(element)
}
}
/// Inserts a text node, foster-parenting it out of a table when in table
/// context (character data is not allowed directly inside table structure).
func placeText(_ node: PureXML.Model.TreeNode) {
if inTableContext() {
fosterParent(node)
} else {
openBody.last?.append(node)
}
}
private func inTableContext() -> Bool {
guard let current = openBody.last else { return false }
return Self.tableContexts.contains(tagName(current))
}
/// Inserts `node` immediately before the nearest open table in that table's
/// parent; if the table has no parent yet, falls back to ordinary insertion.
private func fosterParent(_ node: PureXML.Model.TreeNode) {
guard let table = openBody.last(where: { tagName($0) == "table" }), let parent = table.parent else {
openBody.last?.append(node)
return
}
parent.insert(node, before: table)
}
}