about summary refs log tree commit diff stats
path: root/src/html/dom.nim
diff options
context:
space:
mode:
Diffstat (limited to 'src/html/dom.nim')
-rw-r--r--src/html/dom.nim128
1 files changed, 76 insertions, 52 deletions
diff --git a/src/html/dom.nim b/src/html/dom.nim
index 74ebe5ea..1a393134 100644
--- a/src/html/dom.nim
+++ b/src/html/dom.nim
@@ -6,13 +6,13 @@ import tables
 import streams
 import sequtils
 import sugar
+import algorithm
 
 import ../css/style
 import ../css/cssparser
 import ../css/selector
 
 import ../types/enums
-import ../types/tagtypes
 
 import ../utils/twtstr
 
@@ -46,6 +46,7 @@ type
     width*: int
     height*: int
     hidden*: bool
+    box*: CSSBox
 
   Attr* = ref AttrObj
   AttrObj = object of NodeObj
@@ -89,7 +90,8 @@ type
     id*: string
     classList*: seq[string]
     attributes*: Table[string, Attr]
-    box*: CSSBox
+    style*: CSS2Properties
+    cssvalues*: seq[CSSComputedValue]
 
   HTMLElement* = ref HTMLElementObj
   HTMLElementObj = object of ElementObj
@@ -159,8 +161,8 @@ func nodeAttr*(node: Node): HtmlElement =
 
 func getStyle*(node: Node): CSS2Properties =
   case node.nodeType
-  of TEXT_NODE: return node.parentElement.box.props
-  of ELEMENT_NODE: return Element(node).box.props
+  of TEXT_NODE: return node.parentElement.style
+  of ELEMENT_NODE: return Element(node).style
   else: assert(false)
 
 func displayed*(node: Node): bool =
@@ -260,11 +262,11 @@ proc getRawText*(htmlNode: Node): string =
     #eprint "char data", chardata.data
     if htmlNode.parentElement != nil and htmlNode.parentElement.tagType != TAG_PRE:
       result = chardata.data.remove("\n")
-      if unicode.strip(result).runeLen() > 0:
-        if htmlNode.getStyle().display != DISPLAY_INLINE:
-          result = unicode.strip(result)
-      else:
-        result = ""
+      #if unicode.strip(result).runeLen() > 0:
+      #  if htmlNode.getStyle().display != DISPLAY_INLINE:
+      #    result = unicode.strip(result)
+      #else:
+      #  result = ""
     else:
       result = unicode.strip(chardata.data)
     if htmlNode.parentElement != nil and htmlNode.parentElement.tagType == TAG_OPTION:
@@ -290,7 +292,7 @@ func getFmtText*(node: Node): seq[string] =
 
       if style.bold:
         result = result.ansiStyle(styleBright).ansiReset()
-      if style.italic:
+      if style.fontStyle == FONTSTYLE_ITALIC or style.fontStyle == FONTSTYLE_OBLIQUE:
         result = result.ansiStyle(styleItalic).ansiReset()
       if style.underscore:
         result = result.ansiStyle(styleUnderscore).ansiReset()
@@ -307,10 +309,6 @@ func newComment*(): Comment =
   new(result)
   result.nodeType = COMMENT_NODE
 
-func newBox*(element: HTMLElement): CSSBox =
-  new(result)
-  result.props = CSS2Properties()
-
 func newHtmlElement*(tagType: TagType): HTMLElement =
   case tagType
   of TAG_INPUT:
@@ -332,7 +330,7 @@ func newHtmlElement*(tagType: TagType): HTMLElement =
 
   result.nodeType = ELEMENT_NODE
   result.tagType = tagType
-  result.box = result.newBox()
+  result.style = CSS2Properties()
 
 func newDocument*(): Document =
   new(result)
@@ -414,7 +412,6 @@ func selectElems(document: Document, sel: Selector): seq[Element] =
     return document.class_elements[sel.class]
   of UNIVERSAL_SELECTOR:
     return document.all_elements
-  #TODO: following selectors are rather inefficient
   of ATTR_SELECTOR:
     return document.all_elements.filter((elem) => attrSelectorMatches(elem, sel))
   of PSEUDO_SELECTOR:
@@ -422,38 +419,13 @@ func selectElems(document: Document, sel: Selector): seq[Element] =
   of PSELEM_SELECTOR:
     return document.all_elements.filter((elem) => pseudoElemSelectorMatches(elem, sel))
   of FUNC_SELECTOR:
-    if sel.name == "not":
+    case sel.name
+    of "not":
       return document.all_elements.filter((elem) => not selectorsMatch(elem, sel.selectors))
+    of "is", "where":
+      return document.all_elements.filter((elem) => selectorsMatch(elem, sel.selectors))
     return newSeq[Element]()
 
-func optimizeSelectorList(selectors: SelectorList): SelectorList =
-  new(result)
-  #pass 1: check for invalid sequences
-  var i = 1
-  while i < selectors.len:
-    let sel = selectors[i]
-    if sel.t == TYPE_SELECTOR or sel.t == UNIVERSAL_SELECTOR:
-      return SelectorList()
-    inc i
-
-  #pass 2: move selectors in combination
-  if selectors.len > 1:
-    var i = 0
-    var slow = SelectorList()
-    if selectors[0].t == UNIVERSAL_SELECTOR:
-      inc i
-
-    while i < selectors.len:
-      if selectors[i].t in {ATTR_SELECTOR, PSEUDO_SELECTOR, PSELEM_SELECTOR}:
-        slow.add(selectors[i])
-      else:
-        result.add(selectors[i])
-      inc i
-
-    result.add(slow)
-  else:
-    result.add(selectors[0])
-
 func selectElems(document: Document, selectors: SelectorList): seq[Element] =
   assert(selectors.len > 0)
   let sellist = optimizeSelectorList(selectors)
@@ -462,8 +434,12 @@ func selectElems(document: Document, selectors: SelectorList): seq[Element] =
 
   while i < sellist.len:
     if sellist[i].t == FUNC_SELECTOR:
-      if sellist[i].name == "not":
+      case sellist[i].name
+      of "not":
         result = result.filter((elem) => not selectorsMatch(elem, sellist[i].selectors))
+      of "is", "where":
+        result = result.filter((elem) => selectorsMatch(elem, sellist[i].selectors))
+      else: discard
     else:
       result = result.filter((elem) => selectorMatches(elem, sellist[i]))
     inc i
@@ -476,17 +452,65 @@ proc querySelector*(document: Document, q: string): seq[Element] =
   for sel in selectors:
     result.add(document.selectElems(sel))
 
-proc applyRule(elem: Element, rule: CSSRule) =
-  let selectors = parseSelectors(rule.prelude)
-  for sel in selectors:
-    if elem.selectorsMatch(sel):
-      eprint "match!"
+func calcRules(elem: Element, rules: CSSStylesheet): seq[CSSSimpleBlock] =
+  var tosort: seq[tuple[s:int,b:CSSSimpleBlock]]
+  for rule in rules.value:
+    let selectors = parseSelectors(rule.prelude) #TODO perf: compute this once
+    for sel in selectors:
+      if elem.selectorsMatch(sel):
+        let spec = getSpecificity(sel)
+        tosort.add((spec,rule.oblock))
 
-proc applyRules(document: Document, rules: CSSStylesheet) =
+  tosort.sort((x, y) => cmp(x.s,y.s))
+  return tosort.map((x) => x.b)
+
+proc applyRules*(document: Document, rules: CSSStylesheet): seq[tuple[e:Element,d:CSSDeclaration]] =
+  var stack: seq[Element]
+
+  stack.add(document.firstElementChild)
+  while stack.len > 0:
+    let elem = stack.pop()
+    for oblock in calcRules(elem, rules):
+      let decls = parseCSSListOfDeclarations(oblock.value)
+      for item in decls:
+        if item of CSSDeclaration:
+          if ((CSSDeclaration)item).important:
+            result.add((elem, (CSSDeclaration)item))
+          else:
+            elem.style.applyProperty((CSSDeclaration)item)
+
+    for child in elem.children:
+      stack.add(child)
+
+proc addBoxes*(elem: Element) =
+  var b = false
+  for child in elem.childNodes:
+    if child.nodeType == ELEMENT_NODE:
+      if ((Element)child).style.display == DISPLAY_BLOCK:
+        b = true
+        break
+  for child in elem.childNodes:
+    if child.nodeType == ELEMENT_NODE:
+      if b:
+        child.box = CSSBox(display: DISPLAY_BLOCK)
+      else:
+        child.box = CSSBox()
+
+proc generateBoxModel(document: Document) =
   var stack: seq[Element]
 
   stack.add(document.firstElementChild)
+  document.firstElementChild.box = CSSBox()
   while stack.len > 0:
     let elem = stack.pop()
+    elem.addBoxes()
     for child in elem.children:
       stack.add(child)
+
+proc applyDefaultStylesheet*(document: Document) =
+  let important = document.applyRules(stylesheet)
+  for rule in important:
+    rule.e.style.applyProperty(rule.d)
+    rule.e.cssvalues.add(getComputedValue(rule.d))
+
+  document.generateBoxModel()