diff options
Diffstat (limited to 'src/css/select.nim')
-rw-r--r-- | src/css/select.nim | 223 |
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 |