summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/js/dom.nim889
-rw-r--r--tools/dochack/dochack.nim45
-rw-r--r--tools/dochack/karax.nim344
3 files changed, 797 insertions, 481 deletions
diff --git a/lib/js/dom.nim b/lib/js/dom.nim
index 668dee822..945d6cda1 100644
--- a/lib/js/dom.nim
+++ b/lib/js/dom.nim
@@ -13,6 +13,9 @@
 when not defined(js) and not defined(Nimdoc):
   {.error: "This module only works on the JavaScript platform".}
 
+const
+  DomApiVersion* = 3 ## the version of DOM API we try to follow. No guarantees though.
+
 type
   EventTarget* = ref EventTargetObj
   EventTargetObj {.importc.} = object of RootObj
@@ -37,6 +40,38 @@ type
     onsubmit*: proc (event: Event) {.nimcall.}
     onunload*: proc (event: Event) {.nimcall.}
 
+  # https://developer.mozilla.org/en-US/docs/Web/Events
+  DomEvent* {.pure.} = enum
+    Abort = "abort",
+    BeforeInput = "beforeinput",
+    Blur = "blur",
+    Click = "click",
+    CompositionEnd = "compositionend",
+    CompositionStart = "compositionstart",
+    CompositionUpdate = "compositionupdate",
+    DblClick = "dblclick",
+    Error = "error",
+    Focus = "focus",
+    FocusIn = "focusin",
+    FocusOut = "focusout",
+    Input = "input",
+    KeyDown = "keydown",
+    KeyPress = "keypress",
+    KeyUp = "keyup",
+    Load = "load",
+    MouseDown = "mousedown",
+    MouseEnter = "mouseenter",
+    MouseLeave = "mouseleave",
+    MouseMove = "mousemove",
+    MouseOut = "mouseout",
+    MouseOver = "mouseover",
+    MouseUp = "mouseup",
+    Resize = "resize",
+    Scroll = "scroll",
+    Select = "select",
+    Unload = "unload",
+    Wheel = "wheel"
+
   Window* = ref WindowObj
   WindowObj {.importc.} = object of EventTargetObj
     document*: Document
@@ -45,24 +80,19 @@ type
     location*: Location
     closed*: bool
     defaultStatus*: cstring
-    devicePixelRatio*: float
     innerHeight*, innerWidth*: int
-    locationbar*: ref TLocationBar
-    menubar*: ref TMenuBar
+    locationbar*: ref LocationBar
+    menubar*: ref MenuBar
     name*: cstring
     outerHeight*, outerWidth*: int
     pageXOffset*, pageYOffset*: int
-    personalbar*: ref TPersonalBar
-    scrollbars*: ref TScrollBars
-    scrollX*: float
-    scrollY*: float
-    statusbar*: ref TStatusBar
+    personalbar*: ref PersonalBar
+    scrollbars*: ref ScrollBars
+    statusbar*: ref StatusBar
     status*: cstring
-    toolbar*: ref TToolBar
-    frames*: seq[TFrame]
+    toolbar*: ref ToolBar
+    frames*: seq[Frame]
     screen*: Screen
-    performance*: Performance
-    onpopstate*: proc (event: Event)
 
   Frame* = ref FrameObj
   FrameObj {.importc.} = object of WindowObj
@@ -135,10 +165,8 @@ type
     name*: cstring
     readOnly*: bool
     options*: seq[OptionElement]
+    selectedOptions*: seq[OptionElement]
     clientWidth*, clientHeight*: int
-
-  # https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
-  HtmlElement* = ref object of Element
     contentEditable*: cstring
     isContentEditable*: bool
     dir*: cstring
@@ -147,6 +175,87 @@ type
     offsetLeft*: int
     offsetTop*: int
 
+  # https://developer.mozilla.org/en-US/docs/Web/API/ValidityState
+  ValidityState* = ref ValidityStateObj
+  ValidityStateObj {.importc.} = object
+    badInput*: bool
+    customError*: bool
+    patternMismatch*: bool
+    rangeOverflow*: bool
+    rangeUnderflow*: bool
+    stepMismatch*: bool
+    tooLong*: bool
+    tooShort*: bool
+    typeMismatch*: bool
+    valid*: bool
+    valueMissing*: bool
+
+  # https://developer.mozilla.org/en-US/docs/Web/API/Blob
+  Blob* = ref BlobObj
+  BlobObj {.importc.} = object of RootObj
+    size*: int
+    `type`*: cstring
+
+  # https://developer.mozilla.org/en-US/docs/Web/API/File
+  File* = ref FileObj
+  FileObj {.importc.} = object of Blob
+    lastModified*: int
+    name*: cstring
+
+  # https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement
+  InputElement* = ref InputElementObj
+  InputElementObj {.importc.} = object of Element
+    # Properties related to the parent form
+    formAction*: cstring
+    formEncType*: cstring
+    formMethod*: cstring
+    formNoValidate*: bool
+    formTarget*: cstring
+
+    # Properties that apply to any type of input element that is not hidden
+    `type`*: cstring
+    autofocus*: bool
+    required*: bool
+    value*: cstring
+    validity*: ValidityState
+    validationMessage*: cstring
+    willValidate*: bool
+
+    # Properties that apply only to elements of type "checkbox" or "radio"
+    indeterminate*: bool
+
+    # Properties that apply only to elements of type "image"
+    alt*: cstring
+    height*: cstring
+    src*: cstring
+    width*: cstring
+
+    # Properties that apply only to elements of type "file"
+    accept*: cstring
+    files*: seq[Blob]
+
+    # Properties that apply only to text/number-containing or elements
+    autocomplete*: cstring
+    maxLength*: int
+    size*: int
+    pattern*: cstring
+    placeholder*: cstring
+    min*: cstring
+    max*: cstring
+    selectionStart*: int
+    selectionEnd*: int
+    selectionDirection*: cstring
+
+    # Properties not yet categorized
+    dirName*: cstring
+    accessKey*: cstring
+    list*: Element
+    multiple*: bool
+    labels*: seq[Element]
+    step*: cstring
+    valueAsDate*: cstring
+    valueAsNumber*: float
+
   LinkElement* = ref LinkObj
   LinkObj {.importc.} = object of ElementObj
     target*: cstring
@@ -176,19 +285,19 @@ type
     text*: cstring
     value*: cstring
 
-  TextAreaElement* = ref object of ElementObj
-    value*: cstring
-    selectionStart*, selectionEnd*: int
-    selectionDirection*: cstring
-    rows*, cols*: int
-
+  # https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement
   FormElement* = ref FormObj
   FormObj {.importc.} = object of ElementObj
+    acceptCharset*: cstring
     action*: cstring
+    autocomplete*: cstring
+    elements*: seq[Element]
     encoding*: cstring
+    enctype*: cstring
+    length*: int
     `method`*: cstring
+    noValidate*: bool
     target*: cstring
-    elements*: seq[Element]
 
   ImageElement* = ref ImageObj
   ImageObj {.importc.} = object of ElementObj
@@ -264,8 +373,6 @@ type
     minHeight*: cstring
     minWidth*: cstring
     overflow*: cstring
-    overflowX*: cstring
-    overflowY*: cstring
     padding*: cstring
     paddingBottom*: cstring
     paddingLeft*: cstring
@@ -273,6 +380,7 @@ type
     paddingTop*: cstring
     pageBreakAfter*: cstring
     pageBreakBefore*: cstring
+    pointerEvents*: cstring
     position*: cstring
     right*: cstring
     scrollbar3dLightColor*: cstring
@@ -288,6 +396,7 @@ type
     textDecoration*: cstring
     textIndent*: cstring
     textTransform*: cstring
+    transform*: cstring
     top*: cstring
     verticalAlign*: cstring
     visibility*: cstring
@@ -295,55 +404,467 @@ type
     wordSpacing*: cstring
     zIndex*: int
 
-  # TODO: A lot of the fields in Event belong to a more specific type of event.
-  # TODO: Should we clean this up?
+  EventPhase* = enum
+    None = 0,
+    CapturingPhase,
+    AtTarget,
+    BubblingPhase
+
+  # https://developer.mozilla.org/en-US/docs/Web/API/Event
   Event* = ref EventObj
   EventObj {.importc.} = object of RootObj
+    bubbles*: bool
+    cancelBubble*: bool
+    cancelable*: bool
+    composed*: bool
+    currentTarget*: Node
+    defaultPrevented*: bool
+    eventPhase*: int
     target*: Node
-    altKey*, ctrlKey*, shiftKey*: bool
+    `type`*: cstring
+    isTrusted*: bool
+
+  # https://developer.mozilla.org/en-US/docs/Web/API/UIEvent
+  UIEvent* = ref UIEventObj
+  UIEventObj {.importc.} = object of Event
+    detail*: int64
+    view*: Window
+
+  # https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
+  KeyboardEvent* = ref KeyboardEventObj
+  KeyboardEventObj {.importc.} = object of UIEvent
+    altKey*, ctrlKey*, metaKey*, shiftKey*: bool
+    code*: cstring
+    isComposing*: bool
+    key*: cstring
+    keyCode*: int
+    location*: int
+
+  # https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
+  KeyboardEventKey* {.pure.} = enum
+    # Modifier keys
+    Alt,
+    AltGraph,
+    CapsLock,
+    Control,
+    Fn,
+    FnLock,
+    Hyper,
+    Meta,
+    NumLock,
+    ScrollLock,
+    Shift,
+    Super,
+    Symbol,
+    SymbolLock,
+
+    # Whitespace keys
+    ArrowDown,
+    ArrowLeft,
+    ArrowRight,
+    ArrowUp,
+    End,
+    Home,
+    PageDown,
+    PageUp,
+
+    # Editing keys
+    Backspace,
+    Clear,
+    Copy,
+    CrSel,
+    Cut,
+    Delete,
+    EraseEof,
+    ExSel,
+    Insert,
+    Paste,
+    Redo,
+    Undo,
+
+    # UI keys
+    Accept,
+    Again,
+    Attn,
+    Cancel,
+    ContextMenu,
+    Escape,
+    Execute,
+    Find,
+    Finish,
+    Help,
+    Pause,
+    Play,
+    Props,
+    Select,
+    ZoomIn,
+    ZoomOut,
+
+    # Device keys
+    BrigtnessDown,
+    BrigtnessUp,
+    Eject,
+    LogOff,
+    Power,
+    PowerOff,
+    PrintScreen,
+    Hibernate,
+    Standby,
+    WakeUp,
+
+    # Common IME keys
+    AllCandidates,
+    Alphanumeric,
+    CodeInput,
+    Compose,
+    Convert,
+    Dead,
+    FinalMode,
+    GroupFirst,
+    GroupLast,
+    GroupNext,
+    GroupPrevious,
+    ModeChange,
+    NextCandidate,
+    NonConvert,
+    PreviousCandidate,
+    Process,
+    SingleCandidate,
+
+    # Korean keyboards only
+    HangulMode,
+    HanjaMode,
+    JunjaMode,
+
+    # Japanese keyboards only
+    Eisu,
+    Hankaku,
+    Hiragana,
+    HiraganaKatakana,
+    KanaMode,
+    KanjiMode,
+    Katakana,
+    Romaji,
+    Zenkaku,
+    ZenkakuHanaku,
+
+    # Function keys
+    F1,
+    F2,
+    F3,
+    F4,
+    F5,
+    F6,
+    F7,
+    F8,
+    F9,
+    F10,
+    F11,
+    F12,
+    F13,
+    F14,
+    F15,
+    F16,
+    F17,
+    F18,
+    F19,
+    F20,
+    Soft1,
+    Soft2,
+    Soft3,
+    Soft4,
+
+    # Phone keys
+    AppSwitch,
+    Call,
+    Camera,
+    CameraFocus,
+    EndCall,
+    GoBack,
+    GoHome,
+    HeadsetHook,
+    LastNumberRedial,
+    Notification,
+    MannerMode,
+    VoiceDial,
+
+    # Multimedia keys
+    ChannelDown,
+    ChannelUp,
+    MediaFastForward,
+    MediaPause,
+    MediaPlay,
+    MediaPlayPause,
+    MediaRecord,
+    MediaRewind,
+    MediaStop,
+    MediaTrackNext,
+    MediaTrackPrevious,
+
+    # Audio control keys
+    AudioBalanceLeft,
+    AudioBalanceRight,
+    AudioBassDown,
+    AudioBassBoostDown,
+    AudioBassBoostToggle,
+    AudioBassBoostUp,
+    AudioBassUp,
+    AudioFaderFront,
+    AudioFaderRear,
+    AudioSurroundModeNext,
+    AudioTrebleDown,
+    AudioTrebleUp,
+    AudioVolumeDown,
+    AUdioVolumeMute,
+    AudioVolumeUp,
+    MicrophoneToggle,
+    MicrophoneVolumeDown,
+    MicrophoneVolumeMute,
+    MicrophoneVolumeUp,
+
+    # TV control keys
+    TV,
+    TV3DMode,
+    TVAntennaCable,
+    TVAudioDescription,
+    TVAudioDescriptionMixDown,
+    TVAudioDescriptionMixUp,
+    TVContentsMenu,
+    TVDataService,
+    TVInput,
+    TVInputComponent1,
+    TVInputComponent2,
+    TVInputComposite1,
+    TVInputComposite2,
+    TVInputHDMI1,
+    TVInputHDMI2,
+    TVInputHDMI3,
+    TVInputHDMI4,
+    TVInputVGA1,
+    TVMediaContext,
+    TVNetwork,
+    TVNumberEntry,
+    TVPower,
+    TVRadioService,
+    TVSatellite,
+    TVSatelliteBS,
+    TVSatelliteCS,
+    TVSatelliteToggle,
+    TVTerrestrialAnalog,
+    TVTerrestrialDigital,
+    TVTimer,
+
+    # Media controller keys
+    AVRInput,
+    AVRPower,
+    ColorF0Red,
+    ColorF1Green,
+    ColorF2Yellow,
+    ColorF3Blue,
+    ColorF4Grey,
+    ColorF5Brown,
+    ClosedCaptionToggle,
+    Dimmer,
+    DisplaySwap,
+    DVR,
+    Exit,
+    FavoriteClear0,
+    FavoriteClear1,
+    FavoriteClear2,
+    FavoriteClear3,
+    FavoriteRecall0,
+    FavoriteRecall1,
+    FavoriteRecall2,
+    FavoriteRecall3,
+    FavoriteStore0,
+    FavoriteStore1,
+    FavoriteStore2,
+    FavoriteStore3,
+    Guide,
+    GuideNextDay,
+    GuidePreviousDay,
+    Info,
+    InstantReplay,
+    Link,
+    ListProgram,
+    LiveContent,
+    Lock,
+    MediaApps,
+    MediaAudioTrack,
+    MediaLast,
+    MediaSkipBackward,
+    MediaSkipForward,
+    MediaStepBackward,
+    MediaStepForward,
+    MediaTopMenu,
+    NavigateIn,
+    NavigateNext,
+    NavigateOut,
+    NavigatePrevious,
+    NextFavoriteChannel,
+    NextUserProfile,
+    OnDemand,
+    Pairing,
+    PinPDown,
+    PinPMove,
+    PinPUp,
+    PlaySpeedDown,
+    PlaySpeedReset,
+    PlaySpeedUp,
+    RandomToggle,
+    RcLowBattery,
+    RecordSpeedNext,
+    RfBypass,
+    ScanChannelsToggle,
+    ScreenModeNext,
+    Settings,
+    SplitScreenToggle,
+    STBInput,
+    STBPower,
+    Subtitle,
+    Teletext,
+    VideoModeNext,
+    Wink,
+    ZoomToggle,
+
+    # Speech recognition keys
+    SpeechCorrectionList,
+    SpeechInputToggle,
+
+    # Document keys
+    Close,
+    New,
+    Open,
+    Print,
+    Save,
+    SpellCheck,
+    MailForward,
+    MailReply,
+    MailSend,
+
+    # Application selector keys
+    LaunchCalculator,
+    LaunchCalendar,
+    LaunchContacts,
+    LaunchMail,
+    LaunchMediaPlayer,
+    LaunchMusicPlayer,
+    LaunchMyComputer,
+    LaunchPhone,
+    LaunchScreenSaver,
+    LaunchSpreadsheet,
+    LaunchWebBrowser,
+    LaunchWebCam,
+    LaunchWordProcessor,
+    LaunchApplication1,
+    LaunchApplication2,
+    LaunchApplication3,
+    LaunchApplication4,
+    LaunchApplication5,
+    LaunchApplication6,
+    LaunchApplication7,
+    LaunchApplication8,
+    LaunchApplication9,
+    LaunchApplication10,
+    LaunchApplication11,
+    LaunchApplication12,
+    LaunchApplication13,
+    LaunchApplication14,
+    LaunchApplication15,
+    LaunchApplication16,
+
+    # Browser control keys
+    BrowserBack,
+    BrowserFavorites,
+    BrowserForward,
+    BrowserHome,
+    BrowserRefresh,
+    BrowserSearch,
+    BrowserStop,
+
+    # Numeric keypad keys
+    Key11,
+    Key12,
+    Separator
+
+  MouseButtons* = enum
+    NoButton = 0,
+    PrimaryButton = 1,
+    SecondaryButton = 2,
+    AuxilaryButton = 4,
+    FourthButton = 8,
+    FifthButton = 16
+
+  # https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+  MouseEvent* = ref MouseEventObj
+  MouseEventObj {.importc.} = object of UIEvent
+    altKey*, ctrlKey*, metaKey*, shiftKey*: bool
     button*: int
+    buttons*: int
     clientX*, clientY*: int
-    keyCode*: int
-    layerX*, layerY*: int
-    modifiers*: int
-    ALT_MASK*, CONTROL_MASK*, SHIFT_MASK*, META_MASK*: int
+    movementX*, movementY*: int
     offsetX*, offsetY*: int
     pageX*, pageY*: int
+    relatedTarget*: EventTarget
+    #region*: cstring
     screenX*, screenY*: int
-    which*: int
-    `type`*: cstring
     x*, y*: int
-    ABORT*: int
-    BLUR*: int
-    CHANGE*: int
-    CLICK*: int
-    DBLCLICK*: int
-    DRAGDROP*: int
-    ERROR*: int
-    FOCUS*: int
-    KEYDOWN*: int
-    KEYPRESS*: int
-    KEYUP*: int
-    LOAD*: int
-    MOUSEDOWN*: int
-    MOUSEMOVE*: int
-    MOUSEOUT*: int
-    MOUSEOVER*: int
-    MOUSEUP*: int
-    MOVE*: int
-    RESET*: int
-    RESIZE*: int
-    SELECT*: int
-    SUBMIT*: int
-    UNLOAD*: int
+
+  DataTransferItemKind* {.pure.} = enum
+    File = "file",
+    String = "string"
+
+  # https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem
+  DataTransferItem* = ref DataTransferItemObj
+  DataTransferItemObj {.importc.} = object of RootObj
+    kind*: cstring
+    `type`*: cstring
+
+  # https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer
+  DataTransfer* = ref DataTransferObj
+  DataTransferObj {.importc.} = object of RootObj
+    dropEffect*: cstring
+    effectAllowed*: cstring
+    files*: seq[Element]
+    items*: seq[DataTransferItem]
+    types*: seq[cstring]
+
+  DataTransferDropEffect* {.pure.} = enum
+    None = "none",
+    Copy = "copy",
+    Link = "link",
+    Move = "move"
+
+  DataTransferEffectAllowed* {.pure.} = enum
+    None = "none",
+    Copy = "copy",
+    CopyLink = "copyLink",
+    CopyMove = "copyMove",
+    Link = "link",
+    LinkMove = "linkMove",
+    Move = "move",
+    All = "all",
+    Uninitialized = "uninitialized"
+
+  DragEventTypes* = enum
+    Drag = "drag",
+    DragEnd = "dragend",
+    DragEnter = "dragenter",
+    DragExit = "dragexit",
+    DragLeave = "dragleave",
+    DragOver = "dragover",
+    DragStart = "dragstart",
+    Drop = "drop"
+
+  # https://developer.mozilla.org/en-US/docs/Web/API/DragEvent
+  DragEvent* {.importc.} = object of MouseEvent
+    dataTransfer*: DataTransfer
 
   TouchList* {.importc.} = ref object of RootObj
     length*: int
 
-  TouchEvent* {.importc.} = ref object of Event
-    changedTouches*, targetTouches*, touches*: TouchList
-
-  Touch* {.importc.} = ref object of RootObj
+  Touch* = ref TouchObj
+  TouchObj {.importc.} = object of RootObj
     identifier*: int
     screenX*, screenY*, clientX*, clientY*, pageX*, pageY*: int
     target*: Element
@@ -351,6 +872,10 @@ type
     rotationAngle*: int
     force*: float
 
+  TouchEvent* = ref TouchEventObj
+  TouchEventObj {.importc.} = object of UIEvent
+    changedTouches*, targetTouches*, touches*: seq[Touch]
+
   Location* = ref LocationObj
   LocationObj {.importc.} = object of RootObj
     hash*: cstring
@@ -375,26 +900,26 @@ type
     language*: cstring
     platform*: cstring
     userAgent*: cstring
-    mimeTypes*: seq[ref TMimeType]
+    mimeTypes*: seq[ref MimeType]
 
-  TPlugin* {.importc.} = object of RootObj
+  Plugin* {.importc.} = object of RootObj
     description*: cstring
     filename*: cstring
     name*: cstring
 
-  TMimeType* {.importc.} = object of RootObj
+  MimeType* {.importc.} = object of RootObj
     description*: cstring
-    enabledPlugin*: ref TPlugin
+    enabledPlugin*: ref Plugin
     suffixes*: seq[cstring]
     `type`*: cstring
 
-  TLocationBar* {.importc.} = object of RootObj
+  LocationBar* {.importc.} = object of RootObj
     visible*: bool
-  TMenuBar* = TLocationBar
-  TPersonalBar* = TLocationBar
-  TScrollBars* = TLocationBar
-  TToolBar* = TLocationBar
-  TStatusBar* = TLocationBar
+  MenuBar* = LocationBar
+  PersonalBar* = LocationBar
+  ScrollBars* = LocationBar
+  ToolBar* = LocationBar
+  StatusBar* = LocationBar
 
   Screen = ref ScreenObj
   ScreenObj {.importc.} = object of RootObj
@@ -405,62 +930,120 @@ type
     pixelDepth*: int
     width*: int
 
-  TTimeOut* {.importc.} = object of RootObj
-  TInterval* {.importc.} = object of RootObj
+  TimeOut* {.importc.} = ref object of RootObj
+  Interval* {.importc.} = object of RootObj
 
   AddEventListenerOptions* = object
     capture*: bool
     once*: bool
     passive*: bool
 
-  BoundingRect* {.importc.} = ref object
-    top*, bottom*, left*, right*, x*, y*, width*, height*: float
+proc id*(n: Node): cstring {.importcpp: "#.id", nodecl.}
+proc `id=`*(n: Node; x: cstring) {.importcpp: "#.id = #", nodecl.}
+proc class*(n: Node): cstring {.importcpp: "#.className", nodecl.}
+proc `class=`*(n: Node; v: cstring) {.importcpp: "#.className = #", nodecl.}
+
+proc value*(n: Node): cstring {.importcpp: "#.value", nodecl.}
+proc `value=`*(n: Node; v: cstring) {.importcpp: "#.value = #", nodecl.}
+
+proc `disabled=`*(n: Node; v: bool) {.importcpp: "#.disabled = #", nodecl.}
+
+when defined(nodejs):
+  # we provide a dummy DOM for nodejs for testing purposes
+  proc len*(x: Node): int = x.childNodes.len
+  proc `[]`*(x: Node; idx: int): Element =
+    assert idx >= 0 and idx < x.childNodes.len
+    result = cast[Element](x.childNodes[idx])
+
+  var document* = Document(nodeType: DocumentNode)
 
-  PerformanceMemory* {.importc.} = ref object
-    jsHeapSizeLimit*: float
-    totalJSHeapSize*: float
-    usedJSHeapSize*: float
-
-  PerformanceTiming* {.importc.} = ref object
-    connectStart*: float
-    domComplete*: float
-    domContentLoadedEventEnd*: float
-    domContentLoadedEventStart*: float
-    domInteractive*: float
-    domLoading*: float
-    domainLookupEnd*: float
-    domainLookupStart*: float
-    fetchStart*: float
-    loadEventEnd*: float
-    loadEventStart*: float
-    navigationStart*: float
-    redirectEnd*: float
-    redirectStart*: float
-    requestStart*: float
-    responseEnd*: float
-    responseStart*: float
-    secureConnectionStart*: float
-    unloadEventEnd*: float
-    unloadEventStart*: float
-
-  Performance* {.importc.} = ref object
-    memory*: PerformanceMemory
-    timing*: PerformanceTiming
+  proc getElem(x: Element; id: cstring): Element =
+    if x.id == id: return x
+    for i in 0..<x.len:
+      result = getElem(x[i], id)
+      if result != nil: return result
+
+  proc getElementById*(doc: Document; id: cstring): Element =
+    getElem(doc.body, id)
+  proc getElementById*(id: cstring): Element = document.getElementById(id)
+
+  proc appendChild*(parent, n: Node) =
+    n.parentNode = parent
+    parent.childNodes.add n
+
+  proc replaceChild*(parent, newNode, oldNode: Node) =
+    newNode.parentNode = parent
+    oldNode.parentNode = nil
+    var i = 0
+    while i < parent.len:
+      if Node(parent[i]) == oldNode:
+        parent.childNodes[i] = newNode
+        return
+      inc i
+    doAssert false, "old node not in node list"
+
+  proc removeChild*(parent, child: Node) =
+    child.parentNode = nil
+    var i = 0
+    while i < parent.len:
+      if Node(parent[i]) == child:
+        parent.childNodes.delete(i)
+        return
+      inc i
+    doAssert false, "old node not in node list"
+
+  proc insertBefore*(parent, newNode, before: Node) =
+    appendChild(parent, newNode)
+    var i = 0
+    while i < parent.len-1:
+      if Node(parent[i]) == before:
+        for j in countdown(parent.len-1, i-1):
+          parent.childNodes[j] = parent.childNodes[j-1]
+        parent.childNodes[i-1] = newNode
+        return
+      inc i
+    #doAssert false, "before not in node list"
+
+  proc createElement*(d: Document, identifier: cstring): Element =
+    new(result)
+    result.nodeName = identifier
+    result.nodeType = NodeType.ElementNode
+
+  proc createTextNode*(d: Document, identifier: cstring): Node =
+    new(result)
+    result.nodeName = "#text"
+    result.nodeValue = identifier
+    result.nodeType = NodeType.TextNode
+
+else:
+  proc len*(x: Node): int {.importcpp: "#.childNodes.length".}
+  proc `[]`*(x: Node; idx: int): Element {.importcpp: "#.childNodes[#]".}
+  proc getElementById*(id: cstring): Element {.importc: "document.getElementById", nodecl.}
+  proc appendChild*(n, child: Node) {.importcpp.}
+  proc removeChild*(n, child: Node) {.importcpp.}
+  proc replaceChild*(n, newNode, oldNode: Node) {.importcpp.}
+  proc insertBefore*(n, newNode, before: Node) {.importcpp.}
+  proc getElementById*(d: Document, id: cstring): Element {.importcpp.}
+  proc createElement*(d: Document, identifier: cstring): Element {.importcpp.}
+  proc createTextNode*(d: Document, identifier: cstring): Node {.importcpp.}
+
+proc setTimeout*(action: proc(); ms: int): Timeout {.importc, nodecl.}
+proc clearTimeout*(t: Timeout) {.importc, nodecl.}
 
 {.push importcpp.}
 
 # EventTarget "methods"
 proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
 proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), options: AddEventListenerOptions)
-proc removeEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
-proc dispatchEvent*(et: EventTarget, ev: Event)
+proc removeEventListener*(et: EventTarget; ev: cstring; cb: proc(ev: Event))
 
 # Window "methods"
 proc alert*(w: Window, msg: cstring)
 proc back*(w: Window)
 proc blur*(w: Window)
-proc clearInterval*(w: Window, interval: ref TInterval)
-proc clearTimeout*(w: Window, timeout: ref TTimeOut)
+proc captureEvents*(w: Window, eventMask: int) {.deprecated.}
+proc clearInterval*(w: Window, interval: ref Interval)
+proc clearTimeout*(w: Window, timeout: ref TimeOut)
 proc close*(w: Window)
 proc confirm*(w: Window, msg: cstring): bool
 proc disableExternalCapture*(w: Window)
@@ -469,6 +1052,7 @@ proc find*(w: Window, text: cstring, caseSensitive = false,
            backwards = false)
 proc focus*(w: Window)
 proc forward*(w: Window)
+proc getComputedStyle*(w: Window, e: Node, pe:Node = nil): Style
 proc handleEvent*(w: Window, e: Event)
 proc home*(w: Window)
 proc moveBy*(w: Window, x, y: int)
@@ -477,50 +1061,46 @@ proc open*(w: Window, uri, windowname: cstring,
            properties: cstring = nil): Window
 proc print*(w: Window)
 proc prompt*(w: Window, text, default: cstring): cstring
+proc releaseEvents*(w: Window, eventMask: int) {.deprecated.}
 proc resizeBy*(w: Window, x, y: int)
 proc resizeTo*(w: Window, x, y: int)
 proc routeEvent*(w: Window, event: Event)
 proc scrollBy*(w: Window, x, y: int)
 proc scrollTo*(w: Window, x, y: int)
-proc setInterval*(w: Window, code: cstring, pause: int): ref TInterval
-proc setInterval*(w: Window, function: proc (), pause: int): ref TInterval
-proc setTimeout*(w: Window, code: cstring, pause: int): ref TTimeOut
-proc setTimeout*(w: Window, function: proc (), pause: int): ref TInterval
+proc setInterval*(w: Window, code: cstring, pause: int): ref Interval
+proc setInterval*(w: Window, function: proc (), pause: int): ref Interval
+proc setTimeout*(w: Window, code: cstring, pause: int): ref TimeOut
+proc setTimeout*(w: Window, function: proc (), pause: int): ref Interval
 proc stop*(w: Window)
 proc requestAnimationFrame*(w: Window, function: proc (time: float)): int
 proc cancelAnimationFrame*(w: Window, id: int)
 
 # Node "methods"
-proc appendChild*(n, child: Node)
 proc appendData*(n: Node, data: cstring)
 proc cloneNode*(n: Node, copyContent: bool): Node
 proc deleteData*(n: Node, start, len: int)
+proc focus*(e: Node)
 proc getAttribute*(n: Node, attr: cstring): cstring
 proc getAttributeNode*(n: Node, attr: cstring): Node
-proc getBoundingClientRect*(e: Node): BoundingRect
 proc hasChildNodes*(n: Node): bool
-proc insertBefore*(n, newNode, before: Node)
 proc insertData*(n: Node, position: int, data: cstring)
 proc removeAttribute*(n: Node, attr: cstring)
 proc removeAttributeNode*(n, attr: Node)
-proc removeChild*(n, child: Node)
-proc replaceChild*(n, newNode, oldNode: Node)
 proc replaceData*(n: Node, start, len: int, text: cstring)
-proc scrollIntoView*(n: Node, alignToTop: bool=true)
+proc scrollIntoView*(n: Node)
 proc setAttribute*(n: Node, name, value: cstring)
 proc setAttributeNode*(n: Node, attr: Node)
 
 # Document "methods"
+proc captureEvents*(d: Document, eventMask: int) {.deprecated.}
 proc createAttribute*(d: Document, identifier: cstring): Node
-proc createElement*(d: Document, identifier: cstring): Element
-proc createTextNode*(d: Document, identifier: cstring): Node
-proc getElementById*(d: Document, id: cstring): Element
 proc getElementsByName*(d: Document, name: cstring): seq[Element]
 proc getElementsByTagName*(d: Document, name: cstring): seq[Element]
 proc getElementsByClassName*(d: Document, name: cstring): seq[Element]
 proc getSelection*(d: Document): cstring
 proc handleEvent*(d: Document, event: Event)
 proc open*(d: Document)
+proc releaseEvents*(d: Document, eventMask: int) {.deprecated.}
 proc routeEvent*(d: Document, event: Event)
 proc write*(d: Document, text: cstring)
 proc writeln*(d: Document, text: cstring)
@@ -539,6 +1119,8 @@ proc getElementsByClassName*(e: Element, name: cstring): seq[Element]
 # FormElement "methods"
 proc reset*(f: FormElement)
 proc submit*(f: FormElement)
+proc checkValidity*(e: FormElement): bool
+proc reportValidity*(e: FormElement): bool
 
 # EmbedElement "methods"
 proc play*(e: EmbedElement)
@@ -552,7 +1134,6 @@ proc replace*(loc: Location, s: cstring)
 proc back*(h: History)
 proc forward*(h: History)
 proc go*(h: History, pagesToJump: int)
-proc pushState*[T](h: History, stateObject: T, title, url: cstring)
 
 # Navigator "methods"
 proc javaEnabled*(h: Navigator): bool
@@ -560,7 +1141,7 @@ proc javaEnabled*(h: Navigator): bool
 # ClassList "methods"
 proc add*(c: ClassList, class: cstring)
 proc remove*(c: ClassList, class: cstring)
-proc contains*(c: ClassList, class: cstring):bool
+proc contains*(c: ClassList, class: cstring): bool
 proc toggle*(c: ClassList, class: cstring)
 
 # Style "methods"
@@ -570,22 +1151,49 @@ proc setAttribute*(s: Style, attr, value: cstring, caseSensitive=false)
 
 # Event "methods"
 proc preventDefault*(ev: Event)
+proc stopImmediatePropagation*(ev: Event)
+proc stopPropagation*(ev: Event)
+
+# KeyboardEvent "methods"
+proc getModifierState*(ev: KeyboardEvent, keyArg: cstring): bool
+
+# MouseEvent "methods"
+proc getModifierState*(ev: MouseEvent, keyArg: cstring): bool
 
 # TouchEvent "methods"
 proc identifiedTouch*(list: TouchList): Touch
 proc item*(list: TouchList, i: int): Touch
 
-# Performance "methods"
-proc now*(p: Performance): float
+# DataTransfer "methods"
+proc clearData*(dt: DataTransfer, format: cstring)
+proc getData*(dt: DataTransfer, format: cstring): cstring
+proc setData*(dt: DataTransfer, format: cstring, data: cstring)
+proc setDragImage*(dt: DataTransfer, img: Element, xOffset: int64, yOffset: int64)
+
+# DataTransferItem "methods"
+proc getAsFile*(dti: DataTransferItem): File
+
+# InputElement "methods"
+proc setSelectionRange*(e: InputElement, selectionStart: int, selectionEnd: int, selectionDirection: cstring = "none")
+proc setRangeText*(e: InputElement, replacement: cstring, startindex: int = 0, endindex: int = 0, selectionMode: cstring = "preserve")
+proc setCustomValidity*(e: InputElement, error: cstring)
+proc checkValidity*(e: InputElement): bool
+
+# Blob "methods"
+proc slice*(e: Blob, startindex: int = 0, endindex: int = e.size, contentType: cstring = "")
 
 {.pop.}
 
+proc setAttr*(n: Node; key, val: cstring) {.importcpp: "#.setAttribute(@)".}
+
 var
   window* {.importc, nodecl.}: Window
-  document* {.importc, nodecl.}: Document
   navigator* {.importc, nodecl.}: Navigator
   screen* {.importc, nodecl.}: Screen
 
+when not defined(nodejs):
+  var document* {.importc, nodecl.}: Document
+
 proc decodeURI*(uri: cstring): cstring {.importc, nodecl.}
 proc encodeURI*(uri: cstring): cstring {.importc, nodecl.}
 
@@ -596,8 +1204,29 @@ proc decodeURIComponent*(uri: cstring): cstring {.importc, nodecl.}
 proc encodeURIComponent*(uri: cstring): cstring {.importc, nodecl.}
 proc isFinite*(x: BiggestFloat): bool {.importc, nodecl.}
 proc isNaN*(x: BiggestFloat): bool {.importc, nodecl.}
-proc parseFloat*(s: cstring): BiggestFloat {.importc, nodecl.}
-proc parseInt*(s: cstring): int {.importc, nodecl.}
-proc parseInt*(s: cstring, radix: int):int {.importc, nodecl.}
 
-proc newEvent*(name: cstring): Event {.importcpp: "new Event(@)", constructor.}
+proc getElementsByClass*(n: Node; name: cstring): seq[Node] {.
+  importcpp: "#.getElementsByClassName(#)", nodecl.}
+
+
+type
+  BoundingRect* {.importc.} = object
+    top*, bottom*, left*, right*, x*, y*, width*, height*: float
+
+proc getBoundingClientRect*(e: Node): BoundingRect {.
+  importcpp: "getBoundingClientRect", nodecl.}
+proc clientHeight*(): int {.
+  importcpp: "(window.innerHeight || document.documentElement.clientHeight)@", nodecl.}
+proc clientWidth*(): int {.
+  importcpp: "(window.innerWidth || document.documentElement.clientWidth)@", nodecl.}
+
+proc inViewport*(el: Node): bool =
+  let rect = el.getBoundingClientRect()
+  result = rect.top >= 0 and rect.left >= 0 and
+           rect.bottom <= clientHeight().float and
+           rect.right <= clientWidth().float
+
+proc scrollTop*(e: Node): int {.importcpp: "#.scrollTop", nodecl.}
+proc offsetHeight*(e: Node): int {.importcpp: "#.offsetHeight", nodecl.}
+proc offsetTop*(e: Node): int {.importcpp: "#.offsetTop", nodecl.}
+proc offsetLeft*(e: Node): int {.importcpp: "#.offsetLeft", nodecl.}
diff --git a/tools/dochack/dochack.nim b/tools/dochack/dochack.nim
index 61c61225d..2f8465a63 100644
--- a/tools/dochack/dochack.nim
+++ b/tools/dochack/dochack.nim
@@ -1,6 +1,38 @@
-import karax
+import dom
 import fuzzysearch
 
+proc textContent(e: Element): cstring {.
+  importcpp: "#.textContent", nodecl.}
+
+proc textContent(e: Node): cstring {.
+  importcpp: "#.textContent", nodecl.}
+
+proc tree(tag: string; kids: varargs[Element]): Element =
+  result = document.createElement tag
+  for k in kids:
+    result.appendChild k
+
+proc add(parent, kid: Element) =
+  if parent.nodeName == cstring"TR" and (
+      kid.nodeName == cstring"TD" or kid.nodeName == cstring"TH"):
+    let k = document.createElement("TD")
+    appendChild(k, kid)
+    appendChild(parent, k)
+  else:
+    appendChild(parent, kid)
+
+proc setClass(e: Element; value: string) =
+  e.setAttribute("class", value)
+proc text(s: string): Element = cast[Element](document.createTextNode(s))
+proc text(s: cstring): Element = cast[Element](document.createTextNode(s))
+
+proc getElementById(id: cstring): Element {.importc: "document.getElementById", nodecl.}
+
+proc replaceById(id: cstring; newTree: Node) =
+  let x = getElementById(id)
+  x.parentNode.replaceChild(newTree, x)
+  newTree.id = id
+
 proc findNodeWith(x: Element; tag, content: cstring): Element =
   if x.nodeName == tag and x.textContent == content:
     return x
@@ -182,7 +214,7 @@ proc buildToc(orig: TocEntry; types, procs: seq[Element]): TocEntry =
     t.markElement()
     for p in procs:
       if not isMarked(p):
-        let xx = karax.getElementsByClass(p.parent, cstring"attachedType")
+        let xx = getElementsByClass(p.parent, cstring"attachedType")
         if xx.len == 1 and xx[0].textContent == t.textContent:
           #kout(cstring"found ", p.nodeName)
           let q = tree("A", text(p.title))
@@ -230,7 +262,7 @@ proc groupBy*(value: cstring) {.exportc.} =
   togglevis(getElementById"toc-list")
 
 var
-  db: seq[Element]
+  db: seq[Node]
   contents: seq[cstring]
 
 template normalize(x: cstring): cstring = x.toLower.replace("_", "")
@@ -258,7 +290,7 @@ proc dosearch(value: cstring): Element =
   let ul = tree("UL")
   result = tree("DIV")
   result.setClass"search_results"
-  var matches: seq[(Element, int)] = @[]
+  var matches: seq[(Node, int)] = @[]
   for i in 0..<db.len:
     let c = contents[i]
     if c == cstring"Examples" or c == cstring"PEG construction":
@@ -270,11 +302,10 @@ proc dosearch(value: cstring): Element =
     if matched:
       matches.add((db[i], score))
 
-  matches.sort do (a, b: auto) -> int:
-    b[1] - a[1]
+  matches.sort(proc(a, b: auto): int = b[1] - a[1])
   for i in 0 ..< min(matches.len, 19):
     matches[i][0].innerHTML = matches[i][0].getAttribute("data-doc-search-tag")
-    ul.add(tree("LI", matches[i][0]))
+    ul.add(tree("LI", cast[Element](matches[i][0])))
   if ul.len == 0:
     result.add tree("B", text"no search results")
   else:
diff --git a/tools/dochack/karax.nim b/tools/dochack/karax.nim
deleted file mode 100644
index 020fd37d1..000000000
--- a/tools/dochack/karax.nim
+++ /dev/null
@@ -1,344 +0,0 @@
-# Simple lib to write JS UIs
-
-import dom
-
-export dom.Element, dom.Event, dom.cloneNode, dom
-
-proc kout*[T](x: T) {.importc: "console.log", varargs.}
-  ## the preferred way of debugging karax applications.
-
-proc id*(e: Node): cstring {.importcpp: "#.id", nodecl.}
-proc `id=`*(e: Node; x: cstring) {.importcpp: "#.id = #", nodecl.}
-proc className*(e: Node): cstring {.importcpp: "#.className", nodecl.}
-proc `className=`*(e: Node; v: cstring) {.importcpp: "#.className = #", nodecl.}
-
-proc value*(e: Element): cstring {.importcpp: "#.value", nodecl.}
-proc `value=`*(e: Element; v: cstring) {.importcpp: "#.value = #", nodecl.}
-
-proc getElementsByClass*(e: Element; name: cstring): seq[Element] {.importcpp: "#.getElementsByClassName(#)", nodecl.}
-
-proc toLower*(x: cstring): cstring {.
-  importcpp: "#.toLowerCase()", nodecl.}
-proc replace*(x: cstring; search, by: cstring): cstring {.
-  importcpp: "#.replace(#, #)", nodecl.}
-
-type
-  EventHandler* = proc(ev: Event)
-  EventHandlerId* = proc(ev: Event; id: int)
-
-  Timeout* = ref object
-
-var document* {.importc.}: Document
-
-var
-  dorender: proc (): Element {.closure.}
-  drawTimeout: Timeout
-  currentTree: Element
-
-proc setRenderer*(renderer: proc (): Element) =
-  dorender = renderer
-
-proc setTimeout*(action: proc(); ms: int): Timeout {.importc, nodecl.}
-proc clearTimeout*(t: Timeout) {.importc, nodecl.}
-proc targetElem*(e: Event): Element = cast[Element](e.target)
-
-proc getElementById*(id: cstring): Element {.importc: "document.getElementById", nodecl.}
-
-proc getElementsByClassName*(cls: cstring): seq[Element] {.importc:
-  "document.getElementsByClassName", nodecl.}
-
-proc textContent*(e: Element): cstring {.
-  importcpp: "#.textContent", nodecl.}
-
-proc replaceById*(id: cstring; newTree: Node) =
-  let x = getElementById(id)
-  x.parentNode.replaceChild(newTree, x)
-  newTree.id = id
-
-proc equals(a, b: Node): bool =
-  if a.nodeType != b.nodeType: return false
-  if a.id != b.id: return false
-  if a.nodeName != b.nodeName: return false
-  if a.nodeType == TextNode:
-    if a.data != b.data: return false
-  elif a.childNodes.len != b.childNodes.len:
-    return false
-  if a.className != b.className:
-    # style differences are updated in place and we pretend
-    # it's still the same node
-    a.className = b.className
-    #return false
-  return true
-
-proc diffTree(parent, a, b: Node) =
-  if equals(a, b):
-    if a.nodeType != TextNode:
-      # we need to do this correctly in the presence of asyncronous
-      # DOM updates:
-      var i = 0
-      while i < a.childNodes.len and a.childNodes.len == b.childNodes.len:
-        diffTree(a, a.childNodes[i], b.childNodes[i])
-        inc i
-  elif parent == nil:
-    replaceById("ROOT", b)
-  else:
-    parent.replaceChild(b, a)
-
-proc dodraw() =
-  let newtree = dorender()
-  newtree.id = "ROOT"
-  if currentTree == nil:
-    currentTree = newtree
-    replaceById("ROOT", currentTree)
-  else:
-    diffTree(nil, currentTree, newtree)
-
-proc redraw*() =
-  # we buffer redraw requests:
-  if drawTimeout != nil:
-    clearTimeout(drawTimeout)
-  drawTimeout = setTimeout(dodraw, 30)
-
-proc tree*(tag: string; kids: varargs[Element]): Element =
-  result = document.createElement tag
-  for k in kids:
-    result.appendChild k
-
-proc tree*(tag: string; attrs: openarray[(string, string)];
-           kids: varargs[Element]): Element =
-  result = tree(tag, kids)
-  for a in attrs: result.setAttribute(a[0], a[1])
-
-proc text*(s: string): Element = cast[Element](document.createTextNode(s))
-proc text*(s: cstring): Element = cast[Element](document.createTextNode(s))
-proc add*(parent, kid: Element) =
-  if parent.nodeName == cstring"TR" and (
-      kid.nodeName == cstring"TD" or kid.nodeName == cstring"TH"):
-    let k = document.createElement("TD")
-    appendChild(k, kid)
-    appendChild(parent, k)
-  else:
-    appendChild(parent, kid)
-
-proc len*(x: Element): int {.importcpp: "#.childNodes.length".}
-proc `[]`*(x: Element; idx: int): Element {.importcpp: "#.childNodes[#]".}
-
-proc isInt*(s: cstring): bool {.asmNoStackFrame.} =
-  asm """
-    return s.match(/^[0-9]+$/);
-  """
-
-var
-  linkCounter: int
-
-proc link*(id: int): Element =
-  result = document.createElement("a")
-  result.setAttribute("href", "#")
-  inc linkCounter
-  result.setAttribute("id", $linkCounter & ":" & $id)
-
-proc link*(action: EventHandler): Element =
-  result = document.createElement("a")
-  result.setAttribute("href", "#")
-  addEventListener(result, "click", action)
-
-proc parseInt*(s: cstring): int {.importc, nodecl.}
-proc parseFloat*(s: cstring): float {.importc, nodecl.}
-proc split*(s, sep: cstring): seq[cstring] {.importcpp, nodecl.}
-
-proc startsWith*(a, b: cstring): bool {.importcpp: "startsWith", nodecl.}
-proc contains*(a, b: cstring): bool {.importcpp: "(#.indexOf(#)>=0)", nodecl.}
-proc substr*(s: cstring; start: int): cstring {.importcpp: "substr", nodecl.}
-proc substr*(s: cstring; start, length: int): cstring {.importcpp: "substr", nodecl.}
-
-#proc len*(s: cstring): int {.importcpp: "#.length", nodecl.}
-proc `&`*(a, b: cstring): cstring {.importcpp: "(# + #)", nodecl.}
-proc toCstr*(s: int): cstring {.importcpp: "((#)+'')", nodecl.}
-
-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 valueAsInt*(e: Element): int = parseInt(e.value)
-proc suffixAsInt*(s, prefix: cstring): int = parseInt(suffix(s, prefix))
-
-proc scrollTop*(e: Element): int {.importcpp: "#.scrollTop", nodecl.}
-proc offsetHeight*(e: Element): int {.importcpp: "#.offsetHeight", nodecl.}
-proc offsetTop*(e: Element): int {.importcpp: "#.offsetTop", nodecl.}
-
-template onImpl(s) {.dirty} =
-  proc wrapper(ev: Event) =
-    action(ev)
-    redraw()
-  addEventListener(e, s, wrapper)
-
-proc setOnclick*(e: Element; action: proc(ev: Event)) =
-  onImpl "click"
-
-proc setOnclick*(e: Element; action: proc(ev: Event; id: int)) =
-  proc wrapper(ev: Event) =
-    let id = ev.target.id
-    let a = id.split(":")
-    if a.len == 2:
-      action(ev, parseInt(a[1]))
-      redraw()
-    else:
-      kout(cstring("cannot deal with id "), id)
-  addEventListener(e, "click", wrapper)
-
-proc setOnfocuslost*(e: Element; action: EventHandler) =
-  onImpl "blur"
-
-proc setOnchanged*(e: Element; action: EventHandler) =
-  onImpl "change"
-
-proc setOnscroll*(e: Element; action: EventHandler) =
-  onImpl "scroll"
-
-proc select*(choices: openarray[string]): Element =
-  result = document.createElement("select")
-  var i = 0
-  for c in choices:
-    result.add tree("option", [("value", $i)], text(c))
-    inc i
-
-proc select*(choices: openarray[(int, string)]): Element =
-  result = document.createElement("select")
-  for c in choices:
-    result.add tree("option", [("value", $c[0])], text(c[1]))
-
-var radioCounter: int
-
-proc radio*(choices: openarray[(int, string)]): Element =
-  result = document.createElement("fieldset")
-  var i = 0
-  inc radioCounter
-  for c in choices:
-    let id = "radio_" & c[1] & $i
-    var kid = tree("input", [("type", "radio"),
-      ("id", id), ("name", "radio" & $radioCounter),
-      ("value", $c[0])])
-    if i == 0:
-      kid.setAttribute("checked", "checked")
-    var lab = tree("label", [("for", id)], text(c[1]))
-    kid.add lab
-    result.add kid
-    inc i
-
-proc tag*(name: string; id="", class=""): Element =
-  result = document.createElement(name)
-  if id.len > 0:
-    result.setAttribute("id", id)
-  if class.len > 0:
-    result.setAttribute("class", class)
-
-proc tdiv*(id="", class=""): Element = tag("div", id, class)
-proc span*(id="", class=""): Element = tag("span", id, class)
-
-proc th*(s: string): Element =
-  result = tag("th")
-  result.add text(s)
-
-proc td*(s: string): Element =
-  result = tag("td")
-  result.add text(s)
-
-proc td*(s: Element): Element =
-  result = tag("td")
-  result.add s
-
-proc td*(class: string; s: Element): Element =
-  result = tag("td")
-  result.add s
-  result.setAttribute("class", class)
-
-proc table*(class="", kids: varargs[Element]): Element =
-  result = tag("table", "", class)
-  for k in kids: result.add k
-
-proc tr*(kids: varargs[Element]): Element =
-  result = tag("tr")
-  for k in kids:
-    if k.nodeName == cstring"TD" or k.nodeName == cstring"TH":
-      result.add k
-    else:
-      result.add td(k)
-
-proc setClass*(e: Element; value: string) =
-  e.setAttribute("class", value)
-
-proc setAttr*(e: Element; key, value: cstring) =
-  e.setAttribute(key, value)
-
-proc getAttr*(e: Element; key: cstring): cstring {.
-  importcpp: "#.getAttribute(#)", nodecl.}
-
-proc realtimeInput*(id, val: string; changed: proc(value: cstring)): Element =
-  let oldElem = getElementById(id)
-  #if oldElem != nil: return oldElem
-  let newVal = if oldElem.isNil: val else: $oldElem.value
-  var timer: Timeout
-  proc wrapper() =
-    changed(getElementById(id).value)
-    redraw()
-  proc onkeyup(ev: Event) =
-    if timer != nil: clearTimeout(timer)
-    timer = setTimeout(wrapper, 400)
-  result = tree("input", [("type", "text"),
-    ("value", newVal),
-    ("id", id)])
-  result.addEventListener("keyup", onkeyup)
-
-proc ajax(meth, url: cstring; headers: openarray[(string, string)];
-          data: cstring;
-          cont: proc (httpStatus: int; response: cstring)) =
-  proc setRequestHeader(a, b: cstring) {.importc: "ajax.setRequestHeader".}
-  {.emit: """
-  var ajax = new XMLHttpRequest();
-  ajax.open(`meth`,`url`,true);""".}
-  for a, b in items(headers):
-    setRequestHeader(a, b)
-  {.emit: """
-  ajax.onreadystatechange = function(){
-    if(this.readyState == 4){
-      if(this.status == 200){
-        `cont`(this.status, this.responseText);
-      } else {
-        `cont`(this.status, this.statusText);
-      }
-    }
-  }
-  ajax.send(`data`);
-  """.}
-
-proc ajaxPut*(url: string; headers: openarray[(string, string)];
-          data: cstring;
-          cont: proc (httpStatus: int, response: cstring)) =
-  ajax("PUT", url, headers, data, cont)
-
-proc ajaxGet*(url: string; headers: openarray[(string, string)];
-          cont: proc (httpStatus: int, response: cstring)) =
-  ajax("GET", url, headers, nil, cont)
-
-{.push stackTrace:off.}
-
-proc setupErrorHandler*(useAlert=false) =
-  ## Installs an error handler that transforms native JS unhandled
-  ## exceptions into Nim based stack traces. If `useAlert` is false,
-  ## the error message it put into the console, otherwise `alert`
-  ## is called.
-  proc stackTraceAsCstring(): cstring = cstring(getStackTrace())
-  {.emit: """
-  window.onerror = function(msg, url, line, col, error) {
-    var x = "Error: " + msg + "\n" + `stackTraceAsCstring`()
-    if (`useAlert`)
-      alert(x);
-    else
-      console.log(x);
-    var suppressErrorAlert = true;
-    return suppressErrorAlert;
-  };""".}
-
-{.pop.}