Handle JavaScript Events in Mercury WASM

As asked on Twitter, the code that is needed to add and remove event handlers on HTML elements. that will be handled in the Wasm Code.

Edit: I forgot the small part of javaScript code that is needed also.
Add the following code to your html:

<script>
  function WasmEvent(id) {
      var e = window.event;
      program.WasmEvent(e, id);
  }
</script>

and change the WASM code in the html:

  var program; //added to the code
  WasmForms.instantiate("wasm/WasmForms.wasm").then(function (result) {
    console.log("WebAssembly file WasmForms.wasm has been loaded.");
    program = result.Program(); //this was var program - ...
    program.Main();
  });

You need a Module that implements the EventHandling system:

Public Module jsEvents

  Public delegate Sub EventDelegate(e As Dynamic)
  Private EventIds As New Dictionary(Of String, EventDelegate)

  Public Function GetEventDelegate(element As Dynamic, EventName As String) As EventDelegate
    dim id As String = element.getAttribute(EventName)
    If assigned(id) then
      return EventIds(id)
    Else
      Return nothing
    End If
  End Function

  Public Sub SetEvent(element As Dynamic, EventName As String, del As EventDelegate)
    ClearEvent(element, EventName)
    If del IsNot Nothing then
      Dim Id As String = RemObjects.Elements.System.DateTime.Now.Ticks.ToString
      EventIds.Add(Id, del)
      element.setAttribute(EventName, $"WasmEvent('{Id}');")
    End If
  End Sub

  Public Sub ClearEvent(element As Dynamic, EventName As String)
    dim id As String = element.getAttribute(EventName)
    If assigned(id) then
      EventIds.Remove(id)
      element.setAttribute(EventName, "")
    End If
  End Sub

  Public Function GetEventDelegate(Id As String) As EventDelegate
    Return EventIds(Id)
  End Function
End Module

Then you add the dispatcher code to the exported WASM class:

  Public Sub WasmEvent(e As Dynamic, id As String)
    Try
      GetEventDelegate(id).Invoke(e)
    Catch
    End try
  End Sub

After that you can add the event handlers from code:

  CloseBtn = document.getElementById("myclosebutton")
  Dim x As EventDelegate = AddressOf Close
  SetEvent(CloseBtn, "onclick", x)

or remove the handler from code:

 ClearEvent(CloseBtn, "onclick")

The following Sub will handle the event:

 Private Sub Close(e As Dynamic)
1 Like

@mh: can you change the template so that the program variable is reusable?

new suggested template HTML:

<html>
  <head>
    <title>WasmForms</title>
    <script lang="javascript" src="wasm/RemObjectsElements.js"></script>
    <script lang="javascript" src="wasm/WasmForms.js"></script>
    <script lang="javascript">
      var program;
      WasmForms.instantiate("wasm/WasmForms.wasm").then(function (result) {
        console.log("WebAssembly file WasmForms.wasm has been loaded.");
        program = result.Program();
        program.Main();
      });
    </script>
  </head>
  <body>
      … as it was without the javascript code
  </body>
</html>

An example to use the standard VB event system (untested!)

Define a class that implements the events:

public class HtmlEventComponent
    Implements IDisposable
    //the onclick event
    Public Event OnClick as EventDelegate
    private sub InternalOnClick(e As Dynamic)
        RaiseEvent OnClick(e)
    end sub
    //define more events

    Private EmbeddedElement as HtmlElement

    Public Sub New(c as HtmlElement)
      //wire the javascript events
       EmbeddedElement = c
      SetEvent(c, "onclick", AddressOf InternalOnClick)
    end Sub

    //edit: added dispose:
    Public sub Dispose Implements IDisposable.Dispose
        if  EmbeddedElement  IsNot Null Then
            ClearEvent(EmbeddedElement , "onclick")
            EmbeddedElement  = Null
        End If
    End Sub
End Class

Now you can use the following code:

Public WithEvents CloseBtnEvents as New HtmlEventComponent(document.getElementById("myclosebutton"))

Private Sub Close(e As Dynamic) Handles CloseBtnEvents.OnClick
End Sub

Done:

<html>
	<head>
		<title>$safeprojectname$ Test Page</title>
		<script lang="javascript" src="wasm/RemObjectsElements.js"></script>
		<script lang="javascript" src="wasm/$safeprojectname$.js"></script>
		<script lang="javascript">
			var program;
			$safeprojectname$.instantiate("wasm/$safeprojectname$.wasm").then(function (result) {
				console.log("WebAssembly file $safeprojectname$.wasm has been loaded.");
				program = result.Program();
				program.Main();
			});
	    </script>
	</head>
	<body>
		<h1>$safeprojectname$ Test Page</h1>
		<div id="helloWorld"/>
	</body>
</html>
1 Like

Is this available as a complete sample download? In other words, a project that I can download and execute using CTRL+R. There is terminology in this thread that I’m not familiar with (“to the exported WASM class”); so a complete sample would allow me to work through the whole thing.

Ultimately my goal is to get a WASM project up and running that is more than “hello world” static text. For where I want to go (and share with those that follow me), the starting point would be:

  • handle key presses (arrow keys, esc)…
  • handle mouse move/click…
  • draw a box in differing colors to a, I assume, canvas.
  • move said box on the canvas using the keys/mouse.

Thanks.

Hi Cory,

The current version has a problem with the WEBApi - something went wrong with the imports.
This will be fixed in the version of tomorrow - so please use that one.

I will upload a sample project for you later.

The promised project: WasmEnhancedTemplate.zip (1.2 MB)

I commented the complete project.
It creates the page from Wasm (as you can see in the html; the body is completely empty)
When you click on the text “Hello from Elements WebAssembly!”, the event handler is invoked (with some bugs at this moment).

There are some problems in Wasm in the current Alpha:

  • if you set a property in the WebApi, you get the error: v.apply is not a function
  • raiseevent doesn’t see the event, you get the error: No element with this name: OnClick

They will hopefully be solved in the next Alpha.

1 Like