about summary refs log tree commit diff stats
path: root/src/html/event.nim
diff options
context:
space:
mode:
Diffstat (limited to 'src/html/event.nim')
-rw-r--r--src/html/event.nim65
1 files changed, 49 insertions, 16 deletions
diff --git a/src/html/event.nim b/src/html/event.nim
index 7196d151..6307cf3a 100644
--- a/src/html/event.nim
+++ b/src/html/event.nim
@@ -46,7 +46,7 @@ type
 
   EventHandler* = JSValue
 
-  EventListenerCallback = proc (event: Event): Err[JSError]
+  EventListenerCallback = JSValue
 
   EventListener* = ref object
     ctype*: string
@@ -55,7 +55,9 @@ type
     passive: Option[bool]
     once: bool
     #TODO AbortSignal
-    removed: bool
+    #TODO do we really need `removed'? maybe we could just check if
+    # callback is undefined.
+    removed*: bool
 
 jsDestructor(Event)
 jsDestructor(CustomEvent)
@@ -176,33 +178,60 @@ proc initCustomEvent(this: CustomEvent, ctype: string,
 proc newEventTarget(): EventTarget {.jsctor.} =
   return EventTarget()
 
-proc defaultPassiveValue(ctype: string, eventTarget: EventTarget): bool =
+proc defaultPassiveValue(ctype: string; eventTarget: EventTarget): bool =
   if ctype in ["touchstart", "touchmove", "wheel", "mousewheel"]:
     return true
   return eventTarget.isDefaultPassive()
 
-proc findEventListener(eventTarget: EventTarget, ctype: string,
-    callback: EventListenerCallback, capture: bool): int =
+proc findEventListener(eventTarget: EventTarget; ctype: string;
+    callback: EventListenerCallback; capture: bool): int =
   for i in 0 ..< eventTarget.eventListeners.len:
     let it = eventTarget.eventListeners[i]
     if it.ctype == ctype and it.callback == callback and it.capture == capture:
       return i
   return -1
 
+# EventListener
+proc invoke*(ctx: JSContext; listener: EventListener; event: Event):
+    JSValue =
+  #TODO make this standards compliant
+  if JS_IsNull(listener.callback):
+    return JS_UNDEFINED
+  let jsTarget = ctx.toJS(event.currentTarget)
+  var jsEvent = ctx.toJS(event)
+  if JS_IsFunction(ctx, listener.callback):
+    let ret = JS_Call(ctx, listener.callback, jsTarget, 1, addr jsEvent)
+    JS_FreeValue(ctx, jsTarget)
+    JS_FreeValue(ctx, jsEvent)
+    return ret
+  assert JS_IsObject(listener.callback)
+  let handler = JS_GetPropertyStr(ctx, listener.callback, "handleEvent")
+  if JS_IsException(handler):
+    JS_FreeValue(ctx, jsTarget)
+    JS_FreeValue(ctx, jsEvent)
+    return handler
+  let ret = JS_Call(ctx, handler, jsTarget, 1, addr jsEvent)
+  JS_FreeValue(ctx, jsTarget)
+  JS_FreeValue(ctx, jsEvent)
+  return ret
+
 # shared
-proc addAnEventListener(eventTarget: EventTarget, listener: EventListener) =
+proc addAnEventListener(target: EventTarget; listener: EventListener) =
   #TODO signals
-  if listener.callback == nil:
+  if JS_IsUndefined(listener.callback):
     return
   if listener.passive.isNone:
-    listener.passive = some(defaultPassiveValue(listener.ctype, eventTarget))
-  if eventTarget.findEventListener(listener.ctype, listener.callback,
+    listener.passive = some(defaultPassiveValue(listener.ctype, target))
+  if target.findEventListener(listener.ctype, listener.callback,
       listener.capture) == -1: # dedup
-    eventTarget.eventListeners.add(listener)
+    target.eventListeners.add(listener)
   #TODO signals
 
-proc removeAnEventListener(eventTarget: EventTarget, i: int) =
-  eventTarget.eventListeners[i].removed = true
+proc removeAnEventListener(eventTarget: EventTarget; ctx: JSContext; i: int) =
+  let listener = eventTarget.eventListeners[i]
+  listener.removed = true
+  JS_FreeValue(ctx, listener.callback)
+  listener.callback = JS_UNDEFINED
   eventTarget.eventListeners.delete(i)
 
 proc flatten(ctx: JSContext, options: JSValue): bool =
@@ -233,17 +262,21 @@ proc flattenMore(ctx: JSContext, options: JSValue):
       passive = some(x.get)
   return (capture, once, passive)
 
-proc addEventListener(ctx: JSContext, eventTarget: EventTarget, ctype: string,
-    callback: EventListenerCallback, options = JS_UNDEFINED) {.jsfunc.} =
+proc addEventListener*(ctx: JSContext; eventTarget: EventTarget; ctype: string;
+    callback: EventListenerCallback; options = JS_UNDEFINED): Err[JSError]
+    {.jsfunc.} =
+  if not JS_IsObject(callback) and not JS_IsNull(callback):
+    return errTypeError("callback is not an object")
   let (capture, once, passive) = flattenMore(ctx, options)
   let listener = EventListener(
     ctype: ctype,
     capture: capture,
     passive: passive,
     once: once,
-    callback: callback
+    callback: JS_DupValue(ctx, callback)
   )
   eventTarget.addAnEventListener(listener)
+  ok()
 
 proc removeEventListener(ctx: JSContext, eventTarget: EventTarget,
     ctype: string, callback: EventListenerCallback,
@@ -251,7 +284,7 @@ proc removeEventListener(ctx: JSContext, eventTarget: EventTarget,
   let capture = flatten(ctx, options)
   let i = eventTarget.findEventListener(ctype, callback, capture)
   if i != -1:
-    eventTarget.removeAnEventListener(i)
+    eventTarget.removeAnEventListener(ctx, i)
 
 proc addEventModule*(ctx: JSContext) =
   let eventCID = ctx.registerType(Event)