about summary refs log tree commit diff stats
path: root/src/css/select.nim
diff options
context:
space:
mode:
Diffstat (limited to 'src/css/select.nim')
-rw-r--r--src/css/select.nim223
1 files changed, 133 insertions, 90 deletions
diff --git a/src/css/select.nim b/src/css/select.nim
index b43604c4..f1140d1e 100644
--- a/src/css/select.nim
+++ b/src/css/select.nim
@@ -1,12 +1,9 @@
-import unicode
 import tables
 import strutils
-import sequtils
-import sugar
-import streams
 
-import css/selectorparser
 import css/cssparser
+import css/selectorparser
+import css/stylednode
 import html/dom
 import html/tags
 
@@ -14,7 +11,7 @@ func attrSelectorMatches(elem: Element, sel: Selector): bool =
   case sel.rel
   of ' ': return sel.attr in elem.attributes
   of '=': return elem.attr(sel.attr) == sel.value
-  of '~': return sel.value in unicode.split(elem.attr(sel.attr))
+  of '~': return sel.value in elem.attr(sel.attr).split(Whitespace)
   of '|':
     let val = elem.attr(sel.attr)
     return val == sel.value or sel.value.startsWith(val & '-')
@@ -23,12 +20,17 @@ func attrSelectorMatches(elem: Element, sel: Selector): bool =
   of '*': return elem.attr(sel.attr).contains(sel.value)
   else: return false
 
-func pseudoSelectorMatches(elem: Element, sel: Selector): bool =
+func pseudoSelectorMatches[T: Element|StyledNode](elem: T, sel: Selector, felem: T): bool =
+  let selem = elem
+  when elem is StyledNode:
+    let elem = Element(elem.node)
   case sel.pseudo
   of PSEUDO_FIRST_CHILD: return elem.parentNode.firstElementChild == elem
   of PSEUDO_LAST_CHILD: return elem.parentNode.lastElementChild == elem
   of PSEUDO_ONLY_CHILD: return elem.parentNode.firstElementChild == elem and elem.parentNode.lastElementChild == elem
-  of PSEUDO_HOVER: return elem.hover
+  of PSEUDO_HOVER:
+    when selem is StyledNode: felem.depends.nodes[DEPEND_HOVER].add(selem)
+    return elem.hover
   of PSEUDO_ROOT: return elem == elem.document.html
   of PSEUDO_NTH_CHILD:
     let n = int64(sel.pseudonum - 1)
@@ -39,68 +41,101 @@ func pseudoSelectorMatches(elem: Element, sel: Selector): bool =
       inc i
     return false
   of PSEUDO_CHECKED:
+    when selem is StyledNode: felem.depends.nodes[DEPEND_CHECKED].add(selem)
     if elem.tagType == TAG_INPUT:
       return HTMLInputElement(elem).checked
     elif elem.tagType == TAG_OPTION:
       return HTMLOptionElement(elem).selected
     return false
 
-func selectorsMatch*(elem: Element, selectors: SelectorList): bool
+func selectorsMatch*[T: Element|StyledNode](elem: T, selectors: SelectorList, felem: T = nil): bool
 
-func funcSelectorMatches(elem: Element, sel: Selector): bool =
+func funcSelectorMatches[T: Element|StyledNode](elem: T, sel: Selector, felem: T): bool =
   case sel.name
   of "not":
     for slist in sel.fsels:
-      if elem.selectorsMatch(slist):
+      if elem.selectorsMatch(slist, felem):
         return false
     return true
   of "is", "where":
     for slist in sel.fsels:
-      if elem.selectorsMatch(slist):
+      if elem.selectorsMatch(slist, felem):
         return true
     return false
   else: discard
 
-func combinatorSelectorMatches(elem: Element, sel: Selector): bool =
+func combinatorSelectorMatches[T: Element|StyledNode](elem: T, sel: Selector, felem: T): bool =
+  let selem = elem
   #combinator without at least two members makes no sense
   assert sel.csels.len > 1
-  if elem.selectorsMatch(sel.csels[^1]):
+  if selem.selectorsMatch(sel.csels[^1], felem):
     var i = sel.csels.len - 2
     case sel.ct
     of DESCENDANT_COMBINATOR:
-      var e = elem.parentElement
+      when selem is StyledNode:
+        var e = elem.parent
+      else:
+        var e = elem.parentElement
       while e != nil and i >= 0:
-        if e.selectorsMatch(sel.csels[i]):
+        if e.selectorsMatch(sel.csels[i], felem):
           dec i
-        e = e.parentElement
+        when elem is StyledNode:
+          e = e.parent
+        else:
+          e = e.parentElement
     of CHILD_COMBINATOR:
-      var e = elem.parentElement
+      when elem is StyledNode:
+        var e = elem.parent
+      else:
+        var e = elem.parentElement
       while e != nil and i >= 0:
-        if not e.selectorsMatch(sel.csels[i]):
+        if not e.selectorsMatch(sel.csels[i], felem):
           return false
         dec i
-        e = e.parentElement
+        when elem is StyledNode:
+          e = e.parent
+        else:
+          e = e.parentElement
     of NEXT_SIBLING_COMBINATOR:
       var found = false
-      for child in elem.parentElement.children_rev:
+      when elem is StyledNode:
+        var parent = elem.parent
+      else:
+        var parent = elem.parentElement
+      for child in parent.children_rev:
+        when elem is StyledNode:
+          if child.t != STYLED_ELEMENT or child.node == nil: continue
         if found:
-          if not child.selectorsMatch(sel.csels[i]):
+          if not child.selectorsMatch(sel.csels[i], felem):
             return false
           dec i
+          if i < 0:
+            return true
         if child == elem:
           found = true
     of SUBSEQ_SIBLING_COMBINATOR:
       var found = false
-      for child in elem.parentElement.children_rev:
+      when selem is StyledNode:
+        var parent = selem.parent
+      else:
+        var parent = elem.parentElement
+      for child in parent.children_rev:
+        when selem is StyledNode:
+          if child.t != STYLED_ELEMENT or child.node == nil: continue
         if found:
-          if child.selectorsMatch(sel.csels[i]):
+          if child.selectorsMatch(sel.csels[i], felem):
             dec i
-        if child == elem:
+          if i < 0:
+            return true
+        if child == selem:
           found = true
     return i == -1
   return false
 
-func selectorMatches(elem: Element, sel: Selector): bool =
+func selectorMatches[T: Element|StyledNode](elem: T, sel: Selector, felem: T): bool =
+  let selem = elem
+  when elem is StyledNode:
+    let elem = Element(selem.node)
   case sel.t
   of TYPE_SELECTOR:
     return elem.tagType == sel.tag
@@ -111,78 +146,86 @@ func selectorMatches(elem: Element, sel: Selector): bool =
   of ATTR_SELECTOR:
     return elem.attrSelectorMatches(sel)
   of PSEUDO_SELECTOR:
-    return pseudoSelectorMatches(elem, sel)
+    return pseudoSelectorMatches(selem, sel, felem)
   of PSELEM_SELECTOR:
     return true
   of UNIVERSAL_SELECTOR:
     return true
   of FUNC_SELECTOR:
-    return funcSelectorMatches(elem, sel)
+    return funcSelectorMatches(selem, sel, felem)
   of COMBINATOR_SELECTOR:
-    return combinatorSelectorMatches(elem, sel)
+    return combinatorSelectorMatches(selem, sel, felem)
+
+# WARNING for StyledNode, this has the side effect of modifying depends.
+#TODO make that an explicit flag or something, also get rid of the Element case
+func selectorsMatch*[T: Element|StyledNode](elem: T, selectors: SelectorList, felem: T = nil): bool =
+  let felem = if felem != nil:
+    felem
+  else:
+    elem
 
-func selectorsMatch*(elem: Element, selectors: SelectorList): bool =
   for sel in selectors.sels:
-    if not selectorMatches(elem, sel):
+    if not selectorMatches(elem, sel, felem):
       return false
   return true
 
-func selectElems(element: Element, sel: Selector): seq[Element] =
-  case sel.t
-  of TYPE_SELECTOR:
-    return element.filterDescendants((elem) => elem.tagType == sel.tag)
-  of ID_SELECTOR:
-    return element.filterDescendants((elem) => elem.id == sel.id)
-  of CLASS_SELECTOR:
-    return element.filterDescendants((elem) => sel.class in elem.classList)
-  of UNIVERSAL_SELECTOR:
-    return element.all_descendants
-  of ATTR_SELECTOR:
-    return element.filterDescendants((elem) => attrSelectorMatches(elem, sel))
-  of PSEUDO_SELECTOR:
-    return element.filterDescendants((elem) => pseudoSelectorMatches(elem, sel))
-  of PSELEM_SELECTOR:
-    return element.all_descendants
-  of FUNC_SELECTOR:
-    return element.filterDescendants((elem) => selectorMatches(elem, sel))
-  of COMBINATOR_SELECTOR:
-    return element.filterDescendants((elem) => selectorMatches(elem, sel))
-
-func selectElems(element: Element, selectors: SelectorList): seq[Element] =
-  assert(selectors.len > 0)
-  let sellist = optimizeSelectorList(selectors)
-  result = element.selectElems(selectors[0])
-  var i = 1
-
-  while i < sellist.len:
-    result = result.filter((elem) => selectorMatches(elem, sellist[i]))
-    inc i
-
-proc querySelectorAll*(document: Document, q: string): seq[Element] =
-  let ss = newStringStream(q)
-  let cvals = parseListOfComponentValues(ss)
-  let selectors = parseSelectors(cvals)
-
-  if document.html != nil:
-    for sel in selectors:
-      result.add(document.html.selectElems(sel))
-
-proc querySelector*(document: Document, q: string): Element =
-  let elems = document.querySelectorAll(q)
-  if elems.len > 0:
-    return elems[0]
-  return nil
-
-proc querySelectorAll*(element: Element, q: string): seq[Element] =
-  let ss = newStringStream(q)
-  let cvals = parseListOfComponentValues(ss)
-  let selectors = parseSelectors(cvals)
-
-  for sel in selectors:
-    result.add(element.selectElems(sel))
-
-proc querySelector*(element: Element, q: string): Element =
-  let elems = element.querySelectorAll(q)
-  if elems.len > 0:
-    return elems[0]
-  return nil
+#TODO idk, it's not like we have JS anyways
+#func selectElems[T: Element|StyledNode](element: T, sel: Selector, felem: T): seq[T] =
+#  case sel.t
+#  of TYPE_SELECTOR:
+#    return element.filterDescendants((elem) => elem.tagType == sel.tag)
+#  of ID_SELECTOR:
+#    return element.filterDescendants((elem) => elem.id == sel.id)
+#  of CLASS_SELECTOR:
+#    return element.filterDescendants((elem) => sel.class in elem.classList)
+#  of UNIVERSAL_SELECTOR:
+#    return element.all_descendants
+#  of ATTR_SELECTOR:
+#    return element.filterDescendants((elem) => attrSelectorMatches(elem, sel))
+#  of PSEUDO_SELECTOR:
+#    return element.filterDescendants((elem) => pseudoSelectorMatches(elem, sel, felem))
+#  of PSELEM_SELECTOR:
+#    return element.all_descendants
+#  of FUNC_SELECTOR:
+#    return element.filterDescendants((elem) => selectorMatches(elem, sel))
+#  of COMBINATOR_SELECTOR:
+#    return element.filterDescendants((elem) => selectorMatches(elem, sel))
+#
+#func selectElems(element: Element, selectors: SelectorList): seq[Element] =
+#  assert(selectors.len > 0)
+#  let sellist = optimizeSelectorList(selectors)
+#  result = element.selectElems(selectors[0], element)
+#  var i = 1
+#
+#  while i < sellist.len:
+#    result = result.filter((elem) => selectorMatches(elem, sellist[i], elem))
+#    inc i
+#
+#proc querySelectorAll*(document: Document, q: string): seq[Element] =
+#  let ss = newStringStream(q)
+#  let cvals = parseListOfComponentValues(ss)
+#  let selectors = parseSelectors(cvals)
+#
+#  if document.html != nil:
+#    for sel in selectors:
+#      result.add(document.html.selectElems(sel))
+#
+#proc querySelector*(document: Document, q: string): Element =
+#  let elems = document.querySelectorAll(q)
+#  if elems.len > 0:
+#    return elems[0]
+#  return nil
+#
+#proc querySelectorAll*(element: Element, q: string): seq[Element] =
+#  let ss = newStringStream(q)
+#  let cvals = parseListOfComponentValues(ss)
+#  let selectors = parseSelectors(cvals)
+#
+#  for sel in selectors:
+#    result.add(element.selectElems(sel))
+#
+#proc querySelector*(element: Element, q: string): Element =
+#  let elems = element.querySelectorAll(q)
+#  if elems.len > 0:
+#    return elems[0]
+#  return nil