diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client.nim | 50 | ||||
-rw-r--r-- | src/css/cascade.nim | 72 | ||||
-rw-r--r-- | src/css/mediaquery.nim | 306 | ||||
-rw-r--r-- | src/css/parser.nim | 164 | ||||
-rw-r--r-- | src/css/select.nim | 2 | ||||
-rw-r--r-- | src/css/sheet.nim | 52 | ||||
-rw-r--r-- | src/io/buffer.nim | 5 | ||||
-rw-r--r-- | src/layout/box.nim | 20 | ||||
-rw-r--r-- | src/layout/engine.nim | 8 |
9 files changed, 529 insertions, 150 deletions
diff --git a/src/client.nim b/src/client.nim index 23ebb9f3..7ec5b3de 100644 --- a/src/client.nim +++ b/src/client.nim @@ -43,17 +43,19 @@ proc actionError(s: string) = proc interruptError() = raise newException(InterruptError, "Interrupted") -proc getRemotePage(client: Client, url: string): Stream = - return client.http.get(url).bodyStream - -proc getLocalPage(url: string): Stream = - return newFileStream(url, fmRead) - -proc getPage(client: Client, url: Url): Stream = +proc getPage(client: Client, url: Url): tuple[s: Stream, contenttype: string] = if url.scheme == "file": - return getLocalPage($url.path) + let path = url.path.serialize() + result.contenttype = guessContentType(path) + result.s = newFileStream(path, fmRead) elif url.scheme == "http" or url.scheme == "https": - return client.getRemotePage(url.serialize()) + let resp = client.http.get(url.serialize(true)) + let ct = resp.contentType() + if ct != "": + result.contenttype = ct + else: + result.contenttype = guessContentType(url.path.serialize()) + result.s = resp.bodyStream proc addBuffer(client: Client) = if client.buffer == nil: @@ -91,8 +93,6 @@ proc discardBuffer(client: Client) = proc setupBuffer(client: Client) = let buffer = client.buffer - if not buffer.ispipe: - buffer.contenttype = guessContentType($buffer.location.path) buffer.userstyle = client.userstyle buffer.load() buffer.render() @@ -126,15 +126,17 @@ proc gotoUrl(client: Client, url: string, prevurl = none(Url), force = false, ne let prevurl = oldurl if force or prevurl.issome or not prevurl.get.equals(url, true): try: - let s = client.getPage(url) - if s != nil: + let page = client.getPage(url) + if page.s != nil: if newbuf: client.addBuffer() g_client = client setControlCHook(proc() {.noconv.} = - g_client.discardBuffer() + if g_client.buffer.prev != nil or g_client.buffer.next != nil: + g_client.discardBuffer() interruptError()) - client.buffer.istream = s + client.buffer.istream = page.s + client.buffer.contenttype = page.contenttype client.buffer.streamclosed = false else: loadError("Couldn't load " & $url) @@ -156,7 +158,6 @@ proc loadUrl(client: Client, url: string) = try: let cdir = parseUrl("file://" & getCurrentDir() & '/') client.gotoUrl(url, cdir, true) - except InterruptError: discard except LoadError: client.gotoUrl("http://" & url, none(Url), true) @@ -191,6 +192,7 @@ proc toggleSource*(client: Client) = client.buffer.sourcepair = client.buffer.prev client.buffer.sourcepair.sourcepair = client.buffer client.buffer.source = client.buffer.prev.source + client.buffer.streamclosed = true let prevtype = client.buffer.prev.contenttype if prevtype == "text/html": client.buffer.contenttype = "text/plain" @@ -198,6 +200,11 @@ proc toggleSource*(client: Client) = client.buffer.contenttype = "text/html" client.setupBuffer() +proc quit(client: Client) = + eraseScreen() + print(HVP(0, 0)) + quit(0) + proc input(client: Client) = let buffer = client.buffer if not client.feednext: @@ -208,10 +215,7 @@ proc input(client: Client) = client.s &= c let action = getNormalAction(client.s) case action - of ACTION_QUIT: - eraseScreen() - print(HVP(0, 0)) - quit(0) + of ACTION_QUIT: client.quit() of ACTION_CURSOR_LEFT: buffer.cursorLeft() of ACTION_CURSOR_DOWN: buffer.cursorDown() of ACTION_CURSOR_UP: buffer.cursorUp() @@ -256,6 +260,12 @@ proc input(client: Client) = proc inputLoop(client: Client) = while true: + g_client = client + setControlCHook(proc() {.noconv.} = + g_client.buffer.setStatusMessage("Interrupted rendering procedure") + g_client.buffer.redraw = true + g_client.buffer.reshape = false + g_client.inputLoop()) client.buffer.refreshBuffer() try: client.input() diff --git a/src/css/cascade.nim b/src/css/cascade.nim index 56c3dc09..50dbd139 100644 --- a/src/css/cascade.nim +++ b/src/css/cascade.nim @@ -1,11 +1,12 @@ -import streams +import algorithm import sequtils +import streams import sugar -import algorithm +import css/mediaquery +import css/parser import css/select import css/selparser -import css/parser import css/sheet import css/values import html/dom @@ -35,16 +36,57 @@ proc applyProperty(elem: Element, d: CSSDeclaration, pseudo: PseudoElem) = elem.cssapplied = true elem.rendered = false +func applies(mq: MediaQuery): bool = + case mq.t + of CONDITION_MEDIA: + case mq.media + of MEDIA_TYPE_ALL: return true + of MEDIA_TYPE_PRINT: return false + of MEDIA_TYPE_SCREEN: return true + of MEDIA_TYPE_SPEECH: return false + of MEDIA_TYPE_TTY: return true + of MEDIA_TYPE_UNKNOWN: return false + of CONDITION_NOT: + return not mq.n.applies() + of CONDITION_AND: + return mq.anda.applies() and mq.andb.applies() + of CONDITION_OR: + return mq.ora.applies() or mq.orb.applies() + of CONDITION_FEATURE: + case mq.feature.t + of FEATURE_COLOR: + return true #TODO + of FEATURE_GRID: + return mq.feature.b + of FEATURE_HOVER: + return mq.feature.b + of FEATURE_PREFERS_COLOR_SCHEME: + return mq.feature.b + +func applies(mqlist: MediaQueryList): bool = + for mq in mqlist: + if mq.applies(): + return true + return false + +func calcRule(tosorts: var array[PseudoElem, seq[tuple[s:int,b:CSSSimpleBlock]]], elem: Element, rule: CSSRuleBase) = + if rule of CSSRuleDef: + let rule = CSSRuleDef(rule) + for sel in rule.sels: + let match = elem.selectorsMatch(sel) + if match.success: + let spec = getSpecificity(sel) + tosorts[match.pseudo].add((spec,rule.oblock)) + elif rule of CSSMediaQueryDef: + let def = CSSMediaQueryDef(rule) + if def.query.applies(): + for child in def.children: + tosorts.calcRule(elem, child) + func calcRules(elem: Element, rules: CSSStylesheet): RuleList = - var tosorts: array[low(PseudoElem)..high(PseudoElem), seq[tuple[s:int,b:CSSSimpleBlock]]] + var tosorts: array[PseudoElem, seq[tuple[s:int,b:CSSSimpleBlock]]] for rule in rules: - if rule of CSSRuleDef: - let rule = CSSRuleDef(rule) - for sel in rule.sels: - let match = elem.selectorsMatch(sel) - if match.success: - let spec = getSpecificity(sel) - tosorts[match.pseudo].add((spec,rule.oblock)) + tosorts.calcRule(elem, rule) for i in low(PseudoElem)..high(PseudoElem): tosorts[i].sort((x, y) => cmp(x.s,y.s)) @@ -64,23 +106,23 @@ proc applyRules(element: Element, ua, user, author: RuleList, pseudo: PseudoElem let rules_user_agent = ua[pseudo] for rule in rules_user_agent: - let decls = parseCSSListOfDeclarations(rule.value) + let decls = parseListOfDeclarations(rule.value) ares.applyItems(decls) let rules_user = user[pseudo] for rule in rules_user: - let decls = parseCSSListOfDeclarations(rule.value) + let decls = parseListOfDeclarations(rule.value) ares.applyItems(decls) let rules_author = author[pseudo] for rule in rules_author: - let decls = parseCSSListOfDeclarations(rule.value) + let decls = parseListOfDeclarations(rule.value) ares.applyItems(decls) if pseudo == PSEUDO_NONE: let style = element.attr("style") if style.len > 0: - let inline_rules = newStringStream(style).parseCSSListOfDeclarations() + let inline_rules = newStringStream(style).parseListOfDeclarations() ares.applyItems(inline_rules) for rule in ares.normal: diff --git a/src/css/mediaquery.nim b/src/css/mediaquery.nim new file mode 100644 index 00000000..bf9d2535 --- /dev/null +++ b/src/css/mediaquery.nim @@ -0,0 +1,306 @@ +import tables +import unicode + +import css/parser + +type + MediaQueryParser = object + at: int + cvals: seq[CSSComponentValue] + + MediaType* = enum + MEDIA_TYPE_UNKNOWN, MEDIA_TYPE_ALL, MEDIA_TYPE_PRINT, MEDIA_TYPE_SCREEN, + MEDIA_TYPE_SPEECH, MEDIA_TYPE_TTY + + MediaConditionType* = enum + CONDITION_NOT, CONDITION_AND, CONDITION_OR, CONDITION_FEATURE, + CONDITION_MEDIA + + MediaFeatureType* = enum + FEATURE_COLOR, FEATURE_GRID, FEATURE_HOVER, FEATURE_PREFERS_COLOR_SCHEME + + MediaFeature* = object + case t*: MediaFeatureType + of FEATURE_COLOR: + color*: Slice[int] + of FEATURE_GRID, FEATURE_HOVER, FEATURE_PREFERS_COLOR_SCHEME: + b*: bool + + MediaQuery* = ref object + case t*: MediaConditionType + of CONDITION_MEDIA: + media*: MediaType + of CONDITION_FEATURE: + feature*: MediaFeature + of CONDITION_NOT: + n*: MediaQuery + of CONDITION_OR: + ora*: MediaQuery + orb*: MediaQuery + of CONDITION_AND: + anda*: MediaQuery + andb*: MediaQuery + + MediaQueryList* = seq[MediaQuery] + +const MediaTypes = { + "all": MEDIA_TYPE_ALL, + "print": MEDIA_TYPE_PRINT, + "screen": MEDIA_TYPE_SCREEN, + "speech": MEDIA_TYPE_SPEECH, + "tty": MEDIA_TYPE_TTY +}.toTable() + +proc has(parser: MediaQueryParser, i = 0): bool {.inline.} = + return parser.cvals.len > parser.at + i + +proc consume(parser: var MediaQueryParser): CSSComponentValue {.inline.} = + result = parser.cvals[parser.at] + inc parser.at + +proc reconsume(parser: var MediaQueryParser) {.inline.} = + dec parser.at + +proc peek(parser: MediaQueryParser, i = 0): CSSComponentValue {.inline.} = + return parser.cvals[parser.at + i] + +proc skipBlanks(parser: var MediaQueryParser) {.inline.} = + while parser.has(): + let cval = parser.peek() + if cval of CSSToken and CSSToken(cval).tokenType == CSS_WHITESPACE_TOKEN: + inc parser.at + else: + break + +proc getBoolFeature(feature: MediaFeatureType): MediaQuery = + result = MediaQuery(t: CONDITION_FEATURE) + case feature + of FEATURE_GRID, FEATURE_HOVER, FEATURE_PREFERS_COLOR_SCHEME: + result.feature = MediaFeature(t: feature, b: true) + of FEATURE_COLOR: + result.feature = MediaFeature(t: feature, color: 1..high(int)) + +template get_tok(tok: untyped) = + if not (cval of CSSToken): return nil + tok = CSSToken(cval) + +template get_idtok(tok: untyped) = + get_tok(tok) + if tok.tokenType != CSS_IDENT_TOKEN: return nil + +template expect_mq_int(b: bool, ifalse: int, itrue: int) = + let cval = parser.consume() + if not (cval of CSSToken): return nil + let tok = CSSToken(cval) + if tok.tokenType != CSS_NUMBER_TOKEN: return nil + let i = int(tok.nvalue) + if i == ifalse: b = false + elif i == itrue: b = true + else: return nil + +template expect_bool(b: bool, sfalse: string, strue: string) = + let cval = parser.consume() + if not (cval of CSSToken): return nil + let tok = CSSToken(cval) + if tok.tokenType != CSS_IDENT_TOKEN: return nil + let s = $tok.value + case s + of strue: b = true + of sfalse: b = false + else: return nil + +proc parseFeature(parser: var MediaQueryParser, feature: MediaFeatureType): MediaQuery = + if not parser.has(): return getBoolFeature(feature) + let cval = parser.consume() + var tok: CSSToken + get_tok(tok) + if tok.tokenType != CSS_COLON_TOKEN: return nil + parser.skipBlanks() + case feature + of FEATURE_GRID: + var b: bool + expect_mq_int(b, 0, 1) + result = MediaQuery(t: CONDITION_FEATURE, feature: MediaFeature(t: feature, b: b)) + of FEATURE_HOVER: + var b: bool + expect_bool(b, "none", "hover") + result = MediaQuery(t: CONDITION_FEATURE, feature: MediaFeature(t: feature, b: b)) + of FEATURE_PREFERS_COLOR_SCHEME: + var b: bool + expect_bool(b, "light", "dark") + result = MediaQuery(t: CONDITION_FEATURE, feature: MediaFeature(t: feature, b: b)) + else: return nil + + parser.skipBlanks() + if parser.has(): + return nil + +proc parseMediaCondition(parser: var MediaQueryParser, non = false, noor = false): MediaQuery + +proc parseMediaInParens(parser: var MediaQueryParser): MediaQuery = + var fparser: MediaQueryParser + block: + let cval = parser.consume() + if not (cval of CSSSimpleBlock): return nil + + let sb = CSSSimpleBlock(cval) + if sb.token.tokenType != CSS_LPAREN_TOKEN: return nil + + fparser.cvals = sb.value + fparser.skipBlanks() + + block: + let cval = fparser.consume() + var tok: CSSToken + get_tok(tok) + fparser.skipBlanks() + if tok.tokenType == CSS_IDENT_TOKEN: + let tokval = $tok.value + case tokval + of "not": + return fparser.parseMediaCondition(true) + of "color": + return fparser.parseFeature(FEATURE_COLOR) + of "grid": + return fparser.parseFeature(FEATURE_GRID) + of "hover": + return fparser.parseFeature(FEATURE_HOVER) + of "prefers-color-scheme": + return fparser.parseFeature(FEATURE_PREFERS_COLOR_SCHEME) + else: discard + return nil + +proc parseMediaOr(parser: var MediaQueryParser, left: MediaQuery): MediaQuery = + let right = parser.parseMediaCondition() + return MediaQuery(t: CONDITION_OR, ora: left, orb: right) + +proc parseMediaAnd(parser: var MediaQueryParser, left: MediaQuery): MediaQuery = + let right = parser.parseMediaCondition() + return MediaQuery(t: CONDITION_AND, anda: left, andb: right) + +proc parseMediaCondition(parser: var MediaQueryParser, non = false, noor = false): MediaQuery = + var non = non + if not non: + let cval = parser.consume() + if cval of CSSToken: + if $CSSToken(cval).value == "not": + non = true + else: + parser.reconsume() + + if not parser.has(): + return nil + + result = parser.parseMediaInParens() + + if result == nil: + return nil + + if non: + result = MediaQuery(t: CONDITION_NOT, n: result) + + parser.skipBlanks() + if not parser.has(): + return result + + let cval = parser.consume() + var tok: CSSToken + get_idtok(tok) + parser.skipBlanks() + let tokval = $tok.value + case tokval + of "and": + return parser.parseMediaAnd(result) + of "or": + if noor: + return nil + return parser.parseMediaOr(result) + else: discard + +proc parseMediaQuery(parser: var MediaQueryParser): MediaQuery = + parser.skipBlanks() + if not parser.has(): + return nil + var non = false + block: + let cval = parser.consume() + if cval of CSSToken: + let tok = CSSToken(cval) + if tok.tokenType == CSS_IDENT_TOKEN: + let tokval = $tok.value + case tokval + of "not": + non = true + of "only": + discard + elif tokval in MediaTypes: + result = MediaQuery(t: CONDITION_MEDIA, media: MediaTypes[tokval]) + else: + return nil + else: + return nil + else: + parser.reconsume() + return parser.parseMediaCondition() + + parser.skipBlanks() + if not parser.has(): + return result + + block: + let cval = parser.consume() + if cval of CSSToken: + let tok = CSSToken(cval) + if tok.tokenType == CSS_IDENT_TOKEN: + let tokval = $tok.value + if result == nil: + if tokval in MediaTypes: + let mq = MediaQuery(t: CONDITION_MEDIA, media: MediaTypes[tokval]) + if non: + result = MediaQuery(t: CONDITION_NOT, n: mq) + else: + result = mq + else: + return nil + else: + if tokval == "and": + parser.reconsume() + return parser.parseMediaAnd(result) + else: + return nil + else: + return nil + else: + parser.reconsume() + return parser.parseMediaCondition(non) + + parser.skipBlanks() + if not parser.has(): + return result + + block: + let cval = parser.consume() + if cval of CSSToken: + let tok = CSSToken(cval) + if tok.tokenType == CSS_IDENT_TOKEN: + let tokval = $tok.value + if tokval != "and": + return nil + else: + return nil + + parser.skipBlanks() + if not parser.has(): + return nil + + parser.reconsume() + return parser.parseMediaAnd(result) + +proc parseMediaQueryList*(cvals: seq[CSSComponentValue]): MediaQueryList = + let cseplist = cvals.parseCommaSeparatedListOfComponentValues() + for list in cseplist: + var parser: MediaQueryParser + parser.cvals = list + let query = parser.parseMediaQuery() + if query != nil: + result.add(query) diff --git a/src/css/parser.nim b/src/css/parser.nim index c13018de..a3668739 100644 --- a/src/css/parser.nim +++ b/src/css/parser.nim @@ -1,7 +1,7 @@ -import unicode -import streams import options +import streams import sugar +import unicode import utils/twtstr @@ -74,6 +74,65 @@ type SyntaxError = object of ValueError +func `$`*(c: CSSParsedItem): string = + if c of CSSToken: + case CSSToken(c).tokenType: + of CSS_FUNCTION_TOKEN, CSS_AT_KEYWORD_TOKEN, CSS_URL_TOKEN: + result &= $CSSToken(c).tokenType & $CSSToken(c).value & '\n' + of CSS_HASH_TOKEN: + result &= '#' & $CSSToken(c).value + of CSS_IDENT_TOKEN: + result &= $CSSToken(c).value + of CSS_STRING_TOKEN: + result &= ("\"" & $CSSToken(c).value & "\"") + of CSS_DELIM_TOKEN: + result &= $CSSToken(c).rvalue + of CSS_DIMENSION_TOKEN: + result &= $CSSToken(c).tokenType & $CSSToken(c).nvalue & "unit" & $CSSToken(c).unit & $CSSToken(c).tflagb + of CSS_NUMBER_TOKEN: + result &= $CSSToken(c).nvalue & $CSSToken(c).unit + of CSS_PERCENTAGE_TOKEN: + result &= $CSSToken(c).nvalue & "%" + of CSS_COLON_TOKEN: + result &= ":" + of CSS_WHITESPACE_TOKEN: + result &= " " + of CSS_SEMICOLON_TOKEN: + result &= ";\n" + of CSS_COMMA_TOKEN: + result &= "," + else: + result &= $CSSToken(c).tokenType & '\n' + elif c of CSSDeclaration: + result &= $CSSDeclaration(c).name + result &= ": " + for s in CSSDeclaration(c).value: + result &= $s + result &= ";\n" + elif c of CSSFunction: + result &= $CSSFunction(c).name & "(" + for s in CSSFunction(c).value: + result &= $s + result &= ")" + elif c of CSSSimpleBlock: + case CSSSimpleBlock(c).token.tokenType + of CSS_LBRACE_TOKEN: result &= "{\n" + of CSS_LPAREN_TOKEN: result &= "(" + of CSS_LBRACKET_TOKEN: result &= "[" + else: discard + for s in CSSSimpleBlock(c).value: + result &= $s + case CSSSimpleBlock(c).token.tokenType + of CSS_LBRACE_TOKEN: result &= "\n}" + of CSS_LPAREN_TOKEN: result &= ")" + of CSS_LBRACKET_TOKEN: result &= "]" + else: discard + elif c of CSSRule: + if c of CSSAtRule: + result &= $CSSAtRule(c).name & " " + result &= $CSSRule(c).prelude & "\n" + result &= $CSSRule(c).oblock + func `==`*(a: CSSParsedItem, b: CSSTokenType): bool = return a of CSSToken and CSSToken(a).tokenType == b @@ -466,7 +525,7 @@ proc consumeQualifiedRule(state: var CSSParseState): Option[CSSQualifiedRule] = while state.has(): let t = state.consume() if t of CSSSimpleBlock: - r.oblock = state.consumeSimpleBlock() + r.oblock = CSSSimpleBlock(t) return some(r) elif t == CSS_LBRACE_TOKEN: r.oblock = state.consumeSimpleBlock() @@ -484,7 +543,7 @@ proc consumeAtRule(state: var CSSParseState): CSSAtRule = while state.at < state.tokens.len: let t = state.consume() if t of CSSSimpleBlock: - result.oblock = state.consumeSimpleBlock() + result.oblock = CSSSimpleBlock(t) elif t == CSS_SEMICOLON_TOKEN: return result elif t == CSS_LBRACE_TOKEN: @@ -535,7 +594,6 @@ proc consumeDeclaration(state: var CSSParseState): Option[CSSDeclaration] = #> and at-rules, as CSS 2.1 does for @page. Unexpected at-rules (which could be #> all of them, in a given context) are invalid and should be ignored by the #> consumer. -#Wow this is ugly. proc consumeListOfDeclarations(state: var CSSParseState): seq[CSSParsedItem] = while state.has(): let t = state.consume() @@ -594,9 +652,11 @@ proc parseStylesheet(inputStream: Stream): CSSRawStylesheet = proc parseListOfRules(state: var CSSParseState): seq[CSSRule] = return state.consumeListOfRules() -proc parseListOfRules(inputStream: Stream): seq[CSSRule] = +proc parseListOfRules*(cvals: seq[CSSComponentValue]): seq[CSSRule] = var state = CSSParseState() - state.tokens = tokenizeCSS(inputStream) + state.tokens = collect(newSeq): + for cval in cvals: + CSSParsedItem(cval) return state.parseListOfRules() proc parseRule(state: var CSSParseState): CSSRule = @@ -637,7 +697,7 @@ proc parseDeclaration(state: var CSSParseState): CSSDeclaration = raise newException(SyntaxError, "No declaration found!") -proc parseCSSDeclaration*(inputStream: Stream): CSSDeclaration = +proc parseDeclaration*(inputStream: Stream): CSSDeclaration = var state = CSSParseState() state.tokens = tokenizeCSS(inputStream) return state.parseDeclaration() @@ -645,15 +705,15 @@ proc parseCSSDeclaration*(inputStream: Stream): CSSDeclaration = proc parseListOfDeclarations(state: var CSSParseState): seq[CSSParsedItem] = return state.consumeListOfDeclarations() -proc parseCSSListOfDeclarations*(cvals: seq[CSSComponentValue]): seq[CSSParsedItem] = - var state = CSSParseState() +proc parseListOfDeclarations*(cvals: seq[CSSComponentValue]): seq[CSSParsedItem] = + var state: CSSParseState state.tokens = collect(newSeq): for cval in cvals: CSSParsedItem(cval) return state.consumeListOfDeclarations() -proc parseCSSListOfDeclarations*(inputStream: Stream): seq[CSSParsedItem] = - var state = CSSParseState() +proc parseListOfDeclarations*(inputStream: Stream): seq[CSSParsedItem] = + var state: CSSParseState state.tokens = tokenizeCSS(inputStream) return state.parseListOfDeclarations() @@ -670,8 +730,8 @@ proc parseComponentValue(state: var CSSParseState): CSSComponentValue = if state.has(): raise newException(SyntaxError, "EOF not reached!") -proc parseCSSComponentValue*(inputStream: Stream): CSSComponentValue = - var state = CSSParseState() +proc parseComponentValue*(inputStream: Stream): CSSComponentValue = + var state: CSSParseState state.tokens = tokenizeCSS(inputStream) return state.parseComponentValue() @@ -679,76 +739,34 @@ proc parseListOfComponentValues(state: var CSSParseState): seq[CSSComponentValue while state.has(): result.add(state.consumeComponentValue()) -proc parseCSSListOfComponentValues*(inputStream: Stream): seq[CSSComponentValue] = +proc parseListOfComponentValues*(inputStream: Stream): seq[CSSComponentValue] = var state = CSSParseState() state.tokens = tokenizeCSS(inputStream) return state.parseListOfComponentValues() -proc parseCommaSeparatedListOfComponentValues(state: var CSSParseState): seq[CSSComponentValue] = - while state.has(1): +proc parseCommaSeparatedListOfComponentValues(state: var CSSParseState): seq[seq[CSSComponentValue]] = + if state.has(): + result.add(newSeq[CSSComponentValue]()) + + while state.has(): let cvl = state.consumeComponentValue() if cvl != CSS_COMMA_TOKEN: - result.add(state.consumeComponentValue()) + result[^1].add(cvl) + else: + result.add(newSeq[CSSComponentValue]()) + +proc parseCommaSeparatedListOfComponentValues*(cvals: seq[CSSComponentValue]): seq[seq[CSSComponentValue]] = + var state: CSSParseState + state.tokens = collect(newSeq): + for cval in cvals: + CSSParsedItem(cval) + return state.parseCommaSeparatedListOfComponentValues() -proc parseCommaSeparatedListOfComponentValues(inputStream: Stream): seq[CSSComponentValue] = +proc parseCommaSeparatedListOfComponentValues(inputStream: Stream): seq[seq[CSSComponentValue]] = var state = CSSParseState() state.tokens = tokenizeCSS(inputStream) return state.parseCommaSeparatedListOfComponentValues() -func `$`*(c: CSSComponentValue): string = - if c of CSSToken: - case CSSToken(c).tokenType: - of CSS_FUNCTION_TOKEN, CSS_AT_KEYWORD_TOKEN, CSS_URL_TOKEN: - result &= $CSSToken(c).tokenType & $CSSToken(c).value & '\n' - of CSS_HASH_TOKEN: - result &= '#' & $CSSToken(c).value - of CSS_IDENT_TOKEN: - result &= $CSSToken(c).value - of CSS_STRING_TOKEN: - result &= ("\"" & $CSSToken(c).value & "\"") - of CSS_DELIM_TOKEN: - result &= $CSSToken(c).rvalue - of CSS_DIMENSION_TOKEN: - result &= $CSSToken(c).tokenType & $CSSToken(c).nvalue & "unit" & $CSSToken(c).unit & $CSSToken(c).tflagb - of CSS_NUMBER_TOKEN: - result &= $CSSToken(c).nvalue & $CSSToken(c).unit - of CSS_PERCENTAGE_TOKEN: - result &= $CSSToken(c).nvalue & "%" - of CSS_COLON_TOKEN: - result &= ":" - of CSS_WHITESPACE_TOKEN: - result &= " " - of CSS_SEMICOLON_TOKEN: - result &= ";\n" - of CSS_COMMA_TOKEN: - result &= "," - else: - result &= $CSSToken(c).tokenType & '\n' - elif c of CSSDeclaration: - result &= $CSSDeclaration(c).name - result &= ": " - for s in CSSDeclaration(c).value: - result &= $s - result &= ";\n" - elif c of CSSFunction: - result &= $CSSFunction(c).name & "(" - for s in CSSFunction(c).value: - result &= $s - result &= ")" - elif c of CSSSimpleBlock: - case CSSSimpleBlock(c).token.tokenType - of CSS_LBRACE_TOKEN: result &= "{\n" - of CSS_LPAREN_TOKEN: result &= "(" - of CSS_LBRACKET_TOKEN: result &= "[" - else: discard - for s in CSSSimpleBlock(c).value: - result &= $s - case CSSSimpleBlock(c).token.tokenType - of CSS_LBRACE_TOKEN: result &= "\n}" - of CSS_LPAREN_TOKEN: result &= ")" - of CSS_LBRACKET_TOKEN: result &= "]" - else: discard - proc parseCSS*(inputStream: Stream): CSSRawStylesheet = if inputStream.atEnd(): return CSSRawStylesheet() diff --git a/src/css/select.nim b/src/css/select.nim index 1f43844f..9da20d4f 100644 --- a/src/css/select.nim +++ b/src/css/select.nim @@ -184,7 +184,7 @@ func selectElems(document: Document, selectors: SelectorList): seq[Element] = proc querySelector*(document: Document, q: string): seq[Element] = let ss = newStringStream(q) - let cvals = parseCSSListOfComponentValues(ss) + let cvals = parseListOfComponentValues(ss) let selectors = parseSelectors(cvals) for sel in selectors: diff --git a/src/css/sheet.nim b/src/css/sheet.nim index c79dcd2a..468585fa 100644 --- a/src/css/sheet.nim +++ b/src/css/sheet.nim @@ -1,49 +1,51 @@ import streams +import unicode +import css/mediaquery import css/parser import css/selparser type - CSSRuleBase* = object of RootObj + CSSRuleBase* = ref object of RootObj - CSSRuleDef* = object of CSSRuleBase + CSSRuleDef* = ref object of CSSRuleBase sels*: seq[SelectorList] oblock*: CSSSimpleBlock - CSSConditionalDef* = object of CSSRuleBase - rules*: CSSSimpleBlock - nested*: seq[CSSConditionalDef] + CSSConditionalDef* = ref object of CSSRuleBase + children*: seq[CSSRuleBase] - CSSMediaQueryDef = object of CSSConditionalDef - query*: string + CSSMediaQueryDef* = ref object of CSSConditionalDef + query*: MediaQueryList - CSSStylesheet* = seq[CSSRuleDef] + CSSStylesheet* = seq[CSSRuleBase] -proc addRule(stylesheet: var CSSStylesheet, rule: CSSRule) = +proc addRule(stylesheet: var CSSStylesheet, rule: CSSQualifiedRule) = let sels = parseSelectors(rule.prelude) if sels.len > 1 or sels[^1].len > 0: let r = CSSRuleDef(sels: sels, oblock: rule.oblock) stylesheet.add(r) -proc parseAtRule(atrule: CSSAtRule): CSSConditionalDef = - for v in atrule.oblock.value: - if v of CSSRule: - if v of CSSAtRule: - #let atrule = CSSAtRule(v) - discard - else: - discard - #let rule = CSSRule(v) - #let sels = parseSelectors(rule.prelude) - #result.rules.add(CSSRule) +proc addAtRule(stylesheet: var CSSStylesheet, atrule: CSSAtRule) = + case $atrule.name + of "media": + let query = parseMediaQueryList(atrule.prelude) + let rules = atrule.oblock.value.parseListOfRules() + if rules.len > 0: + var media = CSSMediaQueryDef() + media.query = query + for rule in rules: + if rule of CSSAtRule: + media.children.addAtRule(CSSAtRule(rule)) + else: + media.children.addRule(CSSQualifiedRule(rule)) #qualified rule + stylesheet.add(media) + else: discard #TODO proc parseStylesheet*(s: Stream): CSSStylesheet = for v in parseCSS(s).value: - if v of CSSAtRule: - discard - #result.add(CSSAtRule(v).parseAtRule()) - else: - result.addRule(v) + if v of CSSAtRule: result.addAtRule(CSSAtRule(v)) + else: result.addRule(CSSQualifiedRule(v)) proc parseStylesheet*(s: string): CSSStylesheet = return newStringStream(s).parseStylesheet() diff --git a/src/io/buffer.nim b/src/io/buffer.nim index a2d44b8c..c25731b7 100644 --- a/src/io/buffer.nim +++ b/src/io/buffer.nim @@ -725,13 +725,14 @@ proc load*(buffer: Buffer) = #config option like a) store source b) generate source buffer.source = buffer.istream.readAll() buffer.istream.close() - buffer.document = parseHtml(newStringStream(buffer.source)) buffer.streamclosed = true + buffer.document = parseHtml(newStringStream(buffer.source)) else: if not buffer.streamclosed: - buffer.lines = renderStream(buffer.istream) + buffer.source = buffer.istream.readAll() buffer.istream.close() buffer.streamclosed = true + buffer.lines = renderPlainText(buffer.source) proc render*(buffer: Buffer) = case buffer.contenttype diff --git a/src/layout/box.nim b/src/layout/box.nim index 556ad88d..a165c729 100644 --- a/src/layout/box.nim +++ b/src/layout/box.nim @@ -46,16 +46,16 @@ type rows*: seq[CSSRowBox] thisrow*: seq[CSSRowBox] - dimensions*: Rectangle - content*: seq[CSSInlineRow] - rcontent*: CSSInlineRow - color*: CSSColor - fontstyle*: CSSFontStyle - fontweight*: int - textdecoration*: CSSTextDecoration - nodes*: seq[Node] - - maxwidth*: int + #dimensions*: Rectangle + #content*: seq[CSSInlineRow] + #rcontent*: CSSInlineRow + #color*: CSSColor + #fontstyle*: CSSFontStyle + #fontweight*: int + #textdecoration*: CSSTextDecoration + #nodes*: seq[Node] + + #maxwidth*: int BlockContext* = ref object fromy*: int diff --git a/src/layout/engine.nim b/src/layout/engine.nim index bea3371d..97c24d01 100644 --- a/src/layout/engine.nim +++ b/src/layout/engine.nim @@ -497,7 +497,7 @@ proc alignBoxes*(document: Document, term: TermAttributes): CSSBox = state.processElemChildren(rootbox, document.root) return rootbox -proc alignBoxes2*(document: Document, term: TermAttributes): CSSBlockBox = - result = CSSBlockBox() - result.bcontext = BlockContext() - result.bcontext.content.add(CSSInlineBox()) +#proc alignBoxes2*(document: Document, term: TermAttributes): CSSBlockBox = +# result = CSSBlockBox() +# result.bcontext = BlockContext() +# result.bcontext.content.add(CSSInlineBox()) |