///|
fn collapse_transform_html_space(value : StringView) -> String {
let out = StringBuilder(size_hint=value.length())
let mut in_ws = false
for ch in value {
if is_html_whitespace_char(ch) {
if !in_ws {
out.write_char(' ')
in_ws = true
}
} else {
out.write_char(ch)
in_ws = false
}
}
out.to_string()
}
///|
fn apply_collapse_whitespace_transform(
root : @dom.Node,
skip_tags : Set[String],
transform : TransformSpec,
) -> Unit {
match root.kind {
Document | Fragment =>
collapse_whitespace_children(root, skip_tags, false, transform)
Element =>
collapse_whitespace_children(
root,
skip_tags,
skip_tags.contains(@syn.lower_ascii(root.name)),
transform,
)
_ => ()
}
}
///|
/// Collapse whitespace in text below `root` (template contents first, then the
/// regular children), skipping text inside skipped elements. Uses an explicit
/// frame stack instead of recursion; each frame carries the inherited skip
/// state of one activation of the former recursive walk.
fn collapse_whitespace_children(
root : @dom.Node,
skip_tags : Set[String],
skip : Bool,
transform : TransformSpec,
) -> Unit {
let stack : Array[WalkFrame] = [WalkFrame::new(root, skip~)]
while stack.last() is Some(frame) {
if frame.schedule_template_first(stack) {
continue
}
let parent = frame.node
let index = frame.index
if index >= parent.children.length() {
ignore(stack.pop())
continue
}
let child = parent.children[index]
frame.index = index + 1
match child.kind {
Text if !frame.skip && child.data != "" => {
let collapsed = collapse_transform_html_space(child.data)
if collapsed != child.data {
transform_call_hook(child, transform)
transform_report(
transform,
"Collapsed whitespace in text node",
Some(child),
)
let replacement = @dom.text(collapsed)
replacement.parent = Some(parent)
parent.children[index] = replacement
if child.parent is Some(current_parent) &&
physical_equal(current_parent, parent) {
child.parent = None
}
}
}
Document | Fragment => stack.push(WalkFrame::new(child, skip=frame.skip))
Element => {
let child_skip = frame.skip ||
skip_tags.contains(@syn.lower_ascii(child.name))
stack.push(WalkFrame::new(child, skip=child_skip))
}
_ => ()
}
}
}