diff options
author | bptato <nincsnevem662@gmail.com> | 2022-10-19 11:24:45 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2022-10-19 11:25:56 +0200 |
commit | 08a758ed7a06e1bff8994c01d6c5d317d400ccf9 (patch) | |
tree | cb53cf39ebd323491715eb569cc509c00edcae91 /src/display/client.nim | |
parent | c4e2de9cd8cad7e28b33e68b1b76f9044fe510be (diff) | |
download | chawan-08a758ed7a06e1bff8994c01d6c5d317d400ccf9.tar.gz |
Implement tree buffers, fix a js bug, refactor
Diffstat (limited to 'src/display/client.nim')
-rw-r--r-- | src/display/client.nim | 593 |
1 files changed, 48 insertions, 545 deletions
diff --git a/src/display/client.nim b/src/display/client.nim index 82fc2299..1f15e907 100644 --- a/src/display/client.nim +++ b/src/display/client.nim @@ -1,11 +1,9 @@ import options import os import streams -import strutils import tables import terminal import times -import unicode import std/monotimes @@ -15,23 +13,21 @@ import display/pager import html/dom import html/htmlparser import io/buffer -import io/cell import io/lineedit import io/loader import io/request import io/term import js/javascript -import js/regex +import types/cookie import types/url import utils/twtstr type Client* = ref ClientObj ClientObj* = object - buffer*: Buffer + attrs: TermAttributes feednext: bool s: string - iserror: bool errormessage: string userstyle: CSSStylesheet loader: FileLoader @@ -40,11 +36,6 @@ type config: Config jsrt: JSRuntime jsctx: JSContext - regex: Option[Regex] - revsearch: bool - needsauth: bool - redirecturl: Option[Url] - cmdmode: bool timeoutid: int timeouts: Table[int, tuple[handler: proc(), time: int64]] added_timeouts: Table[int, tuple[handler: proc(), time: int64]] @@ -58,10 +49,6 @@ type lastbuf*: Buffer ibuf: string - ActionError* = object of IOError - LoadError* = object of ActionError - InterruptError* = object of LoadError - proc readChar(console: Console): char = if console.ibuf == "": return stdin.readChar() @@ -75,323 +62,26 @@ proc `=destroy`(client: var ClientObj) = free(client.jsrt) proc statusMode(client: Client) = - print(HVP(client.buffer.height + 1, 1)) + print(HVP(client.attrs.height + 1, 1)) print(EL()) -proc loadError(s: string) = - raise newException(LoadError, s) - -proc actionError(s: string) = - raise newException(ActionError, s) - -proc addBuffer(client: Client) = - if client.buffer == nil: - client.buffer = newBuffer() - else: - let oldnext = client.buffer.next - client.buffer.next = newBuffer() - if oldnext != nil: - oldnext.prev = client.buffer.next - client.buffer.next.prev = client.buffer - client.buffer.next.next = oldnext - client.buffer = client.buffer.next - client.buffer.loader = client.loader - client.buffer.userstyle = client.userstyle - -proc prevBuffer(client: Client) {.jsfunc.} = - if client.buffer.prev != nil: - client.buffer = client.buffer.prev - client.buffer.redraw = true - -proc nextBuffer(client: Client) {.jsfunc.} = - if client.buffer.next != nil: - client.buffer = client.buffer.next - client.buffer.redraw = true - -proc discardBuffer(buffer: Buffer) = - if buffer.next == nil and buffer.prev == nil: - actionError("Cannot discard last buffer!") - if buffer.sourcepair != nil: - buffer.sourcepair.sourcepair = nil - if buffer.next != nil: - buffer.next.prev = buffer.prev - if buffer.prev != nil: - buffer.prev.next = buffer.next - buffer.sourcepair = nil - buffer.next = nil - buffer.prev = nil - -proc discardBuffer(client: Client) {.jsfunc.} = - let old = client.buffer - if old.next != nil: - client.buffer = old.next - elif old.prev != nil: - client.buffer = old.prev - else: - actionError("Cannot discard last buffer!") - discardBuffer(old) - client.buffer.redraw = true - -proc setupBuffer(client: Client) = - let buffer = client.buffer - buffer.load() - buffer.render() - buffer.gotoAnchor() - buffer.redraw = true - -proc dupeBuffer(client: Client, location = none(URL)) {.jsfunc.} = - let prev = client.buffer - client.addBuffer() - client.buffer.contenttype = prev.contenttype - client.buffer.ispipe = prev.ispipe - client.buffer.istream = newStringStream(prev.source) - if location.issome: - client.buffer.location = location.get - else: - client.buffer.location = prev.location - client.buffer.document = prev.document - client.setupBuffer() - proc readPipe(client: Client, ctype: string) = - client.addBuffer() - client.buffer.contenttype = if ctype != "": ctype else: "text/plain" - client.buffer.ispipe = true - client.buffer.istream = newFileStream(stdin) - client.buffer.location = newURL("file://-") - client.buffer.load() + let buffer = newBuffer(client.config, client.loader) + buffer.contenttype = if ctype != "": ctype else: "text/plain" + buffer.ispipe = true + buffer.istream = newFileStream(stdin) + buffer.location = newURL("file://-") + client.pager.addBuffer(buffer) #TODO is this portable at all? if reopen(stdin, "/dev/tty", fmReadWrite): - client.setupBuffer() + buffer.setupBuffer() else: - client.buffer.drawBuffer() - -type Cookie = ref object of RootObj - name {.jsget.}: string - value {.jsget.}: string - expires {.jsget.}: int64 # unix time - maxAge {.jsget.}: int64 - secure {.jsget.}: bool - httponly {.jsget.}: bool - samesite {.jsget.}: bool - domain {.jsget.}: string - path {.jsget.}: string - -proc parseCookieDate(val: string): Option[DateTime] = - # cookie-date - const Delimiters = {'\t', ' '..'/', ';'..'@', '['..'`', '{'..'~'} - const NonDigit = Ascii + NonAscii - Digits - var foundTime = false - var foundDayOfMonth = false - var foundMonth = false - var foundYear = false - # date-token-list - var time: array[3, int] - var dayOfMonth: int - var month: int - var year: int - for dateToken in val.split(Delimiters): - if dateToken == "": continue # *delimiter - if not foundTime: - block timeBlock: # test for time - let hmsTime = dateToken.until(NonDigit - {':'}) - var i = 0 - for timeField in hmsTime.split(':'): - if i > 2: break timeBlock # too many time fields - # 1*2DIGIT - if timeField.len != 1 and timeField.len != 2: break timeBlock - var timeFields: array[3, int] - for c in timeField: - if c notin Digits: break timeBlock - timeFields[i] *= 10 - timeFields[i] += c.decValue - time = timeFields - inc i - if i != 3: break timeBlock - foundTime = true - continue - if not foundDayOfMonth: - block dayOfMonthBlock: # test for day-of-month - let digits = dateToken.until(NonDigit) - if digits.len != 1 and digits.len != 2: break dayOfMonthBlock - var n = 0 - for c in digits: - if c notin Digits: break dayOfMonthBlock - n *= 10 - n += c.decValue - dayOfMonth = n - foundDayOfMonth = true - continue - if not foundMonth: - block monthBlock: # test for month - if dateToken.len < 3: break monthBlock - case dateToken.substr(0, 2).toLower() - of "jan": month = 1 - of "feb": month = 2 - of "mar": month = 3 - of "apr": month = 4 - of "may": month = 5 - of "jun": month = 6 - of "jul": month = 7 - of "aug": month = 8 - of "sep": month = 9 - of "oct": month = 10 - of "nov": month = 11 - of "dec": month = 12 - else: break monthBlock - foundMonth = true - continue - if not foundYear: - block yearBlock: # test for year - let digits = dateToken.until(NonDigit) - if digits.len != 2 and digits.len != 4: break yearBlock - var n = 0 - for c in digits: - if c notin Digits: break yearBlock - n *= 10 - n += c.decValue - year = n - foundYear = true - continue - if not (foundDayOfMonth and foundMonth and foundYear and foundTime): return none(DateTime) - if dayOfMonth notin 0..31: return none(DateTime) - if year < 1601: return none(DateTime) - if time[0] > 23: return none(DateTime) - if time[1] > 59: return none(DateTime) - if time[2] > 59: return none(DateTime) - var dateTime = dateTime(year, Month(month), MonthdayRange(dayOfMonth), HourRange(time[0]), MinuteRange(time[1]), SecondRange(time[2])) - return some(dateTime) - -proc parseCookie(client: Client, str: string): Cookie {.jsfunc.} = - let cookie = new(Cookie) - var first = true - for part in str.split(';'): - if first: - cookie.name = part.until('=') - cookie.value = part.after('=') - first = false - continue - let part = percentDecode(part).strip(leading = true, trailing = false, AsciiWhitespace) - var n = 0 - for i in 0..part.high: - if part[i] == '=': - n = i - break - if n == 0: - continue - let key = part.substr(0, n - 1) - let val = part.substr(n + 1) - case key.toLower() - of "expires": - let date = parseCookieDate(val) - if date.issome: - cookie.expires = date.get.toTime().toUnix() - of "max-age": cookie.maxAge = parseInt64(val) - of "secure": cookie.secure = true - of "httponly": cookie.httponly = true - of "samesite": cookie.samesite = true - of "path": cookie.path = val - of "domain": cookie.domain = val - return cookie + buffer.load() + buffer.drawBuffer() proc doRequest(client: Client, req: Request): Response {.jsfunc.} = client.loader.doRequest(req) -# Load request in a new buffer. -var g_client: Client -proc gotoUrl(client: Client, request: Request, prevurl = none(URL), force = false, ctype = "") = - if force or prevurl.isnone or not prevurl.get.equals(request.url, true) or - prevurl.get.equals(request.url) or request.httpmethod != HTTP_GET: - let page = client.doRequest(request) - client.needsauth = page.status == 401 # Unauthorized - client.redirecturl = page.redirect - if page.body != nil: - client.addBuffer() - g_client = client - client.buffer.contenttype = if ctype != "": ctype else: page.contenttype - client.buffer.istream = page.body - client.buffer.location = request.url - client.setupBuffer() - else: - loadError("Couldn't load " & $request.url & " (" & $page.res & ")") - elif client.buffer != nil and prevurl.issome and prevurl.get.equals(request.url, true): - if client.buffer.hasAnchor(request.url.anchor): - client.dupeBuffer(request.url.some) - else: - loadError("Couldn't find anchor " & request.url.anchor) - -# Relative gotoUrl: either to prevurl, or if that's none, client.buffer.url. -proc gotoUrl(client: Client, url: string, prevurl = none(URL), force = false, ctype = "") = - var prevurl = prevurl - if prevurl.isnone and client.buffer != nil: - prevurl = client.buffer.location.some - let newurl = parseUrl(url, prevurl) - if newurl.isnone: - loadError("Invalid URL " & url) - client.gotoUrl(newRequest(newurl.get), prevurl, force, ctype) - -# When the user has passed a partial URL as an argument, they might've meant -# either: -# * file://$PWD/<file> -# * https://<url> -# So we attempt to load both, and see what works. -# (TODO: make this optional) -proc loadUrl(client: Client, url: string, ctype = "") = - let firstparse = parseUrl(url) - if firstparse.issome: - client.gotoUrl(newRequest(firstparse.get), none(Url), true, ctype) - else: - let cdir = parseUrl("file://" & getCurrentDir() & DirSep) - try: - # attempt to load local file - client.gotoUrl(url, cdir, true, ctype) - except LoadError: - try: - # attempt to load local file (this time percent encoded) - client.gotoUrl(percentEncode(url, LocalPathPercentEncodeSet), cdir, true, ctype) - except LoadError: - # attempt to load remote page - client.gotoUrl("https://" & url, none(Url), true, ctype) - -# Reload the page in a new buffer, then kill the previous buffer. -proc reloadPage(client: Client) {.jsfunc.} = - let buf = client.buffer - client.gotoUrl(newRequest(client.buffer.location), none(URL), true, client.buffer.contenttype) - discardBuffer(buf) - -# Open a URL prompt and visit the specified URL. -proc changeLocation(client: Client) {.jsfunc.} = - let buffer = client.buffer - var url = buffer.location.serialize(true) - client.statusMode() - let status = readLine("URL: ", url, buffer.width, config = client.config) - if status: - client.loadUrl(url) - -proc click(client: Client) {.jsfunc.} = - let req = client.buffer.click() - if req.issome: - client.gotoUrl(req.get, client.buffer.location.some) - -proc toggleSource*(client: Client) {.jsfunc.} = - let buffer = client.buffer - if buffer.sourcepair != nil: - client.buffer = buffer.sourcepair - client.buffer.redraw = true - else: - client.addBuffer() - client.buffer.sourcepair = client.buffer.prev - client.buffer.sourcepair.sourcepair = client.buffer - client.buffer.source = client.buffer.prev.source - client.buffer.streamclosed = true - client.buffer.location = client.buffer.sourcepair.location - client.buffer.ispipe = client.buffer.sourcepair.ispipe - let prevtype = client.buffer.prev.contenttype - if prevtype == "text/html": - client.buffer.contenttype = "text/plain" - else: - client.buffer.contenttype = "text/html" - client.setupBuffer() - proc interruptHandler(rt: JSRuntime, opaque: pointer): int {.cdecl.} = let client = cast[Client](opaque) try: @@ -433,184 +123,41 @@ proc command(client: Client, src: string) = restoreStdin() let previ = client.console.err.getPosition() client.command0(src) - g_client = client client.console.err.setPosition(previ) - if client.console.lastbuf == nil or client.console.lastbuf != client.buffer: - client.addBuffer() - client.buffer.istream = newStringStream(client.console.err.readAll()) #TODO - client.buffer.contenttype = "text/plain" - client.buffer.location = parseUrl("javascript:void(0);").get - client.console.lastbuf = client.buffer + if client.console.lastbuf == nil: + let buffer = newBuffer(client.config, client.loader) + buffer.istream = newStringStream(client.console.err.readAll()) #TODO + buffer.contenttype = "text/plain" + buffer.location = parseUrl("javascript:void(0);").get + client.console.lastbuf = buffer + client.pager.addBuffer(buffer) else: - client.buffer.istream = newStringStream(client.buffer.source & client.console.err.readAll()) - client.buffer.streamclosed = false - client.setupBuffer() - client.buffer.cursorLastLine() + client.console.lastbuf.istream = newStringStream(client.console.lastbuf.source & client.console.err.readAll()) + client.console.lastbuf.streamclosed = false + client.console.lastbuf.setupBuffer() + client.console.lastbuf.cursorLastLine() proc command(client: Client): bool {.jsfunc.} = var iput: string client.statusMode() - let status = readLine("COMMAND: ", iput, client.buffer.width, config = client.config) + let status = readLine("COMMAND: ", iput, client.attrs.width, config = client.config) if status: client.command(iput) return status proc commandMode(client: Client) {.jsfunc.} = - client.cmdmode = client.command() - -proc searchNext(client: Client) {.jsfunc.} = - if client.regex.issome: - if not client.revsearch: - discard client.buffer.cursorNextMatch(client.regex.get) - else: - discard client.buffer.cursorPrevMatch(client.regex.get) + client.pager.commandMode = client.command() -proc searchPrev(client: Client) {.jsfunc.} = - if client.regex.issome: - if not client.revsearch: - discard client.buffer.cursorPrevMatch(client.regex.get) - else: - discard client.buffer.cursorNextMatch(client.regex.get) - -proc search(client: Client) {.jsfunc.} = - client.statusMode() - var iput: string - let status = readLine("/", iput, client.buffer.width, config = client.config) - if status: - if iput.len != 0: - client.regex = compileSearchRegex(iput) - client.revsearch = false - client.searchNext() - -proc searchBack(client: Client) {.jsfunc.} = - client.statusMode() - var iput: string - let status = readLine("?", iput, client.buffer.width, config = client.config) - if status: - if iput.len != 0: - client.regex = compileSearchRegex(iput) - client.revsearch = true - client.searchNext() - -proc isearch(client: Client) {.jsfunc.} = - client.statusMode() - var iput: string - let cpos = client.buffer.cpos - var mark: Mark - template del_mark() = - if mark != nil: - client.buffer.removeMark(mark) - - let status = readLine("/", iput, client.buffer.width, {}, false, client.config, (proc(state: var LineState): bool = - del_mark - let regex = compileSearchRegex($state.news) - client.buffer.cpos = cpos - if regex.issome: - let match = client.buffer.cursorNextMatch(regex.get) - if match.success: - mark = client.buffer.addMark(match.x, match.y, match.str.width()) - client.buffer.redraw = true - client.buffer.refreshBuffer(true) - print(HVP(client.buffer.height + 1, 2)) - print(SGR()) - else: - del_mark - client.buffer.redraw = true - client.buffer.refreshBuffer(true) - print(HVP(client.buffer.height + 1, 2)) - print(SGR()) - return true - false - )) - - del_mark - client.buffer.redraw = true - client.buffer.refreshBuffer(true) - if status: - client.regex = compileSearchRegex(iput) - else: - client.buffer.cpos = cpos - -proc isearchBack(client: Client) {.jsfunc.} = - client.statusMode() - var iput: string - let cpos = client.buffer.cpos - var mark: Mark - template del_mark() = - if mark != nil: - client.buffer.removeMark(mark) - let status = readLine("?", iput, client.buffer.width, {}, false, client.config, (proc(state: var LineState): bool = - del_mark - let regex = compileSearchRegex($state.news) - client.buffer.cpos = cpos - if regex.issome: - let match = client.buffer.cursorPrevMatch(regex.get) - if match.success: - mark = client.buffer.addMark(match.x, match.y, match.str.width()) - client.buffer.redraw = true - client.buffer.refreshBuffer(true) - print(HVP(client.buffer.height + 1, 2)) - print(SGR()) - else: - del_mark - client.buffer.redraw = true - client.buffer.refreshBuffer(true) - print(HVP(client.buffer.height + 1, 2)) - print(SGR()) - return true - false - )) - del_mark - client.buffer.redraw = true - if status: - client.regex = compileSearchRegex(iput) - else: - client.buffer.cpos = cpos - -proc quit(client: Client) {.jsfunc.} = +proc quit(client: Client, code = 0) {.jsfunc.} = print(HVP(getTermAttributes().height, 0)) print(EL()) - quit(0) + quit(code) proc feedNext(client: Client) {.jsfunc.} = client.feednext = true -#TODO move this to a pager module or something -proc cursorLeft(client: Client) {.jsfunc.} = client.buffer.cursorLeft() -proc cursorDown(client: Client) {.jsfunc.} = client.buffer.cursorDown() -proc cursorUp(client: Client) {.jsfunc.} = client.buffer.cursorUp() -proc cursorRight(client: Client) {.jsfunc.} = client.buffer.cursorRight() -proc cursorLineBegin(client: Client) {.jsfunc.} = client.buffer.cursorLineBegin() -proc cursorLineEnd(client: Client) {.jsfunc.} = client.buffer.cursorLineEnd() -proc cursorNextWord(client: Client) {.jsfunc.} = client.buffer.cursorNextWord() -proc cursorPrevWord(client: Client) {.jsfunc.} = client.buffer.cursorPrevWord() -proc cursorNextLink(client: Client) {.jsfunc.} = client.buffer.cursorNextLink() -proc cursorPrevLink(client: Client) {.jsfunc.} = client.buffer.cursorPrevLink() -proc pageDown(client: Client) {.jsfunc.} = client.buffer.pageDown() -proc pageUp(client: Client) {.jsfunc.} = client.buffer.pageUp() -proc pageRight(client: Client) {.jsfunc.} = client.buffer.pageRight() -proc pageLeft(client: Client) {.jsfunc.} = client.buffer.pageLeft() -proc halfPageDown(client: Client) {.jsfunc.} = client.buffer.halfPageDown() -proc halfPageUp(client: Client) {.jsfunc.} = client.buffer.halfPageUp() -proc cursorFirstLine(client: Client) {.jsfunc.} = client.buffer.cursorFirstLine() -proc cursorLastLine(client: Client) {.jsfunc.} = client.buffer.cursorLastLine() -proc cursorTop(client: Client) {.jsfunc.} = client.buffer.cursorTop() -proc cursorMiddle(client: Client) {.jsfunc.} = client.buffer.cursorMiddle() -proc cursorBottom(client: Client) {.jsfunc.} = client.buffer.cursorBottom() -proc cursorLeftEdge(client: Client) {.jsfunc.} = client.buffer.cursorLeftEdge() -proc cursorVertMiddle(client: Client) {.jsfunc.} = client.buffer.cursorVertMiddle() -proc cursorRightEdge(client: Client) {.jsfunc.} = client.buffer.cursorRightEdge() -proc centerLine(client: Client) {.jsfunc.} = client.buffer.centerLine() -proc scrollDown(client: Client) {.jsfunc.} = client.buffer.scrollDown() -proc scrollUp(client: Client) {.jsfunc.} = client.buffer.scrollUp() -proc scrollLeft(client: Client) {.jsfunc.} = client.buffer.scrollLeft() -proc scrollRight(client: Client) {.jsfunc.} = client.buffer.scrollRight() -proc lineInfo(client: Client) {.jsfunc.} = client.buffer.lineInfo() -proc reshape(client: Client) {.jsfunc.} = client.buffer.reshape = true -proc redraw(client: Client) {.jsfunc.} = client.buffer.redraw = true - proc input(client: Client) = - if client.cmdmode: + if client.pager.commandMode: client.commandMode() return if not client.feednext: @@ -624,58 +171,16 @@ proc input(client: Client) = let action = getNormalAction(client.config, client.s) client.evalJSFree(action, "<command>") -proc followRedirect(client: Client) - -proc checkAuth(client: Client) = - if client.needsauth: - client.buffer.refreshBuffer() - client.statusMode() - var username = "" - let ustatus = readLine("Username: ", username, client.buffer.width, config = client.config) - if not ustatus: - client.needsauth = false - return - client.statusMode() - var password = "" - let pstatus = readLine("Password: ", password, client.buffer.width, hide = true, config = client.config) - if not pstatus: - client.needsauth = false - return - var url = client.buffer.location - url.username = username - url.password = password - var buf = client.buffer - client.gotoUrl(newRequest(url), prevurl = some(client.buffer.location)) - discardBuffer(buf) - client.followRedirect() - -proc followRedirect(client: Client) = - while client.redirecturl.issome: - client.statusMode() - print("Redirecting to ", $client.redirecturl.get) - stdout.flushFile() - client.buffer.refreshBuffer(true) - var buf = client.buffer - let redirecturl = client.redirecturl.get - client.redirecturl = none(Url) - client.gotoUrl(newRequest(redirecturl), prevurl = some(client.buffer.location)) - discardBuffer(buf) - if client.needsauth: - client.checkAuth() - proc inputLoop(client: Client) = while true: - g_client = client restoreStdin() - client.followRedirect() - client.checkAuth() - client.buffer.refreshBuffer() - if client.needsauth: # Unauthorized - client.checkAuth() - try: - client.input() - except ActionError as e: - client.buffer.setStatusMessage(e.msg) + client.pager.displayPage() + client.pager.followRedirect() + if client.pager.container != nil: + client.pager.container.buffer.refreshBuffer() + if client.pager.container.needsauth: # Unauthorized + client.pager.checkAuth() + client.input() #TODO this is dumb proc readFile(client: Client, path: string): string {.jsfunc.} = @@ -781,26 +286,22 @@ proc launchClient*(client: Client, pages: seq[string], ctype: string, dump: bool client.userstyle = client.config.stylesheet.parseStylesheet() if not stdin.isatty: client.readPipe(ctype) - try: - for page in pages: - client.loadUrl(page, ctype) - except LoadError as e: - eprint e.msg - quit(1) + for page in pages: + client.pager.loadURL(page, force = true, ctype = ctype) if stdout.isatty and not dump: when defined(posix): enableRawMode() client.inputLoop() else: - var buffer = client.buffer - while buffer.next != nil: - buffer = buffer.next - - buffer.drawBuffer() - while buffer.prev != nil: - buffer = buffer.prev - buffer.drawBuffer() + for msg in client.pager.status: + eprint msg + while client.pager.nextBuffer(): + discard + if client.pager.container != nil: + client.pager.container.buffer.drawBuffer() + while client.pager.prevBuffer(): + client.pager.container.buffer.drawBuffer() proc nimGCStats(client: Client): string {.jsfunc.} = return GC_getStatistics() @@ -827,13 +328,14 @@ proc newClient*(config: Config): Client = result.config = config result.loader = newFileLoader() result.console = newConsole() + result.attrs = getTermAttributes() + result.pager = newPager(config, result.attrs, result.loader) let rt = newJSRuntime() rt.setInterruptHandler(interruptHandler, cast[pointer](result)) let ctx = rt.newJSContext() result.jsrt = rt result.jsctx = ctx var global = ctx.getGlobalObject() - ctx.registerType(Cookie) ctx.registerType(Client, asglobal = true) global.setOpaque(result) ctx.setProperty(global.val, "client", global.val) @@ -841,6 +343,7 @@ proc newClient*(config: Config): Client = ctx.registerType(Console) + ctx.addCookieModule() ctx.addUrlModule() ctx.addDOMModule() ctx.addHTMLModule() |