var linkCounter: int proc link*(id: int): VNode = result = newVNode(VNodeKind.anchor) result.setAttr("href", "#") inc linkCounter result.setAttr("id", $linkCounter & ":" & $id) proc link*(action: EventHandler): VNode = result = newVNode(VNodeKind.anchor) result.setAttr("href", "#") result.setOnclick action when false: proc button*(caption: cstring; action: EventHandler; disabled=false): VNode = result = newVNode(VNodeKind.button) result.add text(caption) if action != nil: result.setOnClick action if disabled: result.setAttr("disabled", "true") proc select*(choices: openarray[cstring]): VNode = result = newVNode(VNodeKind.select) var i = 0 for c in choices: result.add tree(VNodeKind.option, [(cstring"value", toCstr(i))], text(c)) inc i proc select*(choices: openarray[(int, cstring)]): VNode = result = newVNode(VNodeKind.select) for c in choices: result.add tree(VNodeKind.option, [(cstring"value", toCstr(c[0]))], text(c[1])) var radioCounter: int proc radio*(choices: openarray[(int, cstring)]): VNode = result = newVNode(VNodeKind.fieldset) var i = 0 inc radioCounter for c in choices: let id = cstring"radio_" & c[1] & toCstr(i) var kid = tree(VNodeKind.input, [(cstring"type", cstring"radio"), (cstring"id", id), (cstring"name", cstring"radio" & toCStr(radioCounter)), (cstring"value", toCStr(c[0]))]) if i == 0: kid.setAttr(cstring"checked", cstring"checked") var lab = tree(VNodeKind.label, [(cstring"for", id)], text(c[1])) kid.add lab result.add kid inc i proc tag*(kind: VNodeKind; id=cstring(nil), class=cstring(nil)): VNode = result = newVNode(kind) result.id = id result.class = class proc tdiv*(id=cstring(nil), class=cstring(nil)): VNode = tag(VNodeKind.tdiv, id, class) proc span*(id=cstring(nil), class=cstring(nil)): VNode = tag(VNodeKind.span, id, class) proc valueAsInt*(e: Node): int = parseInt(e.value) proc th*(s: cstring): VNode = result = newVNode(VNodeKind.th) result.add text(s) proc td*(s: string): VNode = result = newVNode(VNodeKind.td) result.add text(s) proc td*(s: VNode): VNode = result = newVNode(VNodeKind.td) result.add s proc td*(class: cstring; s: VNode): VNode = result = newVNode(VNodeKind.td) result.add s result.class = class proc table*(class=cstring(nil), kids: varargs[VNode]): VNode = result = tag(VNodeKind.table, nil, class) for k in kids: result.add k proc tr*(kids: varargs[VNode]): VNode = result = newVNode(VNodeKind.tr) for k in kids: if k.kind in {VNodeKind.td, VNodeKind.th}: result.add k else: result.add td(k) proc suffix*(s, prefix: cstring): cstring = if s.startsWith(prefix): result = s.substr(prefix.len) else: kout(cstring"bug! " & s & cstring" does not start with " & prefix) proc suffixAsInt*(s, prefix: cstring): int = parseInt(suffix(s, prefix)) #proc ceil(f: float): int {.importc: "Math.ceil", nodecl.} proc realtimeInput*(val: cstring; action: EventHandler): VNode = var timer: Timeout proc onkeyup(ev: Event; n: VNode) = proc wrapper() = keyeventBody() if timer != nil: clearTimeout(timer) timer = setTimeout(wrapper, 400) result = tree(VNodeKind.input, [(cstring"type", cstring"text")]) result.value = val result.addEventListener(EventKind.onkeyup, onkeyup) proc enterInput*(id, val: cstring; action: EventHandler): VNode = proc onkeyup(ev: Event; n: VNode) = if ev.keyCode == 13: keyeventBody() result = tree(VNodeKind.input, [(cstring"type", cstring"text")]) result.id = id result.value = val result.addEventListener(EventKind.onkeyup, onkeyup) proc setOnEnter*(n: VNode; action: EventHandler) = proc onkeyup(ev: Event; n: VNode) = if ev.keyCode == 13: keyeventBody() n.addEventListener(EventKind.onkeyup, onkeyup) proc setOnscroll*(action: proc(min, max: VKey; diff: int)) = var oldY = window.pageYOffset proc wrapper(ev: Event) = let dir = window.pageYOffset - oldY if dir == 0: return var a = VKey high(int) var b = VKey 0 var h, count: int document.visibleKeys(a, b, h, count) let avgh = h / count let diff = toInt(dir.float / avgh) if diff != 0: oldY = window.pageYOffset action(a, b, diff) redraw() document.addEventListener("scroll", wrapper) when false: var plugins {.exportc.}: seq[(string, proc())] = @[] proc onInput(val: cstring) = kout val if val == "dyn": kout(plugins.len) if plugins.len > 0: plugins[0][1]() var images: seq[cstring] = @[cstring"a", "b", "c", "d"] proc carousel*(): VNode = var currentIndex = 0 proc next(ev: Event; n: VNode) = currentIndex = (currentIndex + 1) mod images.len proc prev(ev: Event; n: VNode) = currentIndex = (currentIndex - 1) mod images.len result = buildHtml(tdiv): text images[currentIndex] button(onclick = next): text "Next" button(onclick = prev): text "Previous" #proc targetElem*(e: Event): Element = cast[Element](e.target) #proc getElementsByClassName*(cls: cstring): seq[Element] {.importc: # "document.getElementsByClassName", nodecl.} #proc textContent(e: Node): cstring {. # importcpp: "#.textContent", nodecl.} proc isElementInViewport(el: Node; h: var int): bool = let rect = el.getBoundingClientRect() h = rect.bottom - rect.top result = rect.top >= 0 and rect.left >= 0 and rect.bottom <= clientHeight() and rect.right <= clientWidth() proc visibleKeys(e: Node; a, b: var VKey; h, count: var int) = # we only care about nodes that have a key: var hh = 0 # do not recurse if there is a 'key' field already: if e.key >= 0: if isElementInViewport(e, hh): inc count inc h, hh a = min(a, e.key) b = max(b, e.key) else: for i in 0..