about summary refs log tree commit diff stats
path: root/src/html
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2022-07-11 22:02:15 +0200
committerbptato <nincsnevem662@gmail.com>2022-07-11 22:31:43 +0200
commit1abd3aadf0c999c6e26ba4e7910b5abe3510c6c2 (patch)
tree537e938a4a5a12a69d148c83c5e279e46a73b8b4 /src/html
parent62cba694e47a7a1f4bedc7fd48ceac9c26aa3aa1 (diff)
downloadchawan-1abd3aadf0c999c6e26ba4e7910b5abe3510c6c2.tar.gz
Fix and clean up some dom-based features
Diffstat (limited to 'src/html')
-rw-r--r--src/html/dom.nim113
-rw-r--r--src/html/htmlparser.nim16
2 files changed, 77 insertions, 52 deletions
diff --git a/src/html/dom.nim b/src/html/dom.nim
index 6c63e1ad..921c8702 100644
--- a/src/html/dom.nim
+++ b/src/html/dom.nim
@@ -36,14 +36,12 @@ type
   Node* = ref object of EventTarget
     nodeType*: NodeType
     childNodes*: seq[Node]
-    isConnected*: bool
     nextSibling*: Node
     previousSibling*: Node
     parentNode*: Node
     parentElement*: Element
     rootNode: Node
     document*: Document
-    uid*: int # Unique id
 
   Attr* = ref object of Node
     namespaceURI*: string
@@ -55,9 +53,6 @@ type
 
   Document* = ref object of Node
     location*: Url
-    type_elements*: array[TagType, seq[Element]]
-    class_elements*: Table[string, seq[Element]]
-    all_elements*: seq[Element]
     mode*: QuirksMode
 
     parser_cannot_change_the_mode_flag*: bool
@@ -68,7 +63,6 @@ type
     length*: int
 
   Text* = ref object of CharacterData
-    wholeText*: string
 
   Comment* = ref object of CharacterData
 
@@ -111,7 +105,6 @@ type
     form*: HTMLFormElement
 
   HTMLAnchorElement* = ref object of HTMLElement
-    href*: string
 
   HTMLSelectElement* = ref object of HTMLElement
     name*: string
@@ -147,8 +140,7 @@ type
     sheet_invalid*: bool
 
   HTMLLinkElement* = ref object of HTMLElement
-    href*: string
-    rel*: string
+    sheet*: CSSStylesheet
 
   HTMLFormElement* = ref object of HTMLElement
     name*: string
@@ -187,9 +179,23 @@ func `$`*(node: Node): string =
   else:
     "Node of " & $node.nodeType
 
-iterator elements*(document: Document, tag: TagType): Element {.inline.} =
-  for element in document.type_elements[tag]:
-    yield element
+iterator children*(node: Node): Element {.inline.} =
+  for child in node.childNodes:
+    if child.nodeType == ELEMENT_NODE:
+      yield Element(child)
+
+iterator elements*(node: Node, tag: TagType): Element {.inline.} =
+  var stack: seq[Element]
+  for child in node.children:
+    stack.add(child)
+  while stack.len > 0:
+    let element = stack.pop()
+    if element.tagType == tag:
+      yield element
+    for i in countdown(element.childNodes.high, 0):
+      let child = element.childNodes[i]
+      if child.nodeType == ELEMENT_NODE:
+        stack.add(Element(child))
 
 iterator radiogroup(form: HTMLFormElement): HTMLInputElement {.inline.} =
   for input in form.inputs:
@@ -229,11 +235,6 @@ iterator branch*(node: Node): Node {.inline.} =
     yield node
     node = node.parentNode
 
-iterator children*(node: Node): Element {.inline.} =
-  for child in node.childNodes:
-    if child.nodeType == ELEMENT_NODE:
-      yield Element(child)
-
 func qualifiedName*(element: Element): string =
   if element.namespacePrefix.issome: element.namespacePrefix.get & ':' & element.localName
   else: element.localName
@@ -258,7 +259,6 @@ func body*(document: Document): HTMLElement =
         return HTMLElement(element)
   return nil
 
-
 func countChildren(node: Node, nodeType: NodeType): int =
   for child in node.childNodes:
     if child.nodeType == nodeType:
@@ -379,6 +379,11 @@ func textContent*(node: Node): string =
       if child.nodeType != COMMENT_NODE:
         result &= child.textContent
 
+func childTextContent*(node: Node): string =
+  for child in node.childNodes:
+    if child.nodeType == TEXT_NODE:
+      result &= Text(child).data
+
 proc sheets*(element: Element): seq[CSSStylesheet] =
   for child in element.children:
     if child.tagType == TAG_STYLE:
@@ -386,6 +391,10 @@ proc sheets*(element: Element): seq[CSSStylesheet] =
       if child.sheet_invalid:
         child.sheet = parseStylesheet(newStringStream(child.textContent))
       result.add(child.sheet)
+    elif child.tagType == TAG_LINK:
+      let child = HTMLLinkElement(child)
+      if child.sheet != nil:
+        result.add(child.sheet)
 
 func inputString*(input: HTMLInputElement): string =
   var text = case input.inputType
@@ -561,10 +570,8 @@ func newHTMLElement*(document: Document, tagType: TagType, namespace = Namespace
   result.nodeType = ELEMENT_NODE
   result.tagType = tagType
   result.css = rootProperties()
-  result.uid = document.all_elements.len
   result.rootNode = result
   result.document = document
-  document.all_elements.add(result)
 
 func newHTMLElement*(document: Document, localName: string, namespace = "", prefix = none[string](), tagType = tagType(localName)): Element =
   result = document.newHTMLElement(tagType, namespace(namespace).get(HTML))
@@ -593,7 +600,6 @@ func newAttr*(parent: Element, key, value: string): Attr =
   result.value = value
   result.rootNode = result
 
-#TODO optimize?
 func getElementById*(document: Document, id: string): Element =
   if id.len == 0:
     return nil
@@ -608,29 +614,9 @@ func getElementById*(document: Document, id: string): Element =
         stack.add(Element(child))
   return nil
 
-#TODO optimize?
 func getElementsByTag*(document: Document, tag: TagType): seq[Element] =
-  var stack = document.children
-  while stack.len > 0:
-    let element = stack.pop()
-    if element.tagType == tag:
-      result.add(element)
-    for i in countdown(element.childNodes.high, 0):
-      let child = element.childNodes[i]
-      if child.nodeType == ELEMENT_NODE:
-        stack.add(Element(child))
-
-func baseUrl*(document: Document): Url =
-  var href = ""
-  for base in document.elements(TAG_BASE):
-    if base.attr("href") != "":
-      href = base.attr("href")
-  if href == "":
-    return document.location
-  let url = parseUrl(href, document.location.some)
-  if url.isnone:
-    return document.location
-  return url.get
+  for element in document.elements(tag):
+    result.add(element)
 
 func inHTMLNamespace*(element: Element): bool = element.namespace == Namespace.HTML
 func inMathMLNamespace*(element: Element): bool = element.namespace == Namespace.MATHML
@@ -652,7 +638,36 @@ func isHostIncludingInclusiveAncestor*(a, b: Node): bool =
         return true
   return false
 
-# WARNING the ordering of the arguments in the standard is whack so this doesn't match it
+func baseUrl*(document: Document): Url =
+  var href = ""
+  for base in document.elements(TAG_BASE):
+    if base.attrb("href"):
+      href = base.attr("href")
+  if href == "":
+    return document.location
+  let url = parseUrl(href, document.location.some)
+  if url.isnone:
+    return document.location
+  return url.get
+
+func href*(element: Element): string =
+  assert element.tagType in {TAG_A, TAG_LINK, TAG_BASE}
+  if element.attrb("href"):
+    let url = parseUrl(element.attr("href"), some(element.document.location))
+    if url.issome:
+      return $url.get
+  return ""
+
+func rel*(element: Element): string =
+  assert element.tagType in {TAG_A, TAG_LINK, TAG_AREA}
+  return element.attr("rel")
+
+func title*(document: Document): string =
+  for title in document.elements(TAG_TITLE):
+    return title.childTextContent.stripAndCollapse()
+  return ""
+
+# WARNING the ordering of the arguments in the standard is whack so this doesn't match that
 func preInsertionValidity*(parent, node, before: Node): bool =
   if parent.nodeType notin {DOCUMENT_NODE, DOCUMENT_FRAGMENT_NODE, ELEMENT_NODE}:
     # HierarchyRequestError
@@ -705,6 +720,8 @@ proc remove*(node: Node) =
     oldPreviousSibling.nextSibling = oldNextSibling
   if oldNextSibling != nil:
     oldNextSibling.previousSibling = oldPreviousSibling
+  node.parentNode = nil
+  node.parentElement = nil
 
   #TODO assigned, shadow root, shadow root again, custom nodes, registered observers
   #TODO not surpress observers => queue tree mutation record
@@ -811,6 +828,14 @@ proc reset*(form: HTMLFormElement) =
     input.rendered = false
 
 proc appendAttribute*(element: Element, k, v: string) =
+  case k
+  of "id": element.id = v
+  of "class":
+    let classes = v.split(' ')
+    for class in classes:
+      if class != "" and class notin element.classList:
+        element.classList.add(class)
+  else: discard
   element.attributes[k] = v
 
 proc setForm*(element: Element, form: HTMLFormElement) =
diff --git a/src/html/htmlparser.nim b/src/html/htmlparser.nim
index 3e962495..0b2a99be 100644
--- a/src/html/htmlparser.nim
+++ b/src/html/htmlparser.nim
@@ -1172,30 +1172,30 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode =
         anything_else
       )
       "<a>" => (block:
-        var element: Element = nil
+        var anchor: Element = nil
         for i in countdown(parser.activeFormatting.high, 0):
           let format = parser.activeFormatting[i]
           if format[0] == nil:
             break
           if format[0].tagType == TAG_A:
-            element = format[0]
+            anchor = format[0]
             break
-        if element != nil:
+        if anchor != nil:
           parse_error
           if parser.adoptionAgencyAlgorithm(token):
             any_other_end_tag
             return
           for i in 0..parser.activeFormatting.high:
-            if parser.activeFormatting[i][0] == element:
+            if parser.activeFormatting[i][0] == anchor:
               parser.activeFormatting.del(i)
               break
           for i in 0..parser.openElements.high:
-            if parser.openElements[i] == element:
+            if parser.openElements[i] == anchor:
               parser.openElements.del(i)
               break
-          parser.reconstructActiveFormatting()
-          let element = parser.insertHTMLElement(token)
-          parser.pushOntoActiveFormatting(element, token)
+        parser.reconstructActiveFormatting()
+        let element = parser.insertHTMLElement(token)
+        parser.pushOntoActiveFormatting(element, token)
       )
       ("<b>", "<big>", "<code>", "<em>", "<font>", "<i>", "<s>", "<small>",
        "<strike>", "<strong>", "<tt>", "<u>") => (block: