about summary refs log tree commit diff stats
path: root/src/html
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2024-01-12 00:25:42 +0100
committerbptato <nincsnevem662@gmail.com>2024-01-12 00:31:38 +0100
commit1915e6d25e45a135caaacec1bc9a603510adfad1 (patch)
tree9512858a73c614a8b789e5845bb9f934667418e9 /src/html
parent9f26d1b8215cf039b38735d785753dda035103b7 (diff)
downloadchawan-1915e6d25e45a135caaacec1bc9a603510adfad1.tar.gz
dom: standard-compliant innerHTML/outerHTML
It's still not perfect, but at least now we do not apply non-standard
whitespace changes.

The stringifier is left as it is since it's more useful for debugging
this way.
Diffstat (limited to 'src/html')
-rw-r--r--src/html/dom.nim72
-rw-r--r--src/html/enums.nim6
2 files changed, 73 insertions, 5 deletions
diff --git a/src/html/dom.nim b/src/html/dom.nim
index 89716be5..aa8fb982 100644
--- a/src/html/dom.nim
+++ b/src/html/dom.nim
@@ -1828,13 +1828,75 @@ func attrb*(element: Element, s: string): bool =
     return true
   return false
 
+# https://html.spec.whatwg.org/multipage/parsing.html#serialising-html-fragments
+func serializesAsVoid(element: Element): bool =
+  const Extra = {TAG_BASEFONT, TAG_BGSOUND, TAG_FRAME, TAG_KEYGEN, TAG_PARAM}
+  return element.tagType in VoidElements + Extra
+
+func serializeFragment(node: Node): string
+
+func serializeFragmentInner(child: Node, parentType: TagType): string =
+  result = ""
+  if child of Element:
+    let element = Element(child)
+    result &= '<'
+    #TODO qualified name if not HTML, SVG or MathML
+    result &= element.localName
+    #TODO custom elements
+    for k, v in element.attrs:
+      #TODO namespaced attrs
+      result &= ' ' & k & "=\"" & v.escapeText(true) & "\""
+    result &= '>'
+    result &= element.serializeFragment()
+    result &= "</"
+    result &= element.localName
+    result &= '>'
+  elif child of Text:
+    let text = Text(child)
+    const LiteralTags = {
+      TAG_STYLE, TAG_SCRIPT, TAG_XMP, TAG_IFRAME, TAG_NOEMBED, TAG_NOFRAMES,
+      TAG_PLAINTEXT, TAG_NOSCRIPT
+    }
+    result = if parentType in LiteralTags:
+      text.data
+    else:
+      text.data.escapeText()
+  elif child of Comment:
+    result &= "<!--" & Comment(child).data & "-->"
+  elif child of ProcessingInstruction:
+    let inst = ProcessingInstruction(child)
+    result &= "<?" & inst.target & " " & inst.data & '>'
+  elif child of DocumentType:
+    result &= "<!DOCTYPE " & DocumentType(child).name & '>'
+
+func serializeFragment(node: Node): string =
+  var node = node
+  var parentType = TAG_UNKNOWN
+  if node of Element:
+    let element = Element(node)
+    if element.serializesAsVoid():
+      return ""
+    if element of HTMLTemplateElement:
+      node = HTMLTemplateElement(element).content
+    else:
+      parentType = element.tagType
+      if parentType == TAG_NOSCRIPT and not element.scriptingEnabled:
+        # Pretend parentType is not noscript, so we do not append literally
+        # in serializeFragmentInner.
+        parentType = TAG_UNKNOWN
+  var s = ""
+  for child in node.childList:
+    s &= child.serializeFragmentInner(parentType)
+  return s
+
 # Element attribute reflection (getters)
-func innerHTML*(element: Element): string {.jsfget.} =
-  for child in element.childList:
-    result &= $child
+func innerHTML(element: Element): string {.jsfget.} =
+  #TODO xml
+  return element.serializeFragment()
 
-func outerHTML*(element: Element): string {.jsfget.} =
-  return $element
+func outerHTML(element: Element): string {.jsfget.} =
+  #TODO xml
+  return element.serializeFragmentInner(TAG_UNKNOWN)
 
 func crossOrigin0(element: HTMLElement): CORSAttribute =
   if not element.attrb("crossorigin"):
diff --git a/src/html/enums.nim b/src/html/enums.nim
index e990495e..24f496bb 100644
--- a/src/html/enums.nim
+++ b/src/html/enums.nim
@@ -34,6 +34,12 @@ const LabelableElements* = {
   TAG_BUTTON, TAG_INPUT, TAG_METER, TAG_OUTPUT, TAG_PROGRESS, TAG_SELECT, TAG_TEXTAREA
 }
 
+# https://html.spec.whatwg.org/multipage/syntax.html#void-elements
+const VoidElements* = {
+  TAG_AREA, TAG_BASE, TAG_BR, TAG_COL, TAG_EMBED, TAG_HR, TAG_IMG, TAG_INPUT,
+  TAG_LINK, TAG_META, TAG_SOURCE, TAG_TRACK, TAG_WBR
+}
+
 func getInputTypeMap(): Table[string, InputType] =
   for i in InputType:
     let enumname = $InputType(i)