import unicode import terminal import tables import utils/twtstr import types/enums import css/parser type CSSLength* = object num*: float64 unit*: CSSUnit auto*: bool CSS2Properties* = ref object rawtext*: string fmttext*: seq[string] x*: int y*: int ex*: int ey*: int width*: int height*: int hidden*: bool before*: CSS2Properties after*: CSS2Properties margintop*: CSSLength marginbottom*: CSSLength marginleft*: CSSLength marginright*: CSSLength centered*: bool display*: DisplayType bold*: bool fontStyle*: CSSFontStyle underscore*: bool islink*: bool selected*: bool indent*: int color*: CSSColor position*: CSSPosition content*: seq[Rune] CSSColor* = tuple[r: uint8, g: uint8, b: uint8, a: uint8] CSSComputedValue* = object of RootObj t*: CSSRuleType case v*: CSSValueType of VALUE_COLOR: color*: CSSColor of VALUE_LENGTH: length*: CSSLength of VALUE_FONT_STYLE: fontstyle*: CSSFontStyle of VALUE_DISPLAY: display*: DisplayType of VALUE_CONTENT: content*: seq[Rune] of VALUE_NONE: discard CSSSpecifiedValue* = object of CSSComputedValue hasGlobalValue: bool globalValue: CSSGlobalValueType const ValueTypes = { RULE_ALL: VALUE_NONE, RULE_COLOR: VALUE_COLOR, RULE_MARGIN: VALUE_LENGTH, RULE_MARGIN_TOP: VALUE_LENGTH, RULE_MARGIN_BOTTOM: VALUE_LENGTH, RULE_MARGIN_LEFT: VALUE_LENGTH, RULE_MARGIN_RIGHT: VALUE_LENGTH, RULE_FONT_STYLE: VALUE_FONT_STYLE, RULE_DISPLAY: VALUE_DISPLAY, RULE_CONTENT: VALUE_CONTENT, }.toTable() func getValueType*(rule: CSSRuleType): CSSValueType = return ValueTypes[rule] func cells(l: CSSLength): int = case l.unit of UNIT_EM: return int(l.num) else: #TODO return int(l.num / 8) const colors = { "maroon": (0x80u8, 0x00u8, 0x00u8, 0x00u8), "red": (0xffu8, 0x00u8, 0x00u8, 0x00u8), "orange": (0xffu8, 0xa5u8, 0x00u8, 0x00u8), "yellow": (0xffu8, 0xffu8, 0x00u8, 0x00u8), "olive": (0x80u8, 0x80u8, 0x00u8, 0x00u8), "purple": (0x80u8, 0x00u8, 0x80u8, 0x00u8), "fuchsia": (0xffu8, 0x00u8, 0x00u8, 0x00u8), "white": (0xffu8, 0xffu8, 0xffu8, 0x00u8), "lime": (0x00u8, 0xffu8, 0x00u8, 0x00u8), "green": (0x00u8, 0x80u8, 0x00u8, 0x00u8), "navy": (0x00u8, 0x00u8, 0x80u8, 0x00u8), "blue": (0x00u8, 0x00u8, 0xffu8, 0x00u8), "aqua": (0x00u8, 0xffu8, 0xffu8, 0x00u8), "teal": (0x00u8, 0x80u8, 0x80u8, 0x00u8), "black": (0x00u8, 0x00u8, 0x00u8, 0x00u8), "silver": (0xc0u8, 0xc0u8, 0xc0u8, 0x00u8), "gray": (0x80u8, 0x80u8, 0x80u8, 0x00u8), }.toTable() const defaultColor = (0xffu8, 0xffu8, 0xffu8, 0x00u8) func cssLength(val: float64, unit: string): CSSLength = case unit of "%": return CSSLength(num: val, unit: UNIT_PERC) of "cm": return CSSLength(num: val, unit: UNIT_CM) of "mm": return CSSLength(num: val, unit: UNIT_MM) of "in": return CSSLength(num: val, unit: UNIT_IN) of "px": return CSSLength(num: val, unit: UNIT_PX) of "pt": return CSSLength(num: val, unit: UNIT_PT) of "pc": return CSSLength(num: val, unit: UNIT_PC) of "em": return CSSLength(num: val, unit: UNIT_EM) of "ex": return CSSLength(num: val, unit: UNIT_EX) of "ch": return CSSLength(num: val, unit: UNIT_CH) of "rem": return CSSLength(num: val, unit: UNIT_REM) of "vw": return CSSLength(num: val, unit: UNIT_VW) of "vh": return CSSLength(num: val, unit: UNIT_VH) of "vmin": return CSSLength(num: val, unit: UNIT_VMIN) of "vmax": return CSSLength(num: val, unit: UNIT_VMAX) else: return CSSLength(num: 0, unit: UNIT_EM) func cssColor*(d: CSSDeclaration): CSSColor = if d.value.len > 0: if d.value[0] of CSSToken: let tok = CSSToken(d.value[0]) case tok.tokenType of CSS_HASH_TOKEN: let s = tok.value if s.len == 3: for r in s: if hexValue(r) == -1: return let r = hexValue(s[0]) * 0x10 + hexValue(s[0]) let g = hexValue(s[1]) * 0x10 + hexValue(s[1]) let b = hexValue(s[2]) * 0x10 + hexValue(s[2]) return (uint8(r), uint8(g), uint8(b), 0x00u8) elif s.len == 6: for r in s: if hexValue(r) == -1: return let r = hexValue(s[0]) * 0x10 + hexValue(s[1]) let g = hexValue(s[2]) * 0x10 + hexValue(s[3]) let b = hexValue(s[4]) * 0x10 + hexValue(s[5]) return (uint8(r), uint8(g), uint8(b), 0x00u8) else: return defaultColor of CSS_IDENT_TOKEN: let s = tok.value eprint "ident", s if $s in colors: return colors[$s] else: return defaultColor else: eprint "else", tok.tokenType return defaultColor elif d.value[0] of CSSFunction: let f = CSSFunction(d.value[0]) eprint "func", f.name #todo calc etc (cssnumber function or something) case $f.name of "rgb": if f.value.len != 3: return defaultColor for c in f.value: if c != CSS_NUMBER_TOKEN: return defaultColor let r = CSSToken(f.value[0]).nvalue let g = CSSToken(f.value[1]).nvalue let b = CSSToken(f.value[2]).nvalue return (uint8(r), uint8(g), uint8(b), 0x00u8) of "rgba": if f.value.len != 4: eprint "too few args" return defaultColor for c in f.value: if c != CSS_NUMBER_TOKEN: eprint "not number" return defaultColor let r = CSSToken(f.value[0]).nvalue let g = CSSToken(f.value[1]).nvalue let b = CSSToken(f.value[2]).nvalue let a = CSSToken(f.value[3]).nvalue return (uint8(r), uint8(g), uint8(b), uint8(a)) else: eprint "not rgba" return defaultColor return defaultColor func cssLength(d: CSSDeclaration): CSSLength = if d.value.len > 0 and d.value[0] of CSSToken: let tok = CSSToken(d.value[0]) case tok.tokenType of CSS_PERCENTAGE_TOKEN: return cssLength(tok.nvalue, "%") of CSS_DIMENSION_TOKEN: return cssLength(tok.nvalue, $tok.unit) of CSS_IDENT_TOKEN: if $tok.value == "auto": return CSSLength(auto: true) else: return CSSLength(num: 0, unit: UNIT_EM) return CSSLength(num: 0, unit: UNIT_EM) func hasColor*(style: CSS2Properties): bool = return style.color.r != 0 or style.color.b != 0 or style.color.g != 0 or style.color.a != 0 func termColor*(style: CSS2Properties): ForegroundColor = if style.color.r > 120: return fgRed elif style.color.b > 120: return fgBlue elif style.color.g > 120: return fgGreen else: return fgWhite func isToken(d: CSSDeclaration): bool = d.value.len > 0 and d.value[0] of CSSToken func getToken(d: CSSDeclaration): CSSToken = (CSSToken)d.value[0] func cssString(d: CSSDeclaration): seq[Rune] = if isToken(d): let tok = getToken(d) case tok.tokenType of CSS_IDENT_TOKEN, CSS_STRING_TOKEN: return tok.value else: return func cssDisplay(d: CSSDeclaration): DisplayType = if isToken(d): let tok = getToken(d) if tok.tokenType == CSS_IDENT_TOKEN: case $tok.value of "block": return DISPLAY_BLOCK of "inline": return DISPLAY_INLINE of "inline-block": return DISPLAY_INLINE_BLOCK of "list-item": return DISPLAY_LIST_ITEM of "table-column": return DISPLAY_TABLE_COLUMN of "none": return DISPLAY_NONE else: return DISPLAY_INLINE return DISPLAY_INLINE func cssFontStyle(d: CSSDeclaration): CSSFontStyle = if isToken(d): let tok = getToken(d) if tok.tokenType == CSS_IDENT_TOKEN: case $tok.value of "normal": return FONTSTYLE_NORMAL of "italic": return FONTSTYLE_ITALIC of "oblique": return FONTSTYLE_OBLIQUE else: return FONTSTYLE_NORMAL return FONTSTYLE_NORMAL func getSpecifiedValue*(d: CSSDeclaration): CSSSpecifiedValue = case $d.name of "color": return CSSSpecifiedValue(t: RULE_COLOR, v: VALUE_COLOR, color: cssColor(d)) of "margin": return CSSSpecifiedValue(t: RULE_MARGIN, v: VALUE_LENGTH, length: cssLength(d)) of "margin-top": return CSSSpecifiedValue(t: RULE_MARGIN_TOP, v: VALUE_LENGTH, length: cssLength(d)) of "margin-left": return CSSSpecifiedValue(t: RULE_MARGIN_LEFT, v: VALUE_LENGTH, length: cssLength(d)) of "margin-bottom": return CSSSpecifiedValue(t: RULE_MARGIN_BOTTOM, v: VALUE_LENGTH, length: cssLength(d)) of "margin-right": return CSSSpecifiedValue(t: RULE_MARGIN_RIGHT, v: VALUE_LENGTH, length: cssLength(d)) of "font-style": return CSSSpecifiedValue(t: RULE_FONT_STYLE, v: VALUE_FONT_STYLE, fontStyle: cssFontStyle(d)) of "display": return CSSSpecifiedValue(t: RULE_DISPLAY, v: VALUE_DISPLAY, display: cssDisplay(d)) of "content": return CSSSpecifiedValue(t: RULE_CONTENT, v: VALUE_CONTENT, content: cssString(d)) func getComputedValue*(rule: CSSSpecifiedValue): CSSComputedValue = let inherit = rule.hasGlobalValue and (rule.globalValue == VALUE_INHERIT) let initial = rule.hasGlobalValue and (rule.globalValue == VALUE_INHERIT) let unset = rule.hasGlobalValue and (rule.globalValue == VALUE_INHERIT) let revert = rule.hasGlobalValue and (rule.globalValue == VALUE_INHERIT) case rule.v of VALUE_COLOR: return CSSComputedValue(t: rule.t, v: VALUE_COLOR, color: rule.color) of VALUE_LENGTH: return CSSComputedValue(t: rule.t, v: VALUE_LENGTH, length: rule.length) of VALUE_DISPLAY: return CSSComputedValue(t: rule.t, v: VALUE_DISPLAY, display: rule.display) of VALUE_FONT_STYLE: return CSSComputedValue(t: rule.t, v: VALUE_FONT_STYLE, fontstyle: rule.fontstyle) of VALUE_CONTENT: return CSSComputedValue(t: rule.t, v: VALUE_CONTENT, content: rule.content) of VALUE_NONE: return CSSComputedValue(t: rule.t, v: VALUE_NONE) func getValue*(vals: seq[CSSComputedValue], rule: CSSRuleType): CSSComputedValue = for val in vals: if val.t == rule: result = val func getComputedValue*(d: CSSDeclaration): CSSComputedValue = return getComputedValue(getSpecifiedValue(d)) proc applyProperty*(props: CSS2Properties, d: CSSDeclaration) = case $d.name of "color": props.color = cssColor(d) of "margin": let l = cssLength(d) props.margintop = l props.marginbottom = l props.marginleft = l props.marginright = l of "margin-top": props.margintop = cssLength(d) of "margin-left": props.marginleft = cssLength(d) of "margin-right": props.marginright = cssLength(d) of "margin-bottom": props.marginbottom = cssLength(d) of "font-style": props.fontStyle = cssFontStyle(d) of "display": props.display = cssDisplay(d) of "content": props.content = cssString(d) else: printc(d) #TODO