diff options
author | bptato <nincsnevem662@gmail.com> | 2022-09-01 01:05:04 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2022-09-01 01:33:23 +0200 |
commit | 252174bacfffd5b3eb21056f678a225b368dda10 (patch) | |
tree | f8dca1dda2201386d2e9ecc0adfd524480fa9af0 /src/css | |
parent | 48c236c0dbd58edebf558ebb822e4ceaa56038f2 (diff) | |
download | chawan-252174bacfffd5b3eb21056f678a225b368dda10.tar.gz |
Fix combinator bugs + reimplement querySelectorAll
Diffstat (limited to 'src/css')
-rw-r--r-- | src/css/match.nim | 108 | ||||
-rw-r--r-- | src/css/selectorparser.nim | 62 |
2 files changed, 51 insertions, 119 deletions
diff --git a/src/css/match.nim b/src/css/match.nim index 1fcb9fda..a50f8396 100644 --- a/src/css/match.nim +++ b/src/css/match.nim @@ -1,4 +1,5 @@ import options +import streams import strutils import tables @@ -100,7 +101,9 @@ func pseudoSelectorMatches[T: Element|StyledNode](elem: T, sel: Selector, felem: func combinatorSelectorMatches[T: Element|StyledNode](elem: T, sel: Selector, felem: T): bool = let selem = elem - #combinator without at least two members makes no sense + # combinator without at least two members makes no sense + # actually, combinators with more than two elements are a pretty bad idea + # too. TODO getting rid of them would simplify this function greatly assert sel.csels.len > 1 if selem.selectorsMatch(sel.csels[^1], felem): var i = sel.csels.len - 2 @@ -132,13 +135,12 @@ func combinatorSelectorMatches[T: Element|StyledNode](elem: T, sel: Selector, fe e = e.parentElement of NEXT_SIBLING_COMBINATOR: var found = false - when elem is StyledNode: - var parent = elem.parent - else: - var parent = elem.parentElement + let parent = when elem is StyledNode: elem.parent + else: elem.parentElement + if parent == nil: return false for child in parent.children_rev: when elem is StyledNode: - if child.t != STYLED_ELEMENT or child.node == nil: continue + if not child.isDomElement: continue if found: if not child.selectorsMatch(sel.csels[i], felem): return false @@ -149,24 +151,24 @@ func combinatorSelectorMatches[T: Element|StyledNode](elem: T, sel: Selector, fe found = true of SUBSEQ_SIBLING_COMBINATOR: var found = false - when selem is StyledNode: - var parent = selem.parent - else: - var parent = elem.parentElement + let parent = when selem is StyledNode: selem.parent + else: elem.parentElement + if parent == nil: return false 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], felem): - dec i - if i < 0: - return true + if not child.isDomElement: continue if child == selem: found = true + continue + if not found: continue + if child.selectorsMatch(sel.csels[i], felem): + dec i + if i < 0: + return true return i == -1 return false -func selectorMatches[T: Element|StyledNode](elem: T, sel: Selector, felem: T): bool = +func selectorMatches[T: Element|StyledNode](elem: T, sel: Selector, felem: T = nil): bool = let selem = elem when elem is StyledNode: let elem = Element(selem.node) @@ -201,63 +203,15 @@ func selectorsMatch*[T: Element|StyledNode](elem: T, selectors: ComplexSelector, return false return true -#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 +proc querySelectorAll*(document: Document, q: string): seq[Element] = + let selectors = parseSelectors(newStringStream(q)) + for element in document.elements: + if element.selectorsMatch(selectors): + result.add(element) + +proc querySelector*(document: Document, q: string): Element = + let selectors = parseSelectors(newStringStream(q)) + for element in document.elements: + if element.selectorsMatch(selectors): + return element + return nil diff --git a/src/css/selectorparser.nim b/src/css/selectorparser.nim index b7197d95..f8abc7f7 100644 --- a/src/css/selectorparser.nim +++ b/src/css/selectorparser.nim @@ -1,4 +1,5 @@ import options +import streams import strutils import unicode @@ -172,33 +173,6 @@ func pseudo*(sels: ComplexSelector): PseudoElem = return sels[^1].elem return PSEUDO_NONE -func optimizeComplexSelector*(selectors: ComplexSelector): ComplexSelector = - #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 - inc i - - #pass 2: move selectors in combination - if selectors.len > 1: - var i = 0 - var slow: ComplexSelector - 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]) - proc addSelector(state: var SelectorParser, sel: Selector) = if state.combinator != nil: state.combinator.csels[^1].add(sel) @@ -223,21 +197,22 @@ func getComplexSelectors(state: var SelectorParser): seq[ComplexSelector] = result[^1].add(state.combinator) proc parseSelectorCombinator(state: var SelectorParser, ct: CombinatorType, csstoken: CSSToken) = - if csstoken.tokenType in {CSS_IDENT_TOKEN, CSS_HASH_TOKEN, - CSS_COLON_TOKEN}: - if state.combinator != nil and state.combinator.ct != ct: - let nc = Selector(t: COMBINATOR_SELECTOR, ct: ct) - nc.csels.add(@[state.combinator]) - state.combinator = nc - - if state.combinator == nil: - state.combinator = Selector(t: COMBINATOR_SELECTOR, ct: ct) - - state.combinator.csels.add(state.selectors[^1]) - if state.combinator.csels[^1].len > 0: - state.combinator.csels.add(newSeq[Selector]()) - state.selectors[^1].setLen(0) - state.query = QUERY_TYPE + if csstoken.tokenType notin {CSS_IDENT_TOKEN, CSS_HASH_TOKEN, CSS_COLON_TOKEN} and + (csstoken.tokenType != CSS_DELIM_TOKEN or csstoken.rvalue != Rune('.')): + return + if state.combinator != nil and state.combinator.ct != ct: + let nc = Selector(t: COMBINATOR_SELECTOR, ct: ct) + nc.csels.add(@[state.combinator]) + state.combinator = nc + + if state.combinator == nil: + state.combinator = Selector(t: COMBINATOR_SELECTOR, ct: ct) + + state.combinator.csels.add(state.selectors[^1]) + if state.combinator.csels[^1].len > 0: + state.combinator.csels.add(newSeq[Selector]()) + state.selectors[^1].setLen(0) + state.query = QUERY_TYPE proc parseSelectorToken(state: var SelectorParser, csstoken: CSSToken) = case state.query @@ -440,3 +415,6 @@ func parseSelectors*(cvals: seq[CSSComponentValue]): seq[ComplexSelector] = {.ca state.selectors[^1].add(state.combinator) state.combinator = nil return state.selectors + +proc parseSelectors*(stream: Stream): seq[ComplexSelector] = + return parseSelectors(parseListOfComponentValues(stream)) |