diff options
author | bptato <nincsnevem662@gmail.com> | 2021-08-04 17:54:27 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2021-08-04 17:54:27 +0200 |
commit | caad7b577162a73524277a943050493c489bfb59 (patch) | |
tree | a149be40ceebc4303d94c797d2a62ef62b1a42a0 /src/html | |
parent | 34b023515599bc746c10c597467ecb07f53c49fe (diff) | |
download | chawan-caad7b577162a73524277a943050493c489bfb59.tar.gz |
More css stuff
Diffstat (limited to 'src/html')
-rw-r--r-- | src/html/dom.nim | 128 | ||||
-rw-r--r-- | src/html/htmlparser.nim | 68 |
2 files changed, 77 insertions, 119 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() diff --git a/src/html/htmlparser.nim b/src/html/htmlparser.nim index f43bcf40..3cfc1d8d 100644 --- a/src/html/htmlparser.nim +++ b/src/html/htmlparser.nim @@ -31,71 +31,6 @@ type parentNode: Node textNode: Text -#func newHtmlElement(tagType: TagType, parentNode: Node): HtmlElement = -# case tagType -# of TAG_INPUT: result = new(HtmlInputElement) -# of TAG_A: result = new(HtmlAnchorElement) -# of TAG_SELECT: result = new(HtmlSelectElement) -# of TAG_OPTION: result = new(HtmlOptionElement) -# else: result = new(HtmlElement) -# -# result.nodeType = ELEMENT_NODE -# result.tagType = tagType -# result.parentNode = parentNode -# if parentNode.isElemNode(): -# result.parentElement = HtmlElement(parentNode) -# -# if tagType in DisplayInlineTags: -# result.display = DISPLAY_INLINE -# elif tagType in DisplayBlockTags: -# result.display = DISPLAY_BLOCK -# elif tagType in DisplayInlineBlockTags: -# result.display = DISPLAY_INLINE_BLOCK -# elif tagType == TAG_LI: -# result.display = DISPLAY_LIST_ITEM -# else: -# result.display = DISPLAY_NONE -# -# case tagType -# of TAG_CENTER: -# result.centered = true -# of TAG_B: -# result.bold = true -# of TAG_I: -# result.italic = true -# of TAG_U: -# result.underscore = true -# of TAG_HEAD: -# result.hidden = true -# of TAG_STYLE: -# result.hidden = true -# of TAG_SCRIPT: -# result.hidden = true -# of TAG_OPTION: -# result.hidden = true #TODO -# of TAG_PRE, TAG_TD, TAG_TH: -# result.margin = 1 -# of TAG_UL, TAG_OL: -# result.indent = 2 -# result.margin = 1 -# of TAG_H1, TAG_H2, TAG_H3, TAG_H4, TAG_H5, TAG_H6: -# result.bold = true -# result.margin = 1 -# of TAG_A: -# result.islink = true -# of TAG_INPUT: -# HtmlInputElement(result).size = 20 -# else: discard -# -# if parentNode.isElemNode(): -# let parent = HtmlElement(parentNode) -# result.centered = result.centered or parent.centered -# result.bold = result.bold or parent.bold -# result.italic = result.italic or parent.italic -# result.underscore = result.underscore or parent.underscore -# result.hidden = result.hidden or parent.hidden -# result.islink = result.islink or parent.islink - func inputSize*(str: string): int = if str.len == 0: return 20 @@ -104,7 +39,7 @@ func inputSize*(str: string): int = return 20 return str.parseInt() -#w3m's getescapecmd and parse_tag, transpiled to nim. +#w3m's getescapecmd and parse_tag, transpiled to nim and heavily modified. #(C) Copyright 1994-2002 by Akinori Ito #(C) Copyright 2002-2011 by Akinori Ito, Hironori Sakamoto, Fumitoshi Ukai # @@ -153,7 +88,6 @@ proc getescapecmd(buf: string, at: var int): string = elif not isAlphaAscii(buf[i]): return "" - #TODO this could be way more efficient (and radixnode needs better interface) when defined(small): var n = entityMap var s = "" |