changeset 236:f73d722d98c8 am

renamed folder
author am@osimis.io
date Tue, 19 Jun 2018 16:06:32 +0200
parents ce4405d98b92
children b4642964c355
files Platforms/Wasm/CMakeLists.txt Platforms/Wasm/Defaults.cpp Platforms/Wasm/Defaults.h Platforms/Wasm/WasmViewport.h Platforms/Wasm/WasmWebService.cpp Platforms/Wasm/WasmWebService.h Platforms/Wasm/WasmWebService.js Platforms/Wasm/default-library.js Platforms/Wasm/defaults.js Platforms/Wasm/stone-framework-loader.ts Platforms/Wasm/wasm-application.ts Platforms/Wasm/wasm-viewport.ts Platforms/WebAssembly/CMakeLists.txt Platforms/WebAssembly/Defaults.cpp Platforms/WebAssembly/Defaults.h Platforms/WebAssembly/WasmViewport.h Platforms/WebAssembly/WasmWebService.cpp Platforms/WebAssembly/WasmWebService.h Platforms/WebAssembly/WasmWebService.js Platforms/WebAssembly/default-library.js Platforms/WebAssembly/defaults.js Platforms/WebAssembly/stone-framework-loader.ts Platforms/WebAssembly/wasm-application.ts Platforms/WebAssembly/wasm-viewport.ts
diffstat 22 files changed, 1061 insertions(+), 1061 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Platforms/Wasm/CMakeLists.txt	Tue Jun 19 16:06:32 2018 +0200
@@ -0,0 +1,49 @@
+# Usage (Linux):
+# source ~/Downloads/emsdk/emsdk_env.sh && cmake -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake ..
+
+cmake_minimum_required(VERSION 2.8.3)
+
+
+#####################################################################
+## Configuration of the Emscripten compiler for WebAssembly target
+#####################################################################
+
+set(WASM_FLAGS "-s WASM=1")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${CMAKE_SOURCE_DIR}/library.js")
+
+# Handling of memory
+#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1")  # Resize
+#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_MEMORY=536870912")  # 512MB
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=536870912")  # 512MB + resize
+#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=1073741824")  # 1GB + resize
+
+# To debug exceptions
+#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DEMANGLE_SUPPORT=1 -s ASSERTIONS=2")
+
+
+#####################################################################
+## Build a static library containing the Orthanc Stone framework
+#####################################################################
+
+include(../../Resources/CMake/OrthancStoneParameters.cmake)
+
+SET(ORTHANC_SANDBOXED ON)
+SET(ENABLE_SDL OFF)
+
+include(../../Resources/CMake/OrthancStoneConfiguration.cmake)
+
+add_library(OrthancStone STATIC ${ORTHANC_STONE_SOURCES})
+
+
+
+
+
+
+# Regenerate a dummy "library.c" file each time the "library.js" file
+# is modified, so as to force a new execution of the linking
+add_custom_command(
+    OUTPUT "${AUTOGENERATED_DIR}/library.c"
+    COMMAND ${CMAKE_COMMAND} -E touch "${AUTOGENERATED_DIR}/library.c" ""
+    DEPENDS "${CMAKE_SOURCE_DIR}/library.js")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Platforms/Wasm/Defaults.cpp	Tue Jun 19 16:06:32 2018 +0200
@@ -0,0 +1,252 @@
+#include "Defaults.h"
+
+#include "WasmWebService.h"
+#include <Framework/dev.h>
+#include "Framework/Widgets/TestCairoWidget.h"
+#include <Framework/Viewport/WidgetViewport.h>
+#include <Framework/Widgets/LayerWidget.h>
+#include <algorithm>
+
+static unsigned int width_ = 0;
+static unsigned int height_ = 0;
+
+/**********************************/
+
+static std::auto_ptr<OrthancStone::BasicWasmApplication> application;
+static OrthancStone::ChangeObserver changeObserver_;
+static OrthancStone::StatusBar statusBar_;
+
+
+static std::list<std::shared_ptr<OrthancStone::WidgetViewport>> viewports_;
+
+std::shared_ptr<OrthancStone::WidgetViewport> FindViewportSharedPtr(ViewportHandle viewport) {
+  for (const auto& v : viewports_) {
+    if (v.get() == viewport) {
+      return v;
+    }
+  }
+  assert(false);
+  return std::shared_ptr<OrthancStone::WidgetViewport>();
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  using namespace OrthancStone;
+
+  // when WASM needs a C++ viewport
+  ViewportHandle EMSCRIPTEN_KEEPALIVE CreateCppViewport() {
+    
+    std::shared_ptr<OrthancStone::WidgetViewport> viewport(new OrthancStone::WidgetViewport);
+    printf("viewport %x\n", viewport.get());
+
+    viewports_.push_back(viewport);
+
+    printf("There are now %d viewports in C++\n", viewports_.size());
+
+    viewport->SetStatusBar(statusBar_);
+    viewport->Register(changeObserver_);
+
+    return viewport.get();
+  }
+
+  // when WASM does not need a viewport anymore, it should release it 
+  void EMSCRIPTEN_KEEPALIVE ReleaseCppViewport(ViewportHandle viewport) {
+    viewports_.remove_if([viewport](const std::shared_ptr<OrthancStone::WidgetViewport>& v) { return v.get() == viewport;});
+
+    printf("There are now %d viewports in C++\n", viewports_.size());
+  }
+
+  void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle viewport) {
+
+    printf("CreateWasmApplication\n");
+
+    application.reset(CreateUserApplication());
+
+    boost::program_options::options_description options;
+    application->DeclareStartupOptions(options);
+  }
+
+  void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc,
+                                                  const char* value) {
+    application->SetStartupParameter(keyc, value);
+  }
+
+  void EMSCRIPTEN_KEEPALIVE StartWasmApplication() {
+
+    printf("StartWasmApplication\n");
+
+    // recreate a command line from uri arguments and parse it
+    boost::program_options::variables_map parameters;
+    application->GetStartupParameters(parameters);
+
+    BasicWasmApplicationContext& context = dynamic_cast<BasicWasmApplicationContext&>(application->CreateApplicationContext(OrthancStone::WasmWebService::GetInstance(), NULL));
+    application->Initialize(statusBar_, parameters);
+
+//    viewport->SetSize(width_, height_);
+    printf("StartWasmApplication - completed\n");
+  }
+  
+  void EMSCRIPTEN_KEEPALIVE NotifyUpdateContent()
+  {
+    for (auto viewport : viewports_) {
+      // TODO Only launch the JavaScript timer if "HasUpdateContent()"
+      if (viewport->HasUpdateContent())
+      {
+        viewport->UpdateContent();
+      }
+
+    }
+
+  }
+  
+
+  void EMSCRIPTEN_KEEPALIVE ViewportSetSize(ViewportHandle viewport, unsigned int width, unsigned int height)
+  {
+    width_ = width;
+    height_ = height;
+    
+    viewport->SetSize(width, height);
+  }
+
+  int EMSCRIPTEN_KEEPALIVE ViewportRender(ViewportHandle viewport,
+                                          unsigned int width,
+                                          unsigned int height,
+                                          uint8_t* data)
+  {
+    changeObserver_.Reset();
+
+    //printf("ViewportRender called %dx%d\n", width, height);
+    if (width == 0 ||
+        height == 0)
+    {
+      return 1;
+    }
+
+    Orthanc::ImageAccessor surface;
+    surface.AssignWritable(Orthanc::PixelFormat_BGRA32, width, height, 4 * width, data);
+
+    viewport->Render(surface);
+
+    // Convert from BGRA32 memory layout (only color mode supported by
+    // Cairo, which corresponds to CAIRO_FORMAT_ARGB32) to RGBA32 (as
+    // expected by HTML5 canvas). This simply amounts to swapping the
+    // B and R channels.
+    uint8_t* p = data;
+    for (unsigned int y = 0; y < height; y++) {
+      for (unsigned int x = 0; x < width; x++) {
+        uint8_t tmp = p[0];
+        p[0] = p[2];
+        p[2] = tmp;
+        
+        p += 4;
+      }
+    }
+
+    return 1;
+  }
+
+
+  void EMSCRIPTEN_KEEPALIVE ViewportMouseDown(ViewportHandle viewport,
+                                              unsigned int rawButton,
+                                              int x,
+                                              int y,
+                                              unsigned int rawModifiers)
+  {
+    OrthancStone::MouseButton button;
+    switch (rawButton)
+    {
+      case 0:
+        button = OrthancStone::MouseButton_Left;
+        break;
+
+      case 1:
+        button = OrthancStone::MouseButton_Middle;
+        break;
+
+      case 2:
+        button = OrthancStone::MouseButton_Right;
+        break;
+
+      default:
+        return;  // Unknown button
+    }
+
+    viewport->MouseDown(button, x, y, OrthancStone::KeyboardModifiers_None /* TODO */);
+  }
+  
+
+  void EMSCRIPTEN_KEEPALIVE ViewportMouseWheel(ViewportHandle viewport,
+                                               int deltaY,
+                                               int x,
+                                               int y,
+                                               int isControl)
+  {
+    if (deltaY != 0)
+    {
+      OrthancStone::MouseWheelDirection direction = (deltaY < 0 ?
+                                                     OrthancStone::MouseWheelDirection_Up :
+                                                     OrthancStone::MouseWheelDirection_Down);
+      OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None;
+
+      if (isControl != 0)
+      {
+        modifiers = OrthancStone::KeyboardModifiers_Control;
+      }
+
+      viewport->MouseWheel(direction, x, y, modifiers);
+    }
+  }
+  
+
+  void EMSCRIPTEN_KEEPALIVE ViewportMouseMove(ViewportHandle viewport,
+                                              int x,
+                                              int y)
+  {
+    viewport->MouseMove(x, y);
+  }
+  
+  void EMSCRIPTEN_KEEPALIVE ViewportKeyPressed(ViewportHandle viewport,
+                                               const char* key, 
+                                               bool isShiftPressed, 
+                                               bool isControlPressed,
+                                               bool isAltPressed)
+                                               
+  {
+    OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None;
+    if (isShiftPressed) {
+      modifiers = static_cast<OrthancStone::KeyboardModifiers>(modifiers + OrthancStone::KeyboardModifiers_Shift);
+    }
+    if (isControlPressed) {
+      modifiers = static_cast<OrthancStone::KeyboardModifiers>(modifiers + OrthancStone::KeyboardModifiers_Control);
+    }
+    if (isAltPressed) {
+      modifiers = static_cast<OrthancStone::KeyboardModifiers>(modifiers + OrthancStone::KeyboardModifiers_Alt);
+    }
+    printf("key pressed : %c\n", key[0]);
+    viewport->KeyPressed(key[0], modifiers);
+  }
+  
+
+  void EMSCRIPTEN_KEEPALIVE ViewportMouseUp(ViewportHandle viewport)
+  {
+    viewport->MouseUp();
+  }
+  
+
+  void EMSCRIPTEN_KEEPALIVE ViewportMouseEnter(ViewportHandle viewport)
+  {
+    viewport->MouseEnter();
+  }
+  
+
+  void EMSCRIPTEN_KEEPALIVE ViewportMouseLeave(ViewportHandle viewport)
+  {
+    viewport->MouseLeave();
+  }
+
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Platforms/Wasm/Defaults.h	Tue Jun 19 16:06:32 2018 +0200
@@ -0,0 +1,74 @@
+#pragma once
+
+#include <emscripten/emscripten.h>
+
+#include <Framework/dev.h>
+#include <Framework/Viewport/WidgetViewport.h>
+#include <Framework/Widgets/LayerWidget.h>
+#include <Framework/Widgets/LayoutWidget.h>
+#include <Applications/Wasm/BasicWasmApplication.h>
+#include <Applications/Wasm/BasicWasmApplicationContext.h>
+
+typedef OrthancStone::WidgetViewport* ViewportHandle; // the objects exchanged between JS and C++
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+  
+  // JS methods accessible from C++
+  extern void ScheduleWebViewportRedrawFromCpp(ViewportHandle cppViewportHandle);
+  
+  // C++ methods accessible from JS
+  extern void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle cppViewportHandle);
+
+#ifdef __cplusplus
+}
+#endif
+
+extern OrthancStone::BasicWasmApplication* CreateUserApplication();
+
+namespace OrthancStone {
+
+  // default Ovserver to trigger Viewport redraw when something changes in the Viewport
+  class ChangeObserver :
+    public OrthancStone::IViewport::IObserver
+  {
+  private:
+    // Flag to avoid flooding JavaScript with redundant Redraw requests
+    bool isScheduled_; 
+
+  public:
+    ChangeObserver() :
+      isScheduled_(false)
+    {
+    }
+
+    void Reset()
+    {
+      isScheduled_ = false;
+    }
+
+    virtual void NotifyChange(const OrthancStone::IViewport &viewport)
+    {
+      if (!isScheduled_)
+      {
+        ScheduleWebViewportRedrawFromCpp((ViewportHandle)&viewport);  // loosing constness when transmitted to Web
+        isScheduled_ = true;
+      }
+    }
+  };
+
+  // default status bar to log messages on the console/stdout
+  class StatusBar : public OrthancStone::IStatusBar
+  {
+  public:
+    virtual void ClearMessage()
+    {
+    }
+
+    virtual void SetMessage(const std::string& message)
+    {
+      printf("%s\n", message.c_str());
+    }
+  };
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Platforms/Wasm/WasmViewport.h	Tue Jun 19 16:06:32 2018 +0200
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <Framework/Viewport/WidgetViewport.h>
+
+#include <emscripten/emscripten.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  // JS methods accessible from C++
+  extern OrthancStone::WidgetViewport* CreateWasmViewportFromCpp(const char* htmlCanvasId);
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Platforms/Wasm/WasmWebService.cpp	Tue Jun 19 16:06:32 2018 +0200
@@ -0,0 +1,90 @@
+#include "WasmWebService.h"
+
+#include <emscripten/emscripten.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  extern void WasmWebService_ScheduleGetRequest(void* callback,
+                                                const char* uri,
+                                                void* payload);
+  
+  extern void WasmWebService_SchedulePostRequest(void* callback,
+                                                 const char* uri,
+                                                 const void* body,
+                                                 size_t bodySize,
+                                                 void* payload);
+
+  void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyError(void* callback,
+                                                       const char* uri,
+                                                       void* payload)
+  {
+    if (callback == NULL)
+    {
+      throw;
+    }
+    else
+    {
+      reinterpret_cast<OrthancStone::IWebService::ICallback*>(callback)->
+        NotifyError(uri, reinterpret_cast<Orthanc::IDynamicObject*>(payload));
+    }
+  }
+
+  void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifySuccess(void* callback,
+                                                         const char* uri,
+                                                         const void* body,
+                                                         size_t bodySize,
+                                                         void* payload)
+  {
+    if (callback == NULL)
+    {
+      throw;
+    }
+    else
+    {
+      reinterpret_cast<OrthancStone::IWebService::ICallback*>(callback)->
+        NotifySuccess(uri, body, bodySize, reinterpret_cast<Orthanc::IDynamicObject*>(payload)); 
+   }
+  }
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+namespace OrthancStone
+{
+  void WasmWebService::SetBaseUrl(const std::string base)
+  {
+    // Make sure the base url ends with "/"
+    if (base.empty() ||
+        base[base.size() - 1] != '/')
+    {
+      base_ = base + "/";
+    }
+    else
+    {
+      base_ = base;
+    }
+  }
+
+  void WasmWebService::ScheduleGetRequest(ICallback& callback,
+                                          const std::string& uri,
+                                          Orthanc::IDynamicObject* payload)
+  {
+    std::string url = base_ + uri;
+    WasmWebService_ScheduleGetRequest(&callback, url.c_str(), payload);
+  }
+
+  void WasmWebService::SchedulePostRequest(ICallback& callback,
+                                           const std::string& uri,
+                                           const std::string& body,
+                                           Orthanc::IDynamicObject* payload)
+  {
+    std::string url = base_ + uri;
+    WasmWebService_SchedulePostRequest(&callback, url.c_str(),
+                                       body.c_str(), body.size(), payload);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Platforms/Wasm/WasmWebService.h	Tue Jun 19 16:06:32 2018 +0200
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <Framework/Toolbox/IWebService.h>
+
+namespace OrthancStone
+{
+  class WasmWebService : public IWebService
+  {
+  private:
+    std::string  base_;
+
+    // Private constructor => Singleton design pattern
+    WasmWebService() :
+      base_("../../")
+    {
+    }
+
+  public:
+    static WasmWebService& GetInstance()
+    {
+      static WasmWebService instance;
+      return instance;
+    }
+
+    void SetBaseUrl(const std::string base);
+
+    virtual void ScheduleGetRequest(ICallback& callback,
+                                    const std::string& uri,
+                                    Orthanc::IDynamicObject* payload);
+
+    virtual void SchedulePostRequest(ICallback& callback,
+                                     const std::string& uri,
+                                     const std::string& body,
+                                     Orthanc::IDynamicObject* payload);
+
+    virtual void Start()
+    {
+    }
+    
+    virtual void Stop()
+    {
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Platforms/Wasm/WasmWebService.js	Tue Jun 19 16:06:32 2018 +0200
@@ -0,0 +1,47 @@
+mergeInto(LibraryManager.library, {
+  WasmWebService_ScheduleGetRequest: function(callback, url, payload) {
+    // Directly use XMLHttpRequest (no jQuery) to retrieve the raw binary data
+    // http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/
+    var xhr = new XMLHttpRequest();
+    var tmp = UTF8ToString(url);
+    xhr.open('GET', tmp, true);
+    xhr.responseType = 'arraybuffer';
+
+    xhr.onreadystatechange = function() {
+      if (this.readyState == XMLHttpRequest.DONE) {
+        if (xhr.status === 200) {
+          // TODO - Is "new Uint8Array()" necessary? This copies the
+          // answer to the WebAssembly stack, hence necessitating
+          // increasing the TOTAL_STACK parameter of Emscripten
+          WasmWebService_NotifySuccess(callback, tmp, new Uint8Array(this.response),
+                                       this.response.byteLength, payload);
+        } else {
+          WasmWebService_NotifyError(callback, tmp, payload);
+        }
+      }
+    }
+    
+    xhr.send();
+  },
+
+  WasmWebService_SchedulePostRequest: function(callback, url, body, bodySize, payload) {
+    var xhr = new XMLHttpRequest();
+    var tmp = UTF8ToString(url);
+    xhr.open('POST', tmp, true);
+    xhr.responseType = 'arraybuffer';
+    xhr.setRequestHeader('Content-type', 'application/octet-stream');
+    
+    xhr.onreadystatechange = function() {
+      if (this.readyState == XMLHttpRequest.DONE) {
+        if (xhr.status === 200) {
+          WasmWebService_NotifySuccess(callback, tmp, new Uint8Array(this.response),
+                                       this.response.byteLength, payload);
+        } else {
+          WasmWebService_NotifyError(callback, tmp, payload);
+        }
+      }
+    }
+
+    xhr.send(new Uint8ClampedArray(HEAPU8.buffer, body, bodySize));
+  }
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Platforms/Wasm/default-library.js	Tue Jun 19 16:06:32 2018 +0200
@@ -0,0 +1,11 @@
+// this file contains the JS method you want to expose to C++ code
+
+mergeInto(LibraryManager.library, {
+  ScheduleWebViewportRedrawFromCpp: function(cppViewportHandle) {
+    ScheduleWebViewportRedraw(cppViewportHandle);
+  },
+  CreateWasmViewportFromCpp: function(htmlCanvasId) {
+    return CreateWasmViewport(htmlCanvasId);
+  }
+});
+  
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Platforms/Wasm/stone-framework-loader.ts	Tue Jun 19 16:06:32 2018 +0200
@@ -0,0 +1,90 @@
+module Stone {
+    /**
+     * This file contains primitives to interface with WebAssembly and
+     * with the Stone framework.
+     **/
+    
+    export declare type InitializationCallback = () => void;
+    
+    export declare var StoneFrameworkModule : any;
+    
+    //const ASSETS_FOLDER : string = "assets/lib";
+    //const WASM_FILENAME : string = "orthanc-framework";
+    
+    
+    export class Framework
+    {
+      private static singleton_ : Framework = null;
+    
+      private constructor(verbose : boolean) 
+      {
+        //this.ccall('Initialize', null, [ 'number' ], [ verbose ]);
+      }
+    
+      
+      public ccall(name: string,
+                   returnType: string,
+                   argTypes: Array<string>,
+                   argValues: Array<any>) : any
+      {
+        return StoneFrameworkModule.ccall(name, returnType, argTypes, argValues);
+      }
+    
+      
+      public cwrap(name: string,
+                   returnType: string,
+                   argTypes: Array<string>) : any
+      {
+        return StoneFrameworkModule.cwrap(name, returnType, argTypes);
+      }
+    
+      
+      public static GetInstance() : Framework
+      {
+        if (Framework.singleton_ == null) {
+          throw new Error('The WebAssembly module is not loaded yet');
+        } else {
+          return Framework.singleton_;
+        }
+      }
+      
+    
+      public static Initialize(verbose: boolean,
+                               callback: InitializationCallback)
+      {
+        console.log('Initializing WebAssembly');
+    
+        (<any> window).StoneFrameworkModule = {
+          preRun: [ 
+            function() {
+              console.log('Loading the Stone Framework using WebAssembly');
+            }
+          ],
+          postRun: [ 
+            function()  {
+              // This function is called by ".js" wrapper once the ".wasm"
+              // WebAssembly module has been loaded and compiled by the
+              // browser
+              console.log('WebAssembly is ready');
+              Framework.singleton_ = new Framework(verbose);
+              callback();
+            }
+          ],
+          print: function(text : string) {
+            console.log(text);
+          },
+          printErr: function(text : string) {
+            console.error(text);
+          },
+          totalDependencies: 0
+        };
+    
+        // Dynamic loading of the JavaScript wrapper around WebAssembly
+        var script = document.createElement('script');
+        script.type = 'application/javascript';
+        script.src = "orthanc-stone.js"; // ASSETS_FOLDER + '/' + WASM_FILENAME + '.js';
+        script.async = true;
+        document.head.appendChild(script);
+      }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Platforms/Wasm/wasm-application.ts	Tue Jun 19 16:06:32 2018 +0200
@@ -0,0 +1,119 @@
+///<reference path='stone-framework-loader.ts'/>
+///<reference path='wasm-viewport.ts'/>
+
+if (!('WebAssembly' in window)) {
+    alert('Sorry, your browser does not support WebAssembly :(');
+}
+
+declare var StoneFrameworkModule : Stone.Framework;
+
+// global functions
+var WasmWebService_NotifyError: Function = null;
+var WasmWebService_NotifySuccess: Function = null;
+var NotifyUpdateContent: Function = null;
+var SetStartupParameter: Function = null;
+var CreateWasmApplication: Function = null;
+var CreateCppViewport: Function = null;
+var ReleaseCppViewport: Function = null;
+var StartWasmApplication: Function = null;
+
+
+function UpdateContentThread() {
+    if (NotifyUpdateContent != null) {
+        NotifyUpdateContent();
+    }
+
+    setTimeout(UpdateContentThread, 100);  // Update the viewport content every 100ms if need be
+}
+
+
+function GetUriParameters() {
+    var parameters = window.location.search.substr(1);
+
+    if (parameters != null &&
+        parameters != '') {
+        var result = {};
+        var tokens = parameters.split('&');
+
+        for (var i = 0; i < tokens.length; i++) {
+            var tmp = tokens[i].split('=');
+            if (tmp.length == 2) {
+                result[tmp[0]] = decodeURIComponent(tmp[1]);
+            }
+        }
+
+        return result;
+    }
+    else {
+        return {};
+    }
+}
+
+module Stone {
+
+    //  export declare type InitializationCallback = () => void;
+
+    //  export declare var StoneFrameworkModule : any;
+
+    //const ASSETS_FOLDER : string = "assets/lib";
+    //const WASM_FILENAME : string = "orthanc-framework";
+
+    export class WasmApplication {
+
+        private viewport_: WasmViewport;
+        private canvasId_: string;
+
+        private pimpl_: any; // Private pointer to the underlying WebAssembly C++ object
+
+        public constructor(canvasId: string) {
+            this.canvasId_ = canvasId;
+            //this.module_ = module;
+        }
+    }
+}
+
+
+function InitializeWasmApplication(canvasId: string): void {
+
+    /************************************** */
+    CreateWasmApplication();
+
+    // parse uri and transmit the parameters to the app before initializing it
+    var parameters = GetUriParameters();
+
+    for (var key in parameters) {
+        if (parameters.hasOwnProperty(key)) {
+            SetStartupParameter(key, parameters[key]);
+        }
+    }
+
+    StartWasmApplication();
+    /************************************** */
+
+    UpdateContentThread();
+}
+
+// Wait for the Orthanc Framework to be initialized (this initializes
+// the WebAssembly environment) and then, create and initialize the Wasm application
+Stone.Framework.Initialize(true, function () {
+
+    console.log("Connecting C++ methods to JS methods");
+    
+    SetStartupParameter = StoneFrameworkModule.cwrap('SetStartupParameter', null, ['string', 'string']);
+    CreateWasmApplication = StoneFrameworkModule.cwrap('CreateWasmApplication', null, ['number']);
+    CreateCppViewport = StoneFrameworkModule.cwrap('CreateCppViewport', 'number', []);
+    ReleaseCppViewport = StoneFrameworkModule.cwrap('ReleaseCppViewport', null, ['number']);
+    StartWasmApplication = StoneFrameworkModule.cwrap('StartWasmApplication', null, ['number']);
+
+    WasmWebService_NotifySuccess = StoneFrameworkModule.cwrap('WasmWebService_NotifySuccess', null, ['number', 'string', 'array', 'number', 'number']);
+    WasmWebService_NotifyError = StoneFrameworkModule.cwrap('WasmWebService_NotifyError', null, ['number', 'string', 'number']);
+    NotifyUpdateContent = StoneFrameworkModule.cwrap('NotifyUpdateContent', null, []);
+
+    // Prevent scrolling
+    document.body.addEventListener('touchmove', function (event) {
+        event.preventDefault();
+    }, false);
+
+
+    InitializeWasmApplication("canvas");
+});
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Platforms/Wasm/wasm-viewport.ts	Tue Jun 19 16:06:32 2018 +0200
@@ -0,0 +1,269 @@
+var isPendingRedraw = false;
+
+function ScheduleWebViewportRedraw(cppViewportHandle: any) : void
+{
+  if (!isPendingRedraw) {
+    isPendingRedraw = true;
+    console.log('Scheduling a refresh of the viewport, as its content changed');
+    window.requestAnimationFrame(function() {
+      isPendingRedraw = false;
+      Stone.WasmViewport.GetFromCppViewport(cppViewportHandle).Redraw();
+    });
+  }
+}
+
+declare function UTF8ToString(any): string;
+
+function CreateWasmViewport(htmlCanvasId: string) : any {
+  var cppViewportHandle = CreateCppViewport();
+  var canvasId = UTF8ToString(htmlCanvasId);
+  var webViewport = new Stone.WasmViewport(StoneFrameworkModule, canvasId, cppViewportHandle);  // viewports are stored in a static map in WasmViewport -> won't be deleted
+  webViewport.Initialize();
+
+  return cppViewportHandle;
+}
+
+module Stone {
+  
+//  export declare type InitializationCallback = () => void;
+  
+//  export declare var StoneFrameworkModule : any;
+  
+  //const ASSETS_FOLDER : string = "assets/lib";
+  //const WASM_FILENAME : string = "orthanc-framework";
+
+  export class WasmViewport {
+
+    private static cppWebViewportsMaps_ : Map<number, WasmViewport> = new Map<number, WasmViewport>();
+
+    private module_ : any;
+    private canvasId_ : string;
+    private htmlCanvas_ : HTMLCanvasElement;
+    private context_ : CanvasRenderingContext2D;
+    private imageData_ : any = null;
+    private renderingBuffer_ : any = null;
+    private touchZoom_ : any = false;
+    private touchTranslation_ : any = false;
+
+    private ViewportSetSize : Function;
+    private ViewportRender : Function;
+    private ViewportMouseDown : Function;
+    private ViewportMouseMove : Function;
+    private ViewportMouseUp : Function;
+    private ViewportMouseEnter : Function;
+    private ViewportMouseLeave : Function;
+    private ViewportMouseWheel : Function;
+    private ViewportKeyPressed : Function;
+
+    private pimpl_ : any; // Private pointer to the underlying WebAssembly C++ object
+
+    public constructor(module: any, canvasId: string, cppViewport: any) {
+      
+      this.pimpl_ = cppViewport;
+      WasmViewport.cppWebViewportsMaps_[this.pimpl_] = this;
+
+      this.module_ = module;
+      this.canvasId_ = canvasId;
+      this.htmlCanvas_ = document.getElementById(this.canvasId_) as HTMLCanvasElement;
+      this.context_ = this.htmlCanvas_.getContext('2d');
+
+      this.ViewportSetSize = this.module_.cwrap('ViewportSetSize', null, [ 'number', 'number', 'number' ]);
+      this.ViewportRender = this.module_.cwrap('ViewportRender', null, [ 'number', 'number', 'number', 'number' ]);
+      this.ViewportMouseDown = this.module_.cwrap('ViewportMouseDown', null, [ 'number', 'number', 'number', 'number', 'number' ]);
+      this.ViewportMouseMove = this.module_.cwrap('ViewportMouseMove', null, [ 'number', 'number', 'number' ]);
+      this.ViewportMouseUp = this.module_.cwrap('ViewportMouseUp', null, [ 'number' ]);
+      this.ViewportMouseEnter = this.module_.cwrap('ViewportMouseEnter', null, [ 'number' ]);
+      this.ViewportMouseLeave = this.module_.cwrap('ViewportMouseLeave', null, [ 'number' ]);
+      this.ViewportMouseWheel = this.module_.cwrap('ViewportMouseWheel', null, [ 'number', 'number', 'number', 'number', 'number' ]);
+      this.ViewportKeyPressed = this.module_.cwrap('ViewportKeyPressed', null, [ 'number', 'string', 'number', 'number' ]);
+    }
+
+    public GetCppViewport() : number {
+      return this.pimpl_;
+    }
+
+    public static GetFromCppViewport(cppViewportHandle: number) : WasmViewport {
+      if (WasmViewport.cppWebViewportsMaps_[cppViewportHandle] !== undefined) {
+        return WasmViewport.cppWebViewportsMaps_[cppViewportHandle];
+      }
+      console.log("WasmViewport not found !");
+      return undefined;
+    }
+
+    public Redraw() {
+      if (this.imageData_ === null ||
+          this.renderingBuffer_ === null ||
+          this.ViewportRender(this.pimpl_,
+                         this.imageData_.width,
+                         this.imageData_.height,
+                         this.renderingBuffer_) == 0) {
+        console.log('The rendering has failed');
+      } else {
+        // Create an accessor to the rendering buffer (i.e. create a
+        // "window" above the heap of the WASM module), then copy it to
+        // the ImageData object
+        this.imageData_.data.set(new Uint8ClampedArray(
+          this.module_.buffer,
+          this.renderingBuffer_,
+          this.imageData_.width * this.imageData_.height * 4));
+        
+        this.context_.putImageData(this.imageData_, 0, 0);
+      }
+    }
+  
+    public Resize() {
+      if (this.imageData_ != null &&
+          (this.imageData_.width != window.innerWidth ||
+           this.imageData_.height != window.innerHeight)) {
+        this.imageData_ = null;
+      }
+      
+      // width/height can be defined in percent of window width/height through html attributes like data-width-ratio="50" and data-height-ratio="20"
+      var widthRatio = Number(this.htmlCanvas_.dataset["widthRatio"]) || 100;
+      var heightRatio = Number(this.htmlCanvas_.dataset["heightRatio"]) || 100;
+
+      this.htmlCanvas_.width = window.innerWidth * (widthRatio / 100);  
+      this.htmlCanvas_.height = window.innerHeight * (heightRatio / 100);
+
+      console.log("resizing WasmViewport: ", this.htmlCanvas_.width, "x", this.htmlCanvas_.height);
+
+      if (this.imageData_ === null) {
+        this.imageData_ = this.context_.getImageData(0, 0, this.htmlCanvas_.width, this.htmlCanvas_.height);
+        this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height);
+  
+        if (this.renderingBuffer_ != null) {
+          this.module_._free(this.renderingBuffer_);
+        }
+        
+        this.renderingBuffer_ = this.module_._malloc(this.imageData_.width * this.imageData_.height * 4);
+      }
+      
+      this.Redraw();
+    }
+
+    public Initialize() {
+      
+      // Force the rendering of the viewport for the first time
+      this.Resize();
+    
+      var that : WasmViewport = this;
+      // Register an event listener to call the Resize() function 
+      // each time the window is resized.
+      window.addEventListener('resize', function(event) {
+        that.Resize();
+      }, false);
+  
+      this.htmlCanvas_.addEventListener('contextmenu', function(event) {
+        // Prevent right click on the canvas
+        event.preventDefault();
+      }, false);
+      
+      this.htmlCanvas_.addEventListener('mouseleave', function(event) {
+        that.ViewportMouseLeave(that.pimpl_);
+      });
+      
+      this.htmlCanvas_.addEventListener('mouseenter', function(event) {
+        that.ViewportMouseEnter(that.pimpl_);
+      });
+    
+      this.htmlCanvas_.addEventListener('mousedown', function(event) {
+        var x = event.pageX - this.offsetLeft;
+        var y = event.pageY - this.offsetTop;
+        that.ViewportMouseDown(that.pimpl_, event.button, x, y, 0 /* TODO */);    
+      });
+    
+      this.htmlCanvas_.addEventListener('mousemove', function(event) {
+        var x = event.pageX - this.offsetLeft;
+        var y = event.pageY - this.offsetTop;
+        that.ViewportMouseMove(that.pimpl_, x, y);
+      });
+    
+      this.htmlCanvas_.addEventListener('mouseup', function(event) {
+        that.ViewportMouseUp(that.pimpl_);
+      });
+    
+      window.addEventListener('keydown', function(event) {
+        that.ViewportKeyPressed(that.pimpl_, event.key, event.shiftKey, event.ctrlKey, event.altKey);
+      });
+    
+      this.htmlCanvas_.addEventListener('wheel', function(event) {
+        var x = event.pageX - this.offsetLeft;
+        var y = event.pageY - this.offsetTop;
+        that.ViewportMouseWheel(that.pimpl_, event.deltaY, x, y, event.ctrlKey);
+        event.preventDefault();
+      });
+
+      this.htmlCanvas_.addEventListener('touchstart', function(event) {
+        that.ResetTouch();
+      });
+    
+      this.htmlCanvas_.addEventListener('touchend', function(event) {
+        that.ResetTouch();
+      });
+    
+      this.htmlCanvas_.addEventListener('touchmove', function(event) {
+        if (that.touchTranslation_.length == 2) {
+          var t = that.GetTouchTranslation(event);
+          that.ViewportMouseMove(that.pimpl_, t[0], t[1]);
+        }
+        else if (that.touchZoom_.length == 3) {
+          var z0 = that.touchZoom_;
+          var z1 = that.GetTouchZoom(event);
+          that.ViewportMouseMove(that.pimpl_, z0[0], z0[1] - z0[2] + z1[2]);
+        }
+        else {
+          // Realize the gesture event
+          if (event.targetTouches.length == 1) {
+            // Exactly one finger inside the canvas => Setup a translation
+            that.touchTranslation_ = that.GetTouchTranslation(event);
+            that.ViewportMouseDown(that.pimpl_, 
+                                  1 /* middle button */,
+                                  that.touchTranslation_[0],
+                                  that.touchTranslation_[1], 0);
+          } else if (event.targetTouches.length == 2) {
+            // Exactly 2 fingers inside the canvas => Setup a pinch/zoom
+            that.touchZoom_ = that.GetTouchZoom(event);
+            var z0 = that.touchZoom_;
+            that.ViewportMouseDown(that.pimpl_, 
+                                  2 /* right button */,
+                                  z0[0],
+                                  z0[1], 0);
+          }        
+        }
+      });
+    }  
+
+  public ResetTouch() {
+    if (this.touchTranslation_ ||
+        this.touchZoom_) {
+      this.ViewportMouseUp(this.pimpl_);
+    }
+
+    this.touchTranslation_ = false;
+    this.touchZoom_ = false;
+  }
+  
+  public GetTouchTranslation(event) {
+    var touch = event.targetTouches[0];
+    return [
+      touch.pageX,
+      touch.pageY
+    ];
+  }
+    
+  public GetTouchZoom(event) {
+    var touch1 = event.targetTouches[0];
+    var touch2 = event.targetTouches[1];
+    var dx = (touch1.pageX - touch2.pageX);
+    var dy = (touch1.pageY - touch2.pageY);
+    var d = Math.sqrt(dx * dx + dy * dy);
+    return [
+      (touch1.pageX + touch2.pageX) / 2.0,
+      (touch1.pageY + touch2.pageY) / 2.0,
+      d
+    ];
+  }
+    
+}
+}
+  
\ No newline at end of file
--- a/Platforms/WebAssembly/CMakeLists.txt	Tue Jun 19 16:02:41 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-# Usage (Linux):
-# source ~/Downloads/emsdk/emsdk_env.sh && cmake -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake ..
-
-cmake_minimum_required(VERSION 2.8.3)
-
-
-#####################################################################
-## Configuration of the Emscripten compiler for WebAssembly target
-#####################################################################
-
-set(WASM_FLAGS "-s WASM=1")
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}")
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}")
-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${CMAKE_SOURCE_DIR}/library.js")
-
-# Handling of memory
-#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1")  # Resize
-#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_MEMORY=536870912")  # 512MB
-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=536870912")  # 512MB + resize
-#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=1073741824")  # 1GB + resize
-
-# To debug exceptions
-#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DEMANGLE_SUPPORT=1 -s ASSERTIONS=2")
-
-
-#####################################################################
-## Build a static library containing the Orthanc Stone framework
-#####################################################################
-
-include(../../Resources/CMake/OrthancStoneParameters.cmake)
-
-SET(ORTHANC_SANDBOXED ON)
-SET(ENABLE_SDL OFF)
-
-include(../../Resources/CMake/OrthancStoneConfiguration.cmake)
-
-add_library(OrthancStone STATIC ${ORTHANC_STONE_SOURCES})
-
-
-
-
-
-
-# Regenerate a dummy "library.c" file each time the "library.js" file
-# is modified, so as to force a new execution of the linking
-add_custom_command(
-    OUTPUT "${AUTOGENERATED_DIR}/library.c"
-    COMMAND ${CMAKE_COMMAND} -E touch "${AUTOGENERATED_DIR}/library.c" ""
-    DEPENDS "${CMAKE_SOURCE_DIR}/library.js")
--- a/Platforms/WebAssembly/Defaults.cpp	Tue Jun 19 16:02:41 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,252 +0,0 @@
-#include "Defaults.h"
-
-#include "WasmWebService.h"
-#include <Framework/dev.h>
-#include "Framework/Widgets/TestCairoWidget.h"
-#include <Framework/Viewport/WidgetViewport.h>
-#include <Framework/Widgets/LayerWidget.h>
-#include <algorithm>
-
-static unsigned int width_ = 0;
-static unsigned int height_ = 0;
-
-/**********************************/
-
-static std::auto_ptr<OrthancStone::BasicWasmApplication> application;
-static OrthancStone::ChangeObserver changeObserver_;
-static OrthancStone::StatusBar statusBar_;
-
-
-static std::list<std::shared_ptr<OrthancStone::WidgetViewport>> viewports_;
-
-std::shared_ptr<OrthancStone::WidgetViewport> FindViewportSharedPtr(ViewportHandle viewport) {
-  for (const auto& v : viewports_) {
-    if (v.get() == viewport) {
-      return v;
-    }
-  }
-  assert(false);
-  return std::shared_ptr<OrthancStone::WidgetViewport>();
-}
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  using namespace OrthancStone;
-
-  // when WASM needs a C++ viewport
-  ViewportHandle EMSCRIPTEN_KEEPALIVE CreateCppViewport() {
-    
-    std::shared_ptr<OrthancStone::WidgetViewport> viewport(new OrthancStone::WidgetViewport);
-    printf("viewport %x\n", viewport.get());
-
-    viewports_.push_back(viewport);
-
-    printf("There are now %d viewports in C++\n", viewports_.size());
-
-    viewport->SetStatusBar(statusBar_);
-    viewport->Register(changeObserver_);
-
-    return viewport.get();
-  }
-
-  // when WASM does not need a viewport anymore, it should release it 
-  void EMSCRIPTEN_KEEPALIVE ReleaseCppViewport(ViewportHandle viewport) {
-    viewports_.remove_if([viewport](const std::shared_ptr<OrthancStone::WidgetViewport>& v) { return v.get() == viewport;});
-
-    printf("There are now %d viewports in C++\n", viewports_.size());
-  }
-
-  void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle viewport) {
-
-    printf("CreateWasmApplication\n");
-
-    application.reset(CreateUserApplication());
-
-    boost::program_options::options_description options;
-    application->DeclareStartupOptions(options);
-  }
-
-  void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc,
-                                                  const char* value) {
-    application->SetStartupParameter(keyc, value);
-  }
-
-  void EMSCRIPTEN_KEEPALIVE StartWasmApplication() {
-
-    printf("StartWasmApplication\n");
-
-    // recreate a command line from uri arguments and parse it
-    boost::program_options::variables_map parameters;
-    application->GetStartupParameters(parameters);
-
-    BasicWasmApplicationContext& context = dynamic_cast<BasicWasmApplicationContext&>(application->CreateApplicationContext(OrthancStone::WasmWebService::GetInstance(), NULL));
-    application->Initialize(statusBar_, parameters);
-
-//    viewport->SetSize(width_, height_);
-    printf("StartWasmApplication - completed\n");
-  }
-  
-  void EMSCRIPTEN_KEEPALIVE NotifyUpdateContent()
-  {
-    for (auto viewport : viewports_) {
-      // TODO Only launch the JavaScript timer if "HasUpdateContent()"
-      if (viewport->HasUpdateContent())
-      {
-        viewport->UpdateContent();
-      }
-
-    }
-
-  }
-  
-
-  void EMSCRIPTEN_KEEPALIVE ViewportSetSize(ViewportHandle viewport, unsigned int width, unsigned int height)
-  {
-    width_ = width;
-    height_ = height;
-    
-    viewport->SetSize(width, height);
-  }
-
-  int EMSCRIPTEN_KEEPALIVE ViewportRender(ViewportHandle viewport,
-                                          unsigned int width,
-                                          unsigned int height,
-                                          uint8_t* data)
-  {
-    changeObserver_.Reset();
-
-    //printf("ViewportRender called %dx%d\n", width, height);
-    if (width == 0 ||
-        height == 0)
-    {
-      return 1;
-    }
-
-    Orthanc::ImageAccessor surface;
-    surface.AssignWritable(Orthanc::PixelFormat_BGRA32, width, height, 4 * width, data);
-
-    viewport->Render(surface);
-
-    // Convert from BGRA32 memory layout (only color mode supported by
-    // Cairo, which corresponds to CAIRO_FORMAT_ARGB32) to RGBA32 (as
-    // expected by HTML5 canvas). This simply amounts to swapping the
-    // B and R channels.
-    uint8_t* p = data;
-    for (unsigned int y = 0; y < height; y++) {
-      for (unsigned int x = 0; x < width; x++) {
-        uint8_t tmp = p[0];
-        p[0] = p[2];
-        p[2] = tmp;
-        
-        p += 4;
-      }
-    }
-
-    return 1;
-  }
-
-
-  void EMSCRIPTEN_KEEPALIVE ViewportMouseDown(ViewportHandle viewport,
-                                              unsigned int rawButton,
-                                              int x,
-                                              int y,
-                                              unsigned int rawModifiers)
-  {
-    OrthancStone::MouseButton button;
-    switch (rawButton)
-    {
-      case 0:
-        button = OrthancStone::MouseButton_Left;
-        break;
-
-      case 1:
-        button = OrthancStone::MouseButton_Middle;
-        break;
-
-      case 2:
-        button = OrthancStone::MouseButton_Right;
-        break;
-
-      default:
-        return;  // Unknown button
-    }
-
-    viewport->MouseDown(button, x, y, OrthancStone::KeyboardModifiers_None /* TODO */);
-  }
-  
-
-  void EMSCRIPTEN_KEEPALIVE ViewportMouseWheel(ViewportHandle viewport,
-                                               int deltaY,
-                                               int x,
-                                               int y,
-                                               int isControl)
-  {
-    if (deltaY != 0)
-    {
-      OrthancStone::MouseWheelDirection direction = (deltaY < 0 ?
-                                                     OrthancStone::MouseWheelDirection_Up :
-                                                     OrthancStone::MouseWheelDirection_Down);
-      OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None;
-
-      if (isControl != 0)
-      {
-        modifiers = OrthancStone::KeyboardModifiers_Control;
-      }
-
-      viewport->MouseWheel(direction, x, y, modifiers);
-    }
-  }
-  
-
-  void EMSCRIPTEN_KEEPALIVE ViewportMouseMove(ViewportHandle viewport,
-                                              int x,
-                                              int y)
-  {
-    viewport->MouseMove(x, y);
-  }
-  
-  void EMSCRIPTEN_KEEPALIVE ViewportKeyPressed(ViewportHandle viewport,
-                                               const char* key, 
-                                               bool isShiftPressed, 
-                                               bool isControlPressed,
-                                               bool isAltPressed)
-                                               
-  {
-    OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None;
-    if (isShiftPressed) {
-      modifiers = static_cast<OrthancStone::KeyboardModifiers>(modifiers + OrthancStone::KeyboardModifiers_Shift);
-    }
-    if (isControlPressed) {
-      modifiers = static_cast<OrthancStone::KeyboardModifiers>(modifiers + OrthancStone::KeyboardModifiers_Control);
-    }
-    if (isAltPressed) {
-      modifiers = static_cast<OrthancStone::KeyboardModifiers>(modifiers + OrthancStone::KeyboardModifiers_Alt);
-    }
-    printf("key pressed : %c\n", key[0]);
-    viewport->KeyPressed(key[0], modifiers);
-  }
-  
-
-  void EMSCRIPTEN_KEEPALIVE ViewportMouseUp(ViewportHandle viewport)
-  {
-    viewport->MouseUp();
-  }
-  
-
-  void EMSCRIPTEN_KEEPALIVE ViewportMouseEnter(ViewportHandle viewport)
-  {
-    viewport->MouseEnter();
-  }
-  
-
-  void EMSCRIPTEN_KEEPALIVE ViewportMouseLeave(ViewportHandle viewport)
-  {
-    viewport->MouseLeave();
-  }
-
-
-#ifdef __cplusplus
-}
-#endif
--- a/Platforms/WebAssembly/Defaults.h	Tue Jun 19 16:02:41 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-#pragma once
-
-#include <emscripten/emscripten.h>
-
-#include <Framework/dev.h>
-#include <Framework/Viewport/WidgetViewport.h>
-#include <Framework/Widgets/LayerWidget.h>
-#include <Framework/Widgets/LayoutWidget.h>
-#include <Applications/Wasm/BasicWasmApplication.h>
-#include <Applications/Wasm/BasicWasmApplicationContext.h>
-
-typedef OrthancStone::WidgetViewport* ViewportHandle; // the objects exchanged between JS and C++
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-  
-  // JS methods accessible from C++
-  extern void ScheduleWebViewportRedrawFromCpp(ViewportHandle cppViewportHandle);
-  
-  // C++ methods accessible from JS
-  extern void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle cppViewportHandle);
-
-#ifdef __cplusplus
-}
-#endif
-
-extern OrthancStone::BasicWasmApplication* CreateUserApplication();
-
-namespace OrthancStone {
-
-  // default Ovserver to trigger Viewport redraw when something changes in the Viewport
-  class ChangeObserver :
-    public OrthancStone::IViewport::IObserver
-  {
-  private:
-    // Flag to avoid flooding JavaScript with redundant Redraw requests
-    bool isScheduled_; 
-
-  public:
-    ChangeObserver() :
-      isScheduled_(false)
-    {
-    }
-
-    void Reset()
-    {
-      isScheduled_ = false;
-    }
-
-    virtual void NotifyChange(const OrthancStone::IViewport &viewport)
-    {
-      if (!isScheduled_)
-      {
-        ScheduleWebViewportRedrawFromCpp((ViewportHandle)&viewport);  // loosing constness when transmitted to Web
-        isScheduled_ = true;
-      }
-    }
-  };
-
-  // default status bar to log messages on the console/stdout
-  class StatusBar : public OrthancStone::IStatusBar
-  {
-  public:
-    virtual void ClearMessage()
-    {
-    }
-
-    virtual void SetMessage(const std::string& message)
-    {
-      printf("%s\n", message.c_str());
-    }
-  };
-}
\ No newline at end of file
--- a/Platforms/WebAssembly/WasmViewport.h	Tue Jun 19 16:02:41 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-#pragma once
-
-#include <Framework/Viewport/WidgetViewport.h>
-
-#include <emscripten/emscripten.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  // JS methods accessible from C++
-  extern OrthancStone::WidgetViewport* CreateWasmViewportFromCpp(const char* htmlCanvasId);
-
-#ifdef __cplusplus
-}
-#endif
\ No newline at end of file
--- a/Platforms/WebAssembly/WasmWebService.cpp	Tue Jun 19 16:02:41 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-#include "WasmWebService.h"
-
-#include <emscripten/emscripten.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  extern void WasmWebService_ScheduleGetRequest(void* callback,
-                                                const char* uri,
-                                                void* payload);
-  
-  extern void WasmWebService_SchedulePostRequest(void* callback,
-                                                 const char* uri,
-                                                 const void* body,
-                                                 size_t bodySize,
-                                                 void* payload);
-
-  void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyError(void* callback,
-                                                       const char* uri,
-                                                       void* payload)
-  {
-    if (callback == NULL)
-    {
-      throw;
-    }
-    else
-    {
-      reinterpret_cast<OrthancStone::IWebService::ICallback*>(callback)->
-        NotifyError(uri, reinterpret_cast<Orthanc::IDynamicObject*>(payload));
-    }
-  }
-
-  void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifySuccess(void* callback,
-                                                         const char* uri,
-                                                         const void* body,
-                                                         size_t bodySize,
-                                                         void* payload)
-  {
-    if (callback == NULL)
-    {
-      throw;
-    }
-    else
-    {
-      reinterpret_cast<OrthancStone::IWebService::ICallback*>(callback)->
-        NotifySuccess(uri, body, bodySize, reinterpret_cast<Orthanc::IDynamicObject*>(payload)); 
-   }
-  }
-
-#ifdef __cplusplus
-}
-#endif
-
-
-
-namespace OrthancStone
-{
-  void WasmWebService::SetBaseUrl(const std::string base)
-  {
-    // Make sure the base url ends with "/"
-    if (base.empty() ||
-        base[base.size() - 1] != '/')
-    {
-      base_ = base + "/";
-    }
-    else
-    {
-      base_ = base;
-    }
-  }
-
-  void WasmWebService::ScheduleGetRequest(ICallback& callback,
-                                          const std::string& uri,
-                                          Orthanc::IDynamicObject* payload)
-  {
-    std::string url = base_ + uri;
-    WasmWebService_ScheduleGetRequest(&callback, url.c_str(), payload);
-  }
-
-  void WasmWebService::SchedulePostRequest(ICallback& callback,
-                                           const std::string& uri,
-                                           const std::string& body,
-                                           Orthanc::IDynamicObject* payload)
-  {
-    std::string url = base_ + uri;
-    WasmWebService_SchedulePostRequest(&callback, url.c_str(),
-                                       body.c_str(), body.size(), payload);
-  }
-}
--- a/Platforms/WebAssembly/WasmWebService.h	Tue Jun 19 16:02:41 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-#pragma once
-
-#include <Framework/Toolbox/IWebService.h>
-
-namespace OrthancStone
-{
-  class WasmWebService : public IWebService
-  {
-  private:
-    std::string  base_;
-
-    // Private constructor => Singleton design pattern
-    WasmWebService() :
-      base_("../../")
-    {
-    }
-
-  public:
-    static WasmWebService& GetInstance()
-    {
-      static WasmWebService instance;
-      return instance;
-    }
-
-    void SetBaseUrl(const std::string base);
-
-    virtual void ScheduleGetRequest(ICallback& callback,
-                                    const std::string& uri,
-                                    Orthanc::IDynamicObject* payload);
-
-    virtual void SchedulePostRequest(ICallback& callback,
-                                     const std::string& uri,
-                                     const std::string& body,
-                                     Orthanc::IDynamicObject* payload);
-
-    virtual void Start()
-    {
-    }
-    
-    virtual void Stop()
-    {
-    }
-  };
-}
--- a/Platforms/WebAssembly/WasmWebService.js	Tue Jun 19 16:02:41 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-mergeInto(LibraryManager.library, {
-  WasmWebService_ScheduleGetRequest: function(callback, url, payload) {
-    // Directly use XMLHttpRequest (no jQuery) to retrieve the raw binary data
-    // http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/
-    var xhr = new XMLHttpRequest();
-    var tmp = UTF8ToString(url);
-    xhr.open('GET', tmp, true);
-    xhr.responseType = 'arraybuffer';
-
-    xhr.onreadystatechange = function() {
-      if (this.readyState == XMLHttpRequest.DONE) {
-        if (xhr.status === 200) {
-          // TODO - Is "new Uint8Array()" necessary? This copies the
-          // answer to the WebAssembly stack, hence necessitating
-          // increasing the TOTAL_STACK parameter of Emscripten
-          WasmWebService_NotifySuccess(callback, tmp, new Uint8Array(this.response),
-                                       this.response.byteLength, payload);
-        } else {
-          WasmWebService_NotifyError(callback, tmp, payload);
-        }
-      }
-    }
-    
-    xhr.send();
-  },
-
-  WasmWebService_SchedulePostRequest: function(callback, url, body, bodySize, payload) {
-    var xhr = new XMLHttpRequest();
-    var tmp = UTF8ToString(url);
-    xhr.open('POST', tmp, true);
-    xhr.responseType = 'arraybuffer';
-    xhr.setRequestHeader('Content-type', 'application/octet-stream');
-    
-    xhr.onreadystatechange = function() {
-      if (this.readyState == XMLHttpRequest.DONE) {
-        if (xhr.status === 200) {
-          WasmWebService_NotifySuccess(callback, tmp, new Uint8Array(this.response),
-                                       this.response.byteLength, payload);
-        } else {
-          WasmWebService_NotifyError(callback, tmp, payload);
-        }
-      }
-    }
-
-    xhr.send(new Uint8ClampedArray(HEAPU8.buffer, body, bodySize));
-  }
-});
--- a/Platforms/WebAssembly/default-library.js	Tue Jun 19 16:02:41 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-// this file contains the JS method you want to expose to C++ code
-
-mergeInto(LibraryManager.library, {
-  ScheduleWebViewportRedrawFromCpp: function(cppViewportHandle) {
-    ScheduleWebViewportRedraw(cppViewportHandle);
-  },
-  CreateWasmViewportFromCpp: function(htmlCanvasId) {
-    return CreateWasmViewport(htmlCanvasId);
-  }
-});
-  
\ No newline at end of file
--- a/Platforms/WebAssembly/stone-framework-loader.ts	Tue Jun 19 16:02:41 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-module Stone {
-    /**
-     * This file contains primitives to interface with WebAssembly and
-     * with the Stone framework.
-     **/
-    
-    export declare type InitializationCallback = () => void;
-    
-    export declare var StoneFrameworkModule : any;
-    
-    //const ASSETS_FOLDER : string = "assets/lib";
-    //const WASM_FILENAME : string = "orthanc-framework";
-    
-    
-    export class Framework
-    {
-      private static singleton_ : Framework = null;
-    
-      private constructor(verbose : boolean) 
-      {
-        //this.ccall('Initialize', null, [ 'number' ], [ verbose ]);
-      }
-    
-      
-      public ccall(name: string,
-                   returnType: string,
-                   argTypes: Array<string>,
-                   argValues: Array<any>) : any
-      {
-        return StoneFrameworkModule.ccall(name, returnType, argTypes, argValues);
-      }
-    
-      
-      public cwrap(name: string,
-                   returnType: string,
-                   argTypes: Array<string>) : any
-      {
-        return StoneFrameworkModule.cwrap(name, returnType, argTypes);
-      }
-    
-      
-      public static GetInstance() : Framework
-      {
-        if (Framework.singleton_ == null) {
-          throw new Error('The WebAssembly module is not loaded yet');
-        } else {
-          return Framework.singleton_;
-        }
-      }
-      
-    
-      public static Initialize(verbose: boolean,
-                               callback: InitializationCallback)
-      {
-        console.log('Initializing WebAssembly');
-    
-        (<any> window).StoneFrameworkModule = {
-          preRun: [ 
-            function() {
-              console.log('Loading the Stone Framework using WebAssembly');
-            }
-          ],
-          postRun: [ 
-            function()  {
-              // This function is called by ".js" wrapper once the ".wasm"
-              // WebAssembly module has been loaded and compiled by the
-              // browser
-              console.log('WebAssembly is ready');
-              Framework.singleton_ = new Framework(verbose);
-              callback();
-            }
-          ],
-          print: function(text : string) {
-            console.log(text);
-          },
-          printErr: function(text : string) {
-            console.error(text);
-          },
-          totalDependencies: 0
-        };
-    
-        // Dynamic loading of the JavaScript wrapper around WebAssembly
-        var script = document.createElement('script');
-        script.type = 'application/javascript';
-        script.src = "orthanc-stone.js"; // ASSETS_FOLDER + '/' + WASM_FILENAME + '.js';
-        script.async = true;
-        document.head.appendChild(script);
-      }
-    }
-}
\ No newline at end of file
--- a/Platforms/WebAssembly/wasm-application.ts	Tue Jun 19 16:02:41 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-///<reference path='stone-framework-loader.ts'/>
-///<reference path='wasm-viewport.ts'/>
-
-if (!('WebAssembly' in window)) {
-    alert('Sorry, your browser does not support WebAssembly :(');
-}
-
-declare var StoneFrameworkModule : Stone.Framework;
-
-// global functions
-var WasmWebService_NotifyError: Function = null;
-var WasmWebService_NotifySuccess: Function = null;
-var NotifyUpdateContent: Function = null;
-var SetStartupParameter: Function = null;
-var CreateWasmApplication: Function = null;
-var CreateCppViewport: Function = null;
-var ReleaseCppViewport: Function = null;
-var StartWasmApplication: Function = null;
-
-
-function UpdateContentThread() {
-    if (NotifyUpdateContent != null) {
-        NotifyUpdateContent();
-    }
-
-    setTimeout(UpdateContentThread, 100);  // Update the viewport content every 100ms if need be
-}
-
-
-function GetUriParameters() {
-    var parameters = window.location.search.substr(1);
-
-    if (parameters != null &&
-        parameters != '') {
-        var result = {};
-        var tokens = parameters.split('&');
-
-        for (var i = 0; i < tokens.length; i++) {
-            var tmp = tokens[i].split('=');
-            if (tmp.length == 2) {
-                result[tmp[0]] = decodeURIComponent(tmp[1]);
-            }
-        }
-
-        return result;
-    }
-    else {
-        return {};
-    }
-}
-
-module Stone {
-
-    //  export declare type InitializationCallback = () => void;
-
-    //  export declare var StoneFrameworkModule : any;
-
-    //const ASSETS_FOLDER : string = "assets/lib";
-    //const WASM_FILENAME : string = "orthanc-framework";
-
-    export class WasmApplication {
-
-        private viewport_: WasmViewport;
-        private canvasId_: string;
-
-        private pimpl_: any; // Private pointer to the underlying WebAssembly C++ object
-
-        public constructor(canvasId: string) {
-            this.canvasId_ = canvasId;
-            //this.module_ = module;
-        }
-    }
-}
-
-
-function InitializeWasmApplication(canvasId: string): void {
-
-    /************************************** */
-    CreateWasmApplication();
-
-    // parse uri and transmit the parameters to the app before initializing it
-    var parameters = GetUriParameters();
-
-    for (var key in parameters) {
-        if (parameters.hasOwnProperty(key)) {
-            SetStartupParameter(key, parameters[key]);
-        }
-    }
-
-    StartWasmApplication();
-    /************************************** */
-
-    UpdateContentThread();
-}
-
-// Wait for the Orthanc Framework to be initialized (this initializes
-// the WebAssembly environment) and then, create and initialize the Wasm application
-Stone.Framework.Initialize(true, function () {
-
-    console.log("Connecting C++ methods to JS methods");
-    
-    SetStartupParameter = StoneFrameworkModule.cwrap('SetStartupParameter', null, ['string', 'string']);
-    CreateWasmApplication = StoneFrameworkModule.cwrap('CreateWasmApplication', null, ['number']);
-    CreateCppViewport = StoneFrameworkModule.cwrap('CreateCppViewport', 'number', []);
-    ReleaseCppViewport = StoneFrameworkModule.cwrap('ReleaseCppViewport', null, ['number']);
-    StartWasmApplication = StoneFrameworkModule.cwrap('StartWasmApplication', null, ['number']);
-
-    WasmWebService_NotifySuccess = StoneFrameworkModule.cwrap('WasmWebService_NotifySuccess', null, ['number', 'string', 'array', 'number', 'number']);
-    WasmWebService_NotifyError = StoneFrameworkModule.cwrap('WasmWebService_NotifyError', null, ['number', 'string', 'number']);
-    NotifyUpdateContent = StoneFrameworkModule.cwrap('NotifyUpdateContent', null, []);
-
-    // Prevent scrolling
-    document.body.addEventListener('touchmove', function (event) {
-        event.preventDefault();
-    }, false);
-
-
-    InitializeWasmApplication("canvas");
-});
\ No newline at end of file
--- a/Platforms/WebAssembly/wasm-viewport.ts	Tue Jun 19 16:02:41 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,269 +0,0 @@
-var isPendingRedraw = false;
-
-function ScheduleWebViewportRedraw(cppViewportHandle: any) : void
-{
-  if (!isPendingRedraw) {
-    isPendingRedraw = true;
-    console.log('Scheduling a refresh of the viewport, as its content changed');
-    window.requestAnimationFrame(function() {
-      isPendingRedraw = false;
-      Stone.WasmViewport.GetFromCppViewport(cppViewportHandle).Redraw();
-    });
-  }
-}
-
-declare function UTF8ToString(any): string;
-
-function CreateWasmViewport(htmlCanvasId: string) : any {
-  var cppViewportHandle = CreateCppViewport();
-  var canvasId = UTF8ToString(htmlCanvasId);
-  var webViewport = new Stone.WasmViewport(StoneFrameworkModule, canvasId, cppViewportHandle);  // viewports are stored in a static map in WasmViewport -> won't be deleted
-  webViewport.Initialize();
-
-  return cppViewportHandle;
-}
-
-module Stone {
-  
-//  export declare type InitializationCallback = () => void;
-  
-//  export declare var StoneFrameworkModule : any;
-  
-  //const ASSETS_FOLDER : string = "assets/lib";
-  //const WASM_FILENAME : string = "orthanc-framework";
-
-  export class WasmViewport {
-
-    private static cppWebViewportsMaps_ : Map<number, WasmViewport> = new Map<number, WasmViewport>();
-
-    private module_ : any;
-    private canvasId_ : string;
-    private htmlCanvas_ : HTMLCanvasElement;
-    private context_ : CanvasRenderingContext2D;
-    private imageData_ : any = null;
-    private renderingBuffer_ : any = null;
-    private touchZoom_ : any = false;
-    private touchTranslation_ : any = false;
-
-    private ViewportSetSize : Function;
-    private ViewportRender : Function;
-    private ViewportMouseDown : Function;
-    private ViewportMouseMove : Function;
-    private ViewportMouseUp : Function;
-    private ViewportMouseEnter : Function;
-    private ViewportMouseLeave : Function;
-    private ViewportMouseWheel : Function;
-    private ViewportKeyPressed : Function;
-
-    private pimpl_ : any; // Private pointer to the underlying WebAssembly C++ object
-
-    public constructor(module: any, canvasId: string, cppViewport: any) {
-      
-      this.pimpl_ = cppViewport;
-      WasmViewport.cppWebViewportsMaps_[this.pimpl_] = this;
-
-      this.module_ = module;
-      this.canvasId_ = canvasId;
-      this.htmlCanvas_ = document.getElementById(this.canvasId_) as HTMLCanvasElement;
-      this.context_ = this.htmlCanvas_.getContext('2d');
-
-      this.ViewportSetSize = this.module_.cwrap('ViewportSetSize', null, [ 'number', 'number', 'number' ]);
-      this.ViewportRender = this.module_.cwrap('ViewportRender', null, [ 'number', 'number', 'number', 'number' ]);
-      this.ViewportMouseDown = this.module_.cwrap('ViewportMouseDown', null, [ 'number', 'number', 'number', 'number', 'number' ]);
-      this.ViewportMouseMove = this.module_.cwrap('ViewportMouseMove', null, [ 'number', 'number', 'number' ]);
-      this.ViewportMouseUp = this.module_.cwrap('ViewportMouseUp', null, [ 'number' ]);
-      this.ViewportMouseEnter = this.module_.cwrap('ViewportMouseEnter', null, [ 'number' ]);
-      this.ViewportMouseLeave = this.module_.cwrap('ViewportMouseLeave', null, [ 'number' ]);
-      this.ViewportMouseWheel = this.module_.cwrap('ViewportMouseWheel', null, [ 'number', 'number', 'number', 'number', 'number' ]);
-      this.ViewportKeyPressed = this.module_.cwrap('ViewportKeyPressed', null, [ 'number', 'string', 'number', 'number' ]);
-    }
-
-    public GetCppViewport() : number {
-      return this.pimpl_;
-    }
-
-    public static GetFromCppViewport(cppViewportHandle: number) : WasmViewport {
-      if (WasmViewport.cppWebViewportsMaps_[cppViewportHandle] !== undefined) {
-        return WasmViewport.cppWebViewportsMaps_[cppViewportHandle];
-      }
-      console.log("WasmViewport not found !");
-      return undefined;
-    }
-
-    public Redraw() {
-      if (this.imageData_ === null ||
-          this.renderingBuffer_ === null ||
-          this.ViewportRender(this.pimpl_,
-                         this.imageData_.width,
-                         this.imageData_.height,
-                         this.renderingBuffer_) == 0) {
-        console.log('The rendering has failed');
-      } else {
-        // Create an accessor to the rendering buffer (i.e. create a
-        // "window" above the heap of the WASM module), then copy it to
-        // the ImageData object
-        this.imageData_.data.set(new Uint8ClampedArray(
-          this.module_.buffer,
-          this.renderingBuffer_,
-          this.imageData_.width * this.imageData_.height * 4));
-        
-        this.context_.putImageData(this.imageData_, 0, 0);
-      }
-    }
-  
-    public Resize() {
-      if (this.imageData_ != null &&
-          (this.imageData_.width != window.innerWidth ||
-           this.imageData_.height != window.innerHeight)) {
-        this.imageData_ = null;
-      }
-      
-      // width/height can be defined in percent of window width/height through html attributes like data-width-ratio="50" and data-height-ratio="20"
-      var widthRatio = Number(this.htmlCanvas_.dataset["widthRatio"]) || 100;
-      var heightRatio = Number(this.htmlCanvas_.dataset["heightRatio"]) || 100;
-
-      this.htmlCanvas_.width = window.innerWidth * (widthRatio / 100);  
-      this.htmlCanvas_.height = window.innerHeight * (heightRatio / 100);
-
-      console.log("resizing WasmViewport: ", this.htmlCanvas_.width, "x", this.htmlCanvas_.height);
-
-      if (this.imageData_ === null) {
-        this.imageData_ = this.context_.getImageData(0, 0, this.htmlCanvas_.width, this.htmlCanvas_.height);
-        this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height);
-  
-        if (this.renderingBuffer_ != null) {
-          this.module_._free(this.renderingBuffer_);
-        }
-        
-        this.renderingBuffer_ = this.module_._malloc(this.imageData_.width * this.imageData_.height * 4);
-      }
-      
-      this.Redraw();
-    }
-
-    public Initialize() {
-      
-      // Force the rendering of the viewport for the first time
-      this.Resize();
-    
-      var that : WasmViewport = this;
-      // Register an event listener to call the Resize() function 
-      // each time the window is resized.
-      window.addEventListener('resize', function(event) {
-        that.Resize();
-      }, false);
-  
-      this.htmlCanvas_.addEventListener('contextmenu', function(event) {
-        // Prevent right click on the canvas
-        event.preventDefault();
-      }, false);
-      
-      this.htmlCanvas_.addEventListener('mouseleave', function(event) {
-        that.ViewportMouseLeave(that.pimpl_);
-      });
-      
-      this.htmlCanvas_.addEventListener('mouseenter', function(event) {
-        that.ViewportMouseEnter(that.pimpl_);
-      });
-    
-      this.htmlCanvas_.addEventListener('mousedown', function(event) {
-        var x = event.pageX - this.offsetLeft;
-        var y = event.pageY - this.offsetTop;
-        that.ViewportMouseDown(that.pimpl_, event.button, x, y, 0 /* TODO */);    
-      });
-    
-      this.htmlCanvas_.addEventListener('mousemove', function(event) {
-        var x = event.pageX - this.offsetLeft;
-        var y = event.pageY - this.offsetTop;
-        that.ViewportMouseMove(that.pimpl_, x, y);
-      });
-    
-      this.htmlCanvas_.addEventListener('mouseup', function(event) {
-        that.ViewportMouseUp(that.pimpl_);
-      });
-    
-      window.addEventListener('keydown', function(event) {
-        that.ViewportKeyPressed(that.pimpl_, event.key, event.shiftKey, event.ctrlKey, event.altKey);
-      });
-    
-      this.htmlCanvas_.addEventListener('wheel', function(event) {
-        var x = event.pageX - this.offsetLeft;
-        var y = event.pageY - this.offsetTop;
-        that.ViewportMouseWheel(that.pimpl_, event.deltaY, x, y, event.ctrlKey);
-        event.preventDefault();
-      });
-
-      this.htmlCanvas_.addEventListener('touchstart', function(event) {
-        that.ResetTouch();
-      });
-    
-      this.htmlCanvas_.addEventListener('touchend', function(event) {
-        that.ResetTouch();
-      });
-    
-      this.htmlCanvas_.addEventListener('touchmove', function(event) {
-        if (that.touchTranslation_.length == 2) {
-          var t = that.GetTouchTranslation(event);
-          that.ViewportMouseMove(that.pimpl_, t[0], t[1]);
-        }
-        else if (that.touchZoom_.length == 3) {
-          var z0 = that.touchZoom_;
-          var z1 = that.GetTouchZoom(event);
-          that.ViewportMouseMove(that.pimpl_, z0[0], z0[1] - z0[2] + z1[2]);
-        }
-        else {
-          // Realize the gesture event
-          if (event.targetTouches.length == 1) {
-            // Exactly one finger inside the canvas => Setup a translation
-            that.touchTranslation_ = that.GetTouchTranslation(event);
-            that.ViewportMouseDown(that.pimpl_, 
-                                  1 /* middle button */,
-                                  that.touchTranslation_[0],
-                                  that.touchTranslation_[1], 0);
-          } else if (event.targetTouches.length == 2) {
-            // Exactly 2 fingers inside the canvas => Setup a pinch/zoom
-            that.touchZoom_ = that.GetTouchZoom(event);
-            var z0 = that.touchZoom_;
-            that.ViewportMouseDown(that.pimpl_, 
-                                  2 /* right button */,
-                                  z0[0],
-                                  z0[1], 0);
-          }        
-        }
-      });
-    }  
-
-  public ResetTouch() {
-    if (this.touchTranslation_ ||
-        this.touchZoom_) {
-      this.ViewportMouseUp(this.pimpl_);
-    }
-
-    this.touchTranslation_ = false;
-    this.touchZoom_ = false;
-  }
-  
-  public GetTouchTranslation(event) {
-    var touch = event.targetTouches[0];
-    return [
-      touch.pageX,
-      touch.pageY
-    ];
-  }
-    
-  public GetTouchZoom(event) {
-    var touch1 = event.targetTouches[0];
-    var touch2 = event.targetTouches[1];
-    var dx = (touch1.pageX - touch2.pageX);
-    var dy = (touch1.pageY - touch2.pageY);
-    var d = Math.sqrt(dx * dx + dy * dy);
-    return [
-      (touch1.pageX + touch2.pageX) / 2.0,
-      (touch1.pageY + touch2.pageY) / 2.0,
-      d
-    ];
-  }
-    
-}
-}
-  
\ No newline at end of file