changeset 1400:419d0320c344

moved Platforms into Deprecated
author Alain Mazy <alain@mazy.be>
date Wed, 29 Apr 2020 20:45:14 +0200
parents ff8d2e46ac63
children f6a2d46d2b76
files Deprecated/Platforms/Generic/DelayedCallCommand.cpp Deprecated/Platforms/Generic/DelayedCallCommand.h Deprecated/Platforms/Generic/IOracleCommand.h Deprecated/Platforms/Generic/Oracle.cpp Deprecated/Platforms/Generic/Oracle.h Deprecated/Platforms/Generic/OracleDelayedCallExecutor.h Deprecated/Platforms/Generic/OracleWebService.cpp Deprecated/Platforms/Generic/OracleWebService.h Deprecated/Platforms/Generic/WebServiceCommandBase.cpp Deprecated/Platforms/Generic/WebServiceCommandBase.h Deprecated/Platforms/Generic/WebServiceDeleteCommand.cpp Deprecated/Platforms/Generic/WebServiceDeleteCommand.h Deprecated/Platforms/Generic/WebServiceGetCommand.cpp Deprecated/Platforms/Generic/WebServiceGetCommand.h Deprecated/Platforms/Generic/WebServicePostCommand.cpp Deprecated/Platforms/Generic/WebServicePostCommand.h Deprecated/Platforms/Wasm/Defaults.cpp Deprecated/Platforms/Wasm/Defaults.h Deprecated/Platforms/Wasm/WasmDelayedCallExecutor.cpp Deprecated/Platforms/Wasm/WasmDelayedCallExecutor.h Deprecated/Platforms/Wasm/WasmDelayedCallExecutor.js Deprecated/Platforms/Wasm/WasmPlatformApplicationAdapter.cpp Deprecated/Platforms/Wasm/WasmPlatformApplicationAdapter.h Deprecated/Platforms/Wasm/WasmViewport.cpp Deprecated/Platforms/Wasm/WasmViewport.h Deprecated/Platforms/Wasm/WasmWebService.cpp Deprecated/Platforms/Wasm/WasmWebService.h Deprecated/Platforms/Wasm/WasmWebService.js Deprecated/Platforms/Wasm/default-library.js Deprecated/Platforms/Wasm/logger.ts Deprecated/Platforms/Wasm/stone-framework-loader.ts Deprecated/Platforms/Wasm/tsconfig-stone.json Deprecated/Platforms/Wasm/wasm-application-runner.ts Deprecated/Platforms/Wasm/wasm-viewport.ts Platforms/Generic/DelayedCallCommand.cpp Platforms/Generic/DelayedCallCommand.h Platforms/Generic/IOracleCommand.h Platforms/Generic/Oracle.cpp Platforms/Generic/Oracle.h Platforms/Generic/OracleDelayedCallExecutor.h Platforms/Generic/OracleWebService.cpp Platforms/Generic/OracleWebService.h Platforms/Generic/WebServiceCommandBase.cpp Platforms/Generic/WebServiceCommandBase.h Platforms/Generic/WebServiceDeleteCommand.cpp Platforms/Generic/WebServiceDeleteCommand.h Platforms/Generic/WebServiceGetCommand.cpp Platforms/Generic/WebServiceGetCommand.h Platforms/Generic/WebServicePostCommand.cpp Platforms/Generic/WebServicePostCommand.h Platforms/Wasm/Defaults.cpp Platforms/Wasm/Defaults.h Platforms/Wasm/WasmDelayedCallExecutor.cpp Platforms/Wasm/WasmDelayedCallExecutor.h Platforms/Wasm/WasmDelayedCallExecutor.js Platforms/Wasm/WasmPlatformApplicationAdapter.cpp Platforms/Wasm/WasmPlatformApplicationAdapter.h Platforms/Wasm/WasmViewport.cpp Platforms/Wasm/WasmViewport.h Platforms/Wasm/WasmWebService.cpp Platforms/Wasm/WasmWebService.h Platforms/Wasm/WasmWebService.js Platforms/Wasm/default-library.js Platforms/Wasm/logger.ts Platforms/Wasm/stone-framework-loader.ts Platforms/Wasm/tsconfig-stone.json Platforms/Wasm/wasm-application-runner.ts Platforms/Wasm/wasm-viewport.ts
diffstat 68 files changed, 2887 insertions(+), 2887 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/DelayedCallCommand.cpp	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,64 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "DelayedCallCommand.h"
+#include "boost/thread/thread.hpp"
+
+#include <iostream>
+
+namespace Deprecated
+{
+  DelayedCallCommand::DelayedCallCommand(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,  // takes ownership
+                                         unsigned int timeoutInMs,
+                                         Orthanc::IDynamicObject* payload /* takes ownership */,
+                                         OrthancStone::NativeStoneApplicationContext& context
+                                         ) :
+    callback_(callback),
+    payload_(payload),
+    context_(context),
+    expirationTimePoint_(boost::posix_time::microsec_clock::local_time() + boost::posix_time::milliseconds(timeoutInMs)),
+    timeoutInMs_(timeoutInMs)
+  {
+  }
+
+
+  void DelayedCallCommand::Execute()
+  {
+    while (boost::posix_time::microsec_clock::local_time() < expirationTimePoint_)
+    {
+      boost::this_thread::sleep(boost::posix_time::milliseconds(1));
+    }
+  }
+
+  void DelayedCallCommand::Commit()
+  {
+    // We want to make sure that, i.e, the UpdateThread is not
+    // triggered while we are updating the "model" with the result of
+    // an OracleCommand
+    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_);
+
+    if (callback_.get() != NULL)
+    {
+      IDelayedCallExecutor::TimeoutMessage message; // TODO: add payload
+      callback_->Apply(message);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/DelayedCallCommand.h	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,56 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IOracleCommand.h"
+
+#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h"
+#include "../../Framework/Messages/IObservable.h"
+#include "../../Framework/Messages/ICallable.h"
+#include "../../Applications/Generic/NativeStoneApplicationContext.h"
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+namespace Deprecated
+{
+  class DelayedCallCommand : public IOracleCommand, OrthancStone::IObservable
+  {
+  protected:
+    std::unique_ptr<MessageHandler<IDelayedCallExecutor::TimeoutMessage> >  callback_;
+    std::unique_ptr<Orthanc::IDynamicObject>  payload_;
+    OrthancStone::NativeStoneApplicationContext&          context_;
+    boost::posix_time::ptime                expirationTimePoint_;
+    unsigned int                            timeoutInMs_;
+
+  public:
+    DelayedCallCommand(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,  // takes ownership
+                       unsigned int timeoutInMs,
+                       Orthanc::IDynamicObject* payload /* takes ownership */,
+                       OrthancStone::NativeStoneApplicationContext& context
+                       );
+
+    virtual void Execute();
+
+    virtual void Commit();
+  };
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/IOracleCommand.h	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,42 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include <Core/IDynamicObject.h>
+
+namespace Deprecated
+{
+  class IOracleCommand : public Orthanc::IDynamicObject
+  {
+  public:
+    virtual ~IOracleCommand()
+    {
+    }
+
+    // This part of the command can be invoked simultaneously, and
+    // must not modify the Stone context
+    virtual void Execute() = 0;
+
+    // This part of the command must be invoked in mutual exclusion
+    virtual void Commit() = 0;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/Oracle.cpp	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,212 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "Oracle.h"
+
+#include <Core/Logging.h>
+#include <Core/MultiThreading/SharedMessageQueue.h>
+#include <Core/OrthancException.h>
+
+#include <vector>
+#include <stdio.h>
+#include <boost/thread/mutex.hpp>
+
+namespace Deprecated
+{
+  class Oracle::PImpl
+  {
+  private:
+    enum State
+    {
+      State_Init,
+      State_Started,
+      State_Stopped
+    };
+
+    boost::mutex                   oracleMutex_;
+    State                          state_;
+    std::vector<boost::thread*>    threads_;
+    Orthanc::SharedMessageQueue    queue_;
+
+    static void Worker(PImpl* that)
+    {
+      for (;;)
+      {
+        State state;
+        
+        {
+          boost::mutex::scoped_lock lock(that->oracleMutex_);
+          state = that->state_;
+        }
+
+        if (state == State_Stopped)
+        {
+          break;
+        }
+
+        std::unique_ptr<Orthanc::IDynamicObject> item(that->queue_.Dequeue(100));
+        if (item.get() != NULL)
+        {
+          IOracleCommand& command = dynamic_cast<IOracleCommand&>(*item);
+          try
+          {
+            command.Execute();
+          }
+          catch (Orthanc::OrthancException& /*ex*/)
+          {
+            // this is probably a curl error that has been triggered.  We may just ignore it.
+            // The command.success_ will stay at false and this will be handled in the command.Commit
+          }
+
+          // Random sleeping to test
+          //boost::this_thread::sleep(boost::posix_time::milliseconds(50 * (1 + rand() % 10)));
+
+          command.Commit();
+        }
+      }
+    }
+    
+  public:
+    PImpl(unsigned int threadCount) :
+      state_(State_Init),
+      threads_(threadCount)
+    {
+    }
+
+    ~PImpl()
+    {
+      if (state_ == State_Started)
+      {
+        LOG(ERROR) << "You should have manually called Oracle::Stop()";
+        Stop();
+      }
+    }
+
+    Orthanc::SharedMessageQueue& GetQueue()
+    {
+      return queue_;
+    }
+
+    void Submit(IOracleCommand* command)
+    {
+      std::unique_ptr<IOracleCommand> protection(command);
+
+      if (command == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      }
+      
+      boost::mutex::scoped_lock lock(oracleMutex_);
+
+      switch (state_)
+      {
+        case State_Init:
+        case State_Started:
+          queue_.Enqueue(protection.release());
+          break;
+
+        case State_Stopped:
+          LOG(ERROR) << "Cannot schedule a request to the Oracle after having "
+                     << "called Oracle::Stop()";
+          break;
+
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+      
+    }
+
+    void Start()
+    {
+      boost::mutex::scoped_lock lock(oracleMutex_);
+
+      if (state_ != State_Init)
+      {
+        LOG(ERROR) << "Oracle::PImpl::Start: (state_ != State_Init)";
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      }
+      
+      for (size_t i = 0; i < threads_.size(); i++)
+      {
+        threads_[i] = new boost::thread(Worker, this);
+      }
+
+      state_ = State_Started;
+    }
+
+    void Stop()
+    {
+      {
+        boost::mutex::scoped_lock lock(oracleMutex_);
+
+        if (state_ != State_Started)
+        {
+          LOG(ERROR) << "Oracle::PImpl::Stop(): (state_ != State_Started)";
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+        }
+
+        state_ = State_Stopped;
+      }
+      
+      for (size_t i = 0; i < threads_.size(); i++)
+      {
+        if (threads_[i] != NULL)
+        {
+          if (threads_[i]->joinable())
+          {
+            threads_[i]->join();
+          }
+
+          delete threads_[i];
+        }
+      }
+    }
+  };
+  
+
+  Oracle::Oracle(unsigned int threadCount) :
+    pimpl_(new PImpl(threadCount))
+  {
+  }
+
+  void Oracle::Start()
+  {
+    pimpl_->Start();
+  }
+
+
+  void Oracle::Submit(IOracleCommand* command)
+  {
+    pimpl_->Submit(command);
+  }
+     
+   
+  void Oracle::Stop()
+  {
+    pimpl_->Stop();
+  }
+
+
+  void Oracle::WaitEmpty()
+  {
+    pimpl_->GetQueue().WaitEmpty(50);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/Oracle.h	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,48 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IOracleCommand.h"
+
+#include <boost/shared_ptr.hpp>
+
+namespace Deprecated
+{
+  class Oracle : public boost::noncopyable
+  {
+  private:
+    class PImpl;
+  
+    boost::shared_ptr<PImpl>  pimpl_;
+
+  public:
+    Oracle(unsigned int threadCount);
+
+    void Start();
+
+    void Submit(IOracleCommand* command);
+        
+    void WaitEmpty();  // For unit tests
+
+    void Stop();
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/OracleDelayedCallExecutor.h	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,52 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h"
+#include "Oracle.h"
+#include "../../Applications/Generic/NativeStoneApplicationContext.h"
+#include "DelayedCallCommand.h"
+
+namespace Deprecated
+{
+  // The OracleTimeout executes callbacks after a delay.
+  class OracleDelayedCallExecutor : public IDelayedCallExecutor
+  {
+  private:
+    Oracle&                        oracle_;
+    OrthancStone::NativeStoneApplicationContext& context_;
+
+  public:
+    OracleDelayedCallExecutor(Oracle& oracle,
+                              OrthancStone::NativeStoneApplicationContext& context) :
+      oracle_(oracle),
+      context_(context)
+    {
+    }
+
+    virtual void Schedule(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
+                          unsigned int timeoutInMs = 1000)
+    {
+      oracle_.Submit(new DelayedCallCommand(callback, timeoutInMs, NULL, context_));
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/OracleWebService.cpp	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,80 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "OracleWebService.h"
+#include "../../Framework/Deprecated/Toolbox/IWebService.h"
+
+namespace Deprecated
+{
+
+
+  class OracleWebService::WebServiceCachedGetCommand : public IOracleCommand, OrthancStone::IObservable
+  {
+  protected:
+    std::unique_ptr<MessageHandler<IWebService::HttpRequestSuccessMessage> >  successCallback_;
+    std::unique_ptr<Orthanc::IDynamicObject>                                  payload_;
+    boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage>      cachedMessage_;
+    OrthancStone::NativeStoneApplicationContext&                                          context_;
+
+  public:
+    WebServiceCachedGetCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                               boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage,
+                               Orthanc::IDynamicObject* payload /* takes ownership */,
+                               OrthancStone::NativeStoneApplicationContext& context
+                               ) :
+      successCallback_(successCallback),
+      payload_(payload),
+      cachedMessage_(cachedMessage),
+      context_(context)
+    {
+    }
+
+    virtual void Execute()
+    {
+      // nothing to do, everything is in the commit
+    }
+
+    virtual void Commit()
+    {
+      // We want to make sure that, i.e, the UpdateThread is not
+      // triggered while we are updating the "model" with the result of
+      // a WebServiceCommand
+      OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_);
+
+      IWebService::HttpRequestSuccessMessage successMessage(cachedMessage_->GetUri(),
+                                                            cachedMessage_->GetAnswer(),
+                                                            cachedMessage_->GetAnswerSize(),
+                                                            cachedMessage_->GetAnswerHttpHeaders(),
+                                                            payload_.get());
+
+      successCallback_->Apply(successMessage);
+    }
+  };
+
+  void OracleWebService::NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage,
+                                                Orthanc::IDynamicObject* payload, // takes ownership
+                                                MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback)
+  {
+    oracle_.Submit(new WebServiceCachedGetCommand(successCallback, cachedMessage, payload, context_));
+  }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/OracleWebService.h	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,92 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../Framework/Deprecated/Toolbox/BaseWebService.h"
+#include "Oracle.h"
+#include "WebServiceGetCommand.h"
+#include "WebServicePostCommand.h"
+#include "WebServiceDeleteCommand.h"
+#include "../../Applications/Generic/NativeStoneApplicationContext.h"
+
+namespace Deprecated
+{
+  // The OracleWebService performs HTTP requests in a native environment.
+  // It uses a thread pool to handle multiple HTTP requests in a same time.
+  // It works asynchronously to mimick the behaviour of the WebService running in a WASM environment.
+  class OracleWebService : public BaseWebService
+  {
+  private:
+    Oracle&                        oracle_;
+    OrthancStone::NativeStoneApplicationContext& context_;
+    Orthanc::WebServiceParameters  parameters_;
+
+    class WebServiceCachedGetCommand;
+
+  public:
+    OracleWebService(Oracle& oracle,
+                     const Orthanc::WebServiceParameters& parameters,
+                     OrthancStone::NativeStoneApplicationContext& context) :
+      oracle_(oracle),
+      context_(context),
+      parameters_(parameters)
+    {
+    }
+
+    virtual void PostAsync(const std::string& uri,
+                           const HttpHeaders& headers,
+                           const std::string& body,
+                           Orthanc::IDynamicObject* payload, // takes ownership
+                           MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership
+                           MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL, // takes ownership
+                           unsigned int timeoutInSeconds = 60)
+    {
+      oracle_.Submit(new WebServicePostCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, body, payload, context_));
+    }
+
+    virtual void DeleteAsync(const std::string& uri,
+                             const HttpHeaders& headers,
+                             Orthanc::IDynamicObject* payload,
+                             MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
+                             MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
+                             unsigned int timeoutInSeconds = 60)
+    {
+      oracle_.Submit(new WebServiceDeleteCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_));
+    }
+
+  protected:
+    virtual void GetAsyncInternal(const std::string& uri,
+                                  const HttpHeaders& headers,
+                                  Orthanc::IDynamicObject* payload, // takes ownership
+                                  MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,   // takes ownership
+                                  MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,// takes ownership
+                                  unsigned int timeoutInSeconds = 60)
+    {
+      oracle_.Submit(new WebServiceGetCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_));
+    }
+
+    virtual void NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedHttpMessage,
+                                        Orthanc::IDynamicObject* payload, // takes ownership
+                                        MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback);
+
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/WebServiceCommandBase.cpp	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,69 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "WebServiceCommandBase.h"
+
+#include <Core/HttpClient.h>
+
+namespace Deprecated
+{
+  WebServiceCommandBase::WebServiceCommandBase(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
+                                               MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
+                                               const Orthanc::WebServiceParameters& parameters,
+                                               const std::string& url,
+                                               const IWebService::HttpHeaders& headers,
+                                               unsigned int timeoutInSeconds,
+                                               Orthanc::IDynamicObject* payload /* takes ownership */,
+                                               OrthancStone::NativeStoneApplicationContext& context) :
+    successCallback_(successCallback),
+    failureCallback_(failureCallback),
+    parameters_(parameters),
+    url_(url),
+    headers_(headers),
+    payload_(payload),
+    success_(false),
+    httpStatus_(Orthanc::HttpStatus_None),
+    context_(context),
+    timeoutInSeconds_(timeoutInSeconds)
+  {
+  }
+
+
+  void WebServiceCommandBase::Commit()
+  {
+    // We want to make sure that, i.e, the UpdateThread is not
+    // triggered while we are updating the "model" with the result of
+    // a WebServiceCommand
+    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); 
+
+    if (success_ && successCallback_.get() != NULL)
+    {
+      IWebService::HttpRequestSuccessMessage message
+        (url_, answer_.c_str(), answer_.size(), answerHeaders_, payload_.get());
+      successCallback_->Apply(message);
+    }
+    else if (!success_ && failureCallback_.get() != NULL)
+    {
+      IWebService::HttpRequestErrorMessage message(url_, httpStatus_, payload_.get());
+      failureCallback_->Apply(message);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/WebServiceCommandBase.h	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,69 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IOracleCommand.h"
+
+#include "../../Framework/Deprecated/Toolbox/IWebService.h"
+#include "../../Framework/Messages/IObservable.h"
+#include "../../Framework/Messages/ICallable.h"
+#include "../../Applications/Generic/NativeStoneApplicationContext.h"
+
+#include <Core/WebServiceParameters.h>
+
+#include <memory>
+
+namespace Deprecated
+{
+  class WebServiceCommandBase : public IOracleCommand, OrthancStone::IObservable
+  {
+  protected:
+    std::unique_ptr<MessageHandler<IWebService::HttpRequestSuccessMessage> >  successCallback_;
+    std::unique_ptr<MessageHandler<IWebService::HttpRequestErrorMessage> >    failureCallback_;
+    Orthanc::WebServiceParameters           parameters_;
+    std::string                             url_;
+    IWebService::HttpHeaders                headers_;
+    std::unique_ptr<Orthanc::IDynamicObject>  payload_;
+    bool                                    success_;
+    Orthanc::HttpStatus                     httpStatus_;
+    std::string                             answer_;
+    IWebService::HttpHeaders                answerHeaders_;
+    OrthancStone::NativeStoneApplicationContext&          context_;
+    unsigned int                            timeoutInSeconds_;
+
+  public:
+    WebServiceCommandBase(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+                          const Orthanc::WebServiceParameters& parameters,
+                          const std::string& url,
+                          const IWebService::HttpHeaders& headers,
+                          unsigned int timeoutInSeconds,
+                          Orthanc::IDynamicObject* payload /* takes ownership */,
+                          OrthancStone::NativeStoneApplicationContext& context
+                          );
+
+    virtual void Execute() = 0;
+
+    virtual void Commit();
+  };
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/WebServiceDeleteCommand.cpp	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,56 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "WebServiceDeleteCommand.h"
+
+#include <Core/HttpClient.h>
+
+namespace Deprecated
+{
+  WebServiceDeleteCommand::WebServiceDeleteCommand(MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                                                   MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+                                                   const Orthanc::WebServiceParameters& parameters,
+                                                   const std::string& url,
+                                                   const Deprecated::IWebService::HttpHeaders& headers,
+                                                   unsigned int timeoutInSeconds,
+                                                   Orthanc::IDynamicObject* payload /* takes ownership */,
+                                                   OrthancStone::NativeStoneApplicationContext& context) :
+    WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context)
+  {
+  }
+
+  void WebServiceDeleteCommand::Execute()
+  {
+    Orthanc::HttpClient client(parameters_, "/");
+    client.SetUrl(url_);
+    client.SetTimeout(timeoutInSeconds_);
+    client.SetMethod(Orthanc::HttpMethod_Delete);
+
+    for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
+    {
+      client.AddHeader(it->first, it->second);
+    }
+
+    success_ = client.Apply(answer_, answerHeaders_);
+    httpStatus_ = client.GetLastStatus();
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/WebServiceDeleteCommand.h	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,42 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "WebServiceCommandBase.h"
+
+namespace Deprecated
+{
+  class WebServiceDeleteCommand : public WebServiceCommandBase
+  {
+  public:
+    WebServiceDeleteCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                            MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+                            const Orthanc::WebServiceParameters& parameters,
+                            const std::string& url,
+                            const IWebService::HttpHeaders& headers,
+                            unsigned int timeoutInSeconds,
+                            Orthanc::IDynamicObject* payload /* takes ownership */,
+                            OrthancStone::NativeStoneApplicationContext& context);
+
+    virtual void Execute();
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/WebServiceGetCommand.cpp	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,57 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "WebServiceGetCommand.h"
+
+#include <Core/HttpClient.h>
+
+namespace Deprecated
+{
+  WebServiceGetCommand::WebServiceGetCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                                             MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+                                             const Orthanc::WebServiceParameters& parameters,
+                                             const std::string& url,
+                                             const IWebService::HttpHeaders& headers,
+                                             unsigned int timeoutInSeconds,
+                                             Orthanc::IDynamicObject* payload /* takes ownership */,
+                                             OrthancStone::NativeStoneApplicationContext& context) :
+    WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context)
+  {
+  }
+
+
+  void WebServiceGetCommand::Execute()
+  {
+    Orthanc::HttpClient client(parameters_, "/");
+    client.SetUrl(url_);
+    client.SetTimeout(timeoutInSeconds_);
+    client.SetMethod(Orthanc::HttpMethod_Get);
+
+    for (IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
+    {
+      client.AddHeader(it->first, it->second);
+    }
+
+    success_ = client.Apply(answer_, answerHeaders_);
+    httpStatus_ = client.GetLastStatus();
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/WebServiceGetCommand.h	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,43 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "WebServiceCommandBase.h"
+
+namespace Deprecated
+{
+  class WebServiceGetCommand : public WebServiceCommandBase
+  {
+  public:
+    WebServiceGetCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                         MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+                         const Orthanc::WebServiceParameters& parameters,
+                         const std::string& url,
+                         const IWebService::HttpHeaders& headers,
+                         unsigned int timeoutInSeconds,
+                         Orthanc::IDynamicObject* payload /* takes ownership */,
+                         OrthancStone::NativeStoneApplicationContext& context);
+
+    virtual void Execute();
+  };
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/WebServicePostCommand.cpp	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,58 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "WebServicePostCommand.h"
+
+#include <Core/HttpClient.h>
+
+namespace Deprecated
+{
+  WebServicePostCommand::WebServicePostCommand(MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                                               MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+                                               const Orthanc::WebServiceParameters& parameters,
+                                               const std::string& url,
+                                               const Deprecated::IWebService::HttpHeaders& headers,
+                                               unsigned int timeoutInSeconds,
+                                               const std::string& body,
+                                               Orthanc::IDynamicObject* payload /* takes ownership */,
+                                               OrthancStone::NativeStoneApplicationContext& context) :
+    WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context),
+    body_(body)
+  {
+  }
+
+  void WebServicePostCommand::Execute()
+  {
+    Orthanc::HttpClient client(parameters_, "/");
+    client.SetUrl(url_);
+    client.SetTimeout(timeoutInSeconds_);
+    client.SetMethod(Orthanc::HttpMethod_Post);
+    client.GetBody().swap(body_);
+
+    for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
+    {
+      client.AddHeader(it->first, it->second);
+    }
+
+    success_ = client.Apply(answer_, answerHeaders_);
+    httpStatus_ = client.GetLastStatus();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/WebServicePostCommand.h	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,46 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "WebServiceCommandBase.h"
+
+namespace Deprecated
+{
+  class WebServicePostCommand : public WebServiceCommandBase
+  {
+  protected:
+    std::string  body_;
+
+  public:
+    WebServicePostCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+                          const Orthanc::WebServiceParameters& parameters,
+                          const std::string& url,
+                          const IWebService::HttpHeaders& headers,
+                          unsigned int timeoutInSeconds,
+                          const std::string& body,
+                          Orthanc::IDynamicObject* payload /* takes ownership */,
+                          OrthancStone::NativeStoneApplicationContext& context);
+
+    virtual void Execute();
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/Defaults.cpp	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,416 @@
+#include "Defaults.h"
+
+#include "WasmWebService.h"
+#include "WasmDelayedCallExecutor.h"
+#include "../../Framework/Deprecated/Widgets/TestCairoWidget.h"
+#include <Framework/Deprecated/Viewport/WidgetViewport.h>
+#include <Applications/Wasm/StartupParametersBuilder.h>
+#include <Platforms/Wasm/WasmPlatformApplicationAdapter.h>
+#include <Framework/StoneInitialization.h>
+#include <Core/Logging.h>
+#include <sstream>
+
+#include <algorithm>
+
+
+static unsigned int width_ = 0;
+static unsigned int height_ = 0;
+
+/**********************************/
+
+static std::unique_ptr<OrthancStone::IStoneApplication> application;
+static std::unique_ptr<OrthancStone::WasmPlatformApplicationAdapter> applicationWasmAdapter = NULL;
+static std::unique_ptr<OrthancStone::StoneApplicationContext> context;
+static OrthancStone::StartupParametersBuilder startupParametersBuilder;
+static OrthancStone::MessageBroker broker;
+
+static OrthancStone::ViewportContentChangedObserver viewportContentChangedObserver_(broker);
+static OrthancStone::StatusBar statusBar_;
+
+static std::list<std::shared_ptr<Deprecated::WidgetViewport>> viewports_;
+
+std::shared_ptr<Deprecated::WidgetViewport> FindViewportSharedPtr(ViewportHandle viewport) {
+  for (const auto& v : viewports_) {
+    if (v.get() == viewport) {
+      return v;
+    }
+  }
+  assert(false);
+  return std::shared_ptr<Deprecated::WidgetViewport>();
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if 0
+  // rewrite malloc/free in order to monitor allocations.  We actually only monitor large allocations (like images ...)
+
+  size_t bigChunksTotalSize = 0;
+  std::map<void*, size_t> allocatedBigChunks;
+
+  extern void* emscripten_builtin_malloc(size_t bytes);
+  extern void emscripten_builtin_free(void* mem);
+
+  void * __attribute__((noinline)) malloc(size_t size)
+  {
+    void *ptr = emscripten_builtin_malloc(size);
+    if (size > 100000)
+    {
+      bigChunksTotalSize += size;
+      printf("++ Allocated %zu bytes, got %p. (%zu MB consumed by big chunks)\n", size, ptr, bigChunksTotalSize/(1024*1024));
+      allocatedBigChunks[ptr] = size;
+    }
+    return ptr;
+  }
+
+  void __attribute__((noinline)) free(void *ptr)
+  {
+    emscripten_builtin_free(ptr);
+
+    std::map<void*, size_t>::iterator it = allocatedBigChunks.find(ptr);
+    if (it != allocatedBigChunks.end())
+    {
+      bigChunksTotalSize -= it->second;
+      printf("--     Freed %zu bytes at %p.   (%zu MB consumed by big chunks)\n", it->second, ptr, bigChunksTotalSize/(1024*1024));
+      allocatedBigChunks.erase(it);
+    }
+  }
+#endif // 0
+
+  using namespace OrthancStone;
+
+  // when WASM needs a C++ viewport
+  ViewportHandle EMSCRIPTEN_KEEPALIVE CreateCppViewport() {
+    
+    std::shared_ptr<Deprecated::WidgetViewport> viewport(new Deprecated::WidgetViewport(broker));
+    printf("viewport %x\n", (int)viewport.get());
+
+    viewports_.push_back(viewport);
+
+    printf("There are now %lu viewports in C++\n", viewports_.size());
+
+    viewport->SetStatusBar(statusBar_);
+
+    viewport->RegisterObserverCallback(
+      new Callable<ViewportContentChangedObserver, Deprecated::IViewport::ViewportChangedMessage>
+      (viewportContentChangedObserver_, &ViewportContentChangedObserver::OnViewportChanged));
+
+    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<Deprecated::WidgetViewport>& v) { return v.get() == viewport;});
+
+    printf("There are now %lu viewports in C++\n", viewports_.size());
+  }
+
+  void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle viewport) {
+    printf("Initializing Stone\n");
+    OrthancStone::StoneInitialize();
+    printf("CreateWasmApplication\n");
+
+    application.reset(CreateUserApplication(broker));
+    applicationWasmAdapter.reset(CreateWasmApplicationAdapter(broker, application.get())); 
+    Deprecated::WasmWebService::SetBroker(broker);
+    Deprecated::WasmDelayedCallExecutor::SetBroker(broker);
+
+    startupParametersBuilder.Clear();
+  }
+
+  void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc,
+                                                  const char* value) {
+    startupParametersBuilder.SetStartupParameter(keyc, value);
+  }
+
+  void EMSCRIPTEN_KEEPALIVE StartWasmApplication(const char* baseUri) {
+
+    printf("StartWasmApplication\n");
+
+    Orthanc::Logging::SetErrorWarnInfoTraceLoggingFunctions(
+      stone_console_error, stone_console_warning,
+      stone_console_info, stone_console_trace);
+
+    // recreate a command line from uri arguments and parse it
+    boost::program_options::variables_map parameters;
+    boost::program_options::options_description options;
+    application->DeclareStartupOptions(options);
+    startupParametersBuilder.GetStartupParameters(parameters, options);
+
+    context.reset(new OrthancStone::StoneApplicationContext(broker));
+    context->SetOrthancBaseUrl(baseUri);
+    printf("Base URL to Orthanc API: [%s]\n", baseUri);
+    context->SetWebService(Deprecated::WasmWebService::GetInstance());
+    context->SetDelayedCallExecutor(Deprecated::WasmDelayedCallExecutor::GetInstance());
+    application->Initialize(context.get(), statusBar_, parameters);
+    application->InitializeWasm();
+
+//    viewport->SetSize(width_, height_);
+    printf("StartWasmApplication - completed\n");
+  }
+  
+  bool EMSCRIPTEN_KEEPALIVE WasmIsTraceLevelEnabled()
+  {
+    return Orthanc::Logging::IsTraceLevelEnabled();
+  }
+
+  bool EMSCRIPTEN_KEEPALIVE WasmIsInfoLevelEnabled()
+  {
+    return Orthanc::Logging::IsInfoLevelEnabled();
+  }
+  
+  void EMSCRIPTEN_KEEPALIVE WasmDoAnimation()
+  {
+    for (auto viewport : viewports_) {
+      // TODO Only launch the JavaScript timer if "HasAnimation()"
+      if (viewport->HasAnimation())
+      {
+        viewport->DoAnimation();
+      }
+
+    }
+
+  }
+  
+
+  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)
+  {
+    viewportContentChangedObserver_.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, std::vector<Deprecated::Touch>());
+  }
+  
+
+  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, std::vector<Deprecated::Touch>());
+  }
+
+  void GetTouchVector(std::vector<Deprecated::Touch>& output,
+                      int touchCount,
+                      float x0,
+                      float y0,
+                      float x1,
+                      float y1,
+                      float x2,
+                      float y2)
+  {
+    // TODO: it might be nice to try to pass all the x0,y0 coordinates as arrays but that's not so easy to pass array between JS and C++
+    if (touchCount > 0)
+    {
+      output.push_back(Deprecated::Touch(x0, y0));
+    }
+    if (touchCount > 1)
+    {
+      output.push_back(Deprecated::Touch(x1, y1));
+    }
+    if (touchCount > 2)
+    {
+      output.push_back(Deprecated::Touch(x2, y2));
+    }
+
+  }
+
+  void EMSCRIPTEN_KEEPALIVE ViewportTouchStart(ViewportHandle viewport,
+                                              int touchCount,
+                                              float x0,
+                                              float y0,
+                                              float x1,
+                                              float y1,
+                                              float x2,
+                                              float y2)
+  {
+    // printf("touch start with %d touches\n", touchCount);
+
+    std::vector<Deprecated::Touch> touches;
+    GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2);
+    viewport->TouchStart(touches);
+  }
+
+  void EMSCRIPTEN_KEEPALIVE ViewportTouchMove(ViewportHandle viewport,
+                                              int touchCount,
+                                              float x0,
+                                              float y0,
+                                              float x1,
+                                              float y1,
+                                              float x2,
+                                              float y2)
+  {
+    // printf("touch move with %d touches\n", touchCount);
+
+    std::vector<Deprecated::Touch> touches;
+    GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2);
+    viewport->TouchMove(touches);
+  }
+
+  void EMSCRIPTEN_KEEPALIVE ViewportTouchEnd(ViewportHandle viewport,
+                                              int touchCount,
+                                              float x0,
+                                              float y0,
+                                              float x1,
+                                              float y1,
+                                              float x2,
+                                              float y2)
+  {
+    // printf("touch end with %d touches remaining\n", touchCount);
+
+    std::vector<Deprecated::Touch> touches;
+    GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2);
+    viewport->TouchEnd(touches);
+  }
+
+  void EMSCRIPTEN_KEEPALIVE ViewportKeyPressed(ViewportHandle viewport,
+                                               int key,
+                                               const char* keyChar, 
+                                               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);
+    }
+
+    char c = 0;
+    if (keyChar != NULL && key == OrthancStone::KeyboardKeys_Generic) {
+      c = keyChar[0];
+    }
+    viewport->KeyPressed(static_cast<OrthancStone::KeyboardKeys>(key), c, 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();
+  }
+
+  const char* EMSCRIPTEN_KEEPALIVE SendSerializedMessageToStoneApplication(const char* message) 
+  {
+    static std::string output; // we don't want the string to be deallocated when we return to JS code so we always use the same string (this is fine since JS is single-thread)
+
+    //printf("SendSerializedMessageToStoneApplication\n");
+    //printf("%s", message);
+
+    if (applicationWasmAdapter.get() != NULL) {
+      applicationWasmAdapter->HandleSerializedMessageFromWeb(output, std::string(message));
+      return output.c_str();
+    }
+    printf("This Stone application does not have a Web Adapter, unable to send messages");
+    return NULL;
+  }
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/Defaults.h	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,82 @@
+#pragma once
+
+#include <emscripten/emscripten.h>
+
+#include "../../Framework/Deprecated/Viewport/WidgetViewport.h"
+#include "../../Framework/Deprecated/Widgets/LayoutWidget.h"
+#include <Applications/IStoneApplication.h>
+#include <Platforms/Wasm/WasmPlatformApplicationAdapter.h>
+
+typedef Deprecated::WidgetViewport* ViewportHandle; // the objects exchanged between JS and C++
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+  
+  // JS methods accessible from C++
+  extern void ScheduleWebViewportRedrawFromCpp(ViewportHandle cppViewportHandle);
+  extern void UpdateStoneApplicationStatusFromCppWithString(const char* statusUpdateMessage);
+  extern void UpdateStoneApplicationStatusFromCppWithSerializedMessage(const char* statusUpdateMessage);
+  extern void stone_console_error(const char*);
+  extern void stone_console_warning(const char*);
+  extern void stone_console_info(const char*);
+  extern void stone_console_trace(const char*);
+
+  // C++ methods accessible from JS
+  extern void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle cppViewportHandle);
+  extern void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc, const char* value);
+  
+
+#ifdef __cplusplus
+}
+#endif
+
+// these methods must be implemented in the custom app "mainWasm.cpp"
+extern OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker);
+extern OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, OrthancStone::IStoneApplication* application);
+
+namespace OrthancStone {
+
+  // default Observer to trigger Viewport redraw when something changes in the Viewport
+  class ViewportContentChangedObserver : public IObserver
+  {
+  private:
+    // Flag to avoid flooding JavaScript with redundant Redraw requests
+    bool isScheduled_; 
+
+  public:
+    ViewportContentChangedObserver(MessageBroker& broker) :
+      IObserver(broker),
+      isScheduled_(false)
+    {
+    }
+
+    void Reset()
+    {
+      isScheduled_ = false;
+    }
+
+    void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message)
+    {
+      if (!isScheduled_)
+      {
+        ScheduleWebViewportRedrawFromCpp((ViewportHandle)&message.GetOrigin());  // loosing constness when transmitted to Web
+        isScheduled_ = true;
+      }
+    }
+  };
+
+  // default status bar to log messages on the console/stdout
+  class StatusBar : public Deprecated::IStatusBar
+  {
+  public:
+    virtual void ClearMessage()
+    {
+    }
+
+    virtual void SetMessage(const std::string& message)
+    {
+      printf("%s\n", message.c_str());
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmDelayedCallExecutor.cpp	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,46 @@
+#include "WasmDelayedCallExecutor.h"
+#include "json/value.h"
+#include "json/writer.h"
+#include <emscripten/emscripten.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  extern void WasmDelayedCallExecutor_Schedule(void* callable,
+                                      unsigned int timeoutInMs
+                                      /*void* payload*/);
+
+  void EMSCRIPTEN_KEEPALIVE WasmDelayedCallExecutor_ExecuteCallback(void* callable
+                                                       //void* payload
+                                                       )
+  {
+    if (callable == NULL)
+    {
+      throw;
+    }
+    else
+    {
+      reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IDelayedCallExecutor::TimeoutMessage>*>(callable)->
+        Apply(Deprecated::IDelayedCallExecutor::TimeoutMessage()); // uri, reinterpret_cast<Orthanc::IDynamicObject*>(payload)));
+    }
+  }
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+namespace Deprecated
+{
+  OrthancStone::MessageBroker* WasmDelayedCallExecutor::broker_ = NULL;
+
+
+  void WasmDelayedCallExecutor::Schedule(OrthancStone::MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
+                         unsigned int timeoutInMs)
+  {
+    WasmDelayedCallExecutor_Schedule(callback, timeoutInMs);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmDelayedCallExecutor.h	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h"
+#include <Core/OrthancException.h>
+
+namespace Deprecated
+{
+  class WasmDelayedCallExecutor : public IDelayedCallExecutor
+  {
+  private:
+    static OrthancStone::MessageBroker* broker_;
+
+    // Private constructor => Singleton design pattern
+    WasmDelayedCallExecutor(OrthancStone::MessageBroker& broker) :
+      IDelayedCallExecutor(broker)
+    {
+    }
+
+  public:
+    static WasmDelayedCallExecutor& GetInstance()
+    {
+      if (broker_ == NULL)
+      {
+        printf("WasmDelayedCallExecutor::GetInstance(): broker not initialized\n");
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      }
+      static WasmDelayedCallExecutor instance(*broker_);
+      return instance;
+    }
+
+    static void SetBroker(OrthancStone::MessageBroker& broker)
+    {
+      broker_ = &broker;
+    }
+
+    virtual void Schedule(OrthancStone::MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
+                         unsigned int timeoutInMs = 1000);
+
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmDelayedCallExecutor.js	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,7 @@
+mergeInto(LibraryManager.library, {
+  WasmDelayedCallExecutor_Schedule: function(callable, timeoutInMs/*, payload*/) {
+    setTimeout(function() {
+      window.WasmDelayedCallExecutor_ExecuteCallback(callable/*, payload*/);
+    }, timeoutInMs);
+  }
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmPlatformApplicationAdapter.cpp	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,59 @@
+#include "WasmPlatformApplicationAdapter.h"
+
+#include "Framework/StoneException.h"
+#include <stdio.h>
+#include "Platforms/Wasm/Defaults.h"
+
+namespace OrthancStone
+{
+  WasmPlatformApplicationAdapter::WasmPlatformApplicationAdapter(MessageBroker& broker, IStoneApplication& application)
+  : IObserver(broker),
+    application_(application)
+  {
+  }
+
+  void WasmPlatformApplicationAdapter::HandleSerializedMessageFromWeb(std::string& output, const std::string& input)
+  {
+    try
+    {
+      application_.HandleSerializedMessage(input.c_str());
+    }
+    catch (StoneException& exc)
+    {
+      printf("Error while handling message from web (error code = %d):\n", exc.GetErrorCode());
+      printf("While interpreting input: '%s'\n", input.c_str());
+      output = std::string("ERROR : ");
+    }
+    catch (std::exception& exc)
+    {
+      printf("Error while handling message from web (error text = %s):\n", exc.what());
+      printf("While interpreting input: '%s'\n", input.c_str());
+      output = std::string("ERROR : ");
+    }
+  }
+
+  void WasmPlatformApplicationAdapter::NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage)
+  {
+    try
+    {
+      UpdateStoneApplicationStatusFromCppWithString(statusUpdateMessage.c_str());
+    }
+    catch (...)
+    {
+      printf("Error while handling string message to web\n");
+    }
+  }
+
+  void WasmPlatformApplicationAdapter::NotifyStatusUpdateFromCppToWebWithSerializedMessage(const std::string& statusUpdateMessage)
+  {
+    try
+    {
+      UpdateStoneApplicationStatusFromCppWithSerializedMessage(statusUpdateMessage.c_str());
+    }
+    catch (...)
+    {
+      printf("Error while handling serialized message to web\n");
+    }
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmPlatformApplicationAdapter.h	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <string>
+#include <Framework/Messages/IObserver.h>
+#include <Applications/IStoneApplication.h>
+
+namespace OrthancStone
+{
+  class WasmPlatformApplicationAdapter : public IObserver
+  {
+      IStoneApplication&  application_;
+    public:
+      WasmPlatformApplicationAdapter(MessageBroker& broker, IStoneApplication& application);
+
+      virtual void HandleSerializedMessageFromWeb(std::string& output, const std::string& input);
+      virtual void NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage);
+      virtual void NotifyStatusUpdateFromCppToWebWithSerializedMessage(const std::string& statusUpdateMessage);
+  };
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmViewport.cpp	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,13 @@
+#include "WasmViewport.h"
+
+#include <vector>
+#include <memory>
+
+std::vector<std::shared_ptr<Deprecated::WidgetViewport>> wasmViewports;
+
+void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget) {
+    std::shared_ptr<Deprecated::WidgetViewport> viewport(CreateWasmViewportFromCpp(htmlCanvasId));
+    viewport->SetCentralWidget(centralWidget);
+
+    wasmViewports.push_back(viewport);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmViewport.h	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "../../Framework/Deprecated/Viewport/WidgetViewport.h"
+
+#include <emscripten/emscripten.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  // JS methods accessible from C++
+  extern Deprecated::WidgetViewport* CreateWasmViewportFromCpp(const char* htmlCanvasId);
+
+#ifdef __cplusplus
+}
+#endif
+
+extern void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmWebService.cpp	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,168 @@
+#include "WasmWebService.h"
+#include "json/value.h"
+#include "json/writer.h"
+#include <emscripten/emscripten.h>
+#include <boost/shared_ptr.hpp>
+
+struct CachedSuccessNotification
+{
+  boost::shared_ptr<Deprecated::BaseWebService::CachedHttpRequestSuccessMessage>    cachedMessage;
+  std::unique_ptr<Orthanc::IDynamicObject>                                              payload;
+  OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback;
+};
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  extern void WasmWebService_GetAsync(void* callableSuccess,
+                                      void* callableFailure,
+                                      const char* uri,
+                                      const char* headersInJsonString,
+                                      void* payload,
+                                      unsigned int timeoutInSeconds);
+
+  extern void WasmWebService_ScheduleLaterCachedSuccessNotification(void* brol);
+
+  extern void WasmWebService_PostAsync(void* callableSuccess,
+                                       void* callableFailure,
+                                       const char* uri,
+                                       const char* headersInJsonString,
+                                       const void* body,
+                                       size_t bodySize,
+                                       void* payload,
+                                       unsigned int timeoutInSeconds);
+
+  extern void WasmWebService_DeleteAsync(void* callableSuccess,
+                                         void* callableFailure,
+                                         const char* uri,
+                                         const char* headersInJsonString,
+                                         void* payload,
+                                         unsigned int timeoutInSeconds);
+
+  void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyError(void* failureCallable,
+                                                       const char* uri,
+                                                       unsigned int httpStatus,
+                                                       void* payload)
+  {
+    if (failureCallable != NULL)
+    {
+      reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>*>(failureCallable)->
+        Apply(Deprecated::IWebService::HttpRequestErrorMessage(uri, static_cast<Orthanc::HttpStatus>(httpStatus), reinterpret_cast<Orthanc::IDynamicObject*>(payload)));
+    }
+  }
+
+  void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyCachedSuccess(void* notification_)
+  {
+    // notification has been allocated in C++ and passed to JS.  It must be deleted by this method
+    std::unique_ptr<CachedSuccessNotification> notification(reinterpret_cast<CachedSuccessNotification*>(notification_));
+
+    notification->successCallback->Apply(Deprecated::IWebService::HttpRequestSuccessMessage(
+      notification->cachedMessage->GetUri(), 
+      notification->cachedMessage->GetAnswer(),
+      notification->cachedMessage->GetAnswerSize(),
+      notification->cachedMessage->GetAnswerHttpHeaders(),
+      notification->payload.get()
+      ));
+  }
+
+  void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifySuccess(void* successCallable,
+                                                         const char* uri,
+                                                         const void* body,
+                                                         size_t bodySize,
+                                                         const char* answerHeaders,
+                                                         void* payload)
+  {
+    if (successCallable != NULL)
+    {
+      Deprecated::IWebService::HttpHeaders headers;
+
+      // TODO - Parse "answerHeaders"
+      //printf("TODO: parse headers [%s]\n", answerHeaders);
+      
+      reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>*>(successCallable)->
+        Apply(Deprecated::IWebService::HttpRequestSuccessMessage(uri, body, bodySize, headers,
+                                                                   reinterpret_cast<Orthanc::IDynamicObject*>(payload)));
+    }
+  }
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+namespace Deprecated
+{
+  OrthancStone::MessageBroker* WasmWebService::broker_ = NULL;
+
+  void ToJsonString(std::string& output, const IWebService::HttpHeaders& headers)
+  {
+    Json::Value jsonHeaders;
+    for (IWebService::HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); it++ )
+    {
+      jsonHeaders[it->first] = it->second;
+    }
+
+    Json::StreamWriterBuilder builder;
+    std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
+    std::ostringstream outputStr;
+
+    writer->write(jsonHeaders, &outputStr);
+    output = outputStr.str();
+  }
+
+  void WasmWebService::PostAsync(const std::string& relativeUri,
+                                 const HttpHeaders& headers,
+                                 const std::string& body,
+                                 Orthanc::IDynamicObject* payload,
+                                 OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
+                                 OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable,
+                                 unsigned int timeoutInSeconds)
+  {
+    std::string headersInJsonString;
+    ToJsonString(headersInJsonString, headers);
+    WasmWebService_PostAsync(successCallable, failureCallable, relativeUri.c_str(), headersInJsonString.c_str(),
+                             body.c_str(), body.size(), payload, timeoutInSeconds);
+  }
+
+  void WasmWebService::DeleteAsync(const std::string& relativeUri,
+                                   const HttpHeaders& headers,
+                                   Orthanc::IDynamicObject* payload,
+                                   OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
+                                   OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable,
+                                   unsigned int timeoutInSeconds)
+  {
+    std::string headersInJsonString;
+    ToJsonString(headersInJsonString, headers);
+    WasmWebService_DeleteAsync(successCallable, failureCallable, relativeUri.c_str(), headersInJsonString.c_str(),
+                               payload, timeoutInSeconds);
+  }
+
+  void WasmWebService::GetAsyncInternal(const std::string &relativeUri,
+                                        const HttpHeaders &headers,
+                                        Orthanc::IDynamicObject *payload,
+                                        OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
+                                        OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable,
+                                        unsigned int timeoutInSeconds)
+  {
+    std::string headersInJsonString;
+    ToJsonString(headersInJsonString, headers);
+    WasmWebService_GetAsync(successCallable, failureCallable, relativeUri.c_str(),
+                            headersInJsonString.c_str(), payload, timeoutInSeconds);
+  }
+
+  void WasmWebService::NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage,
+                                              Orthanc::IDynamicObject* payload, // takes ownership
+                                              OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback)
+  {
+    CachedSuccessNotification* notification = new CachedSuccessNotification();  // allocated on the heap, it will be passed to JS and deleted when coming back to C++
+    notification->cachedMessage = cachedMessage;
+    notification->payload.reset(payload);
+    notification->successCallback = successCallback;
+
+    WasmWebService_ScheduleLaterCachedSuccessNotification(notification);
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmWebService.h	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,62 @@
+#pragma once
+
+#include "../../Framework/Deprecated/Toolbox/BaseWebService.h"
+#include <Core/OrthancException.h>
+
+namespace Deprecated
+{
+class WasmWebService : public BaseWebService
+{
+private:
+  static OrthancStone::MessageBroker *broker_;
+
+  // Private constructor => Singleton design pattern
+  WasmWebService(OrthancStone::MessageBroker &broker) : BaseWebService(broker)
+  {
+  }
+
+public:
+  static WasmWebService &GetInstance()
+  {
+    if (broker_ == NULL)
+    {
+      printf("WasmWebService::GetInstance(): broker not initialized\n");
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    static WasmWebService instance(*broker_);
+    return instance;
+  }
+
+  static void SetBroker(OrthancStone::MessageBroker &broker)
+  {
+    broker_ = &broker;
+  }
+
+  virtual void PostAsync(const std::string &uri,
+                         const HttpHeaders &headers,
+                         const std::string &body,
+                         Orthanc::IDynamicObject *payload,
+                         OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
+                         OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL,
+                         unsigned int timeoutInSeconds = 60);
+
+  virtual void DeleteAsync(const std::string &uri,
+                           const HttpHeaders &headers,
+                           Orthanc::IDynamicObject *payload,
+                           OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
+                           OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL,
+                           unsigned int timeoutInSeconds = 60);
+
+protected:
+  virtual void GetAsyncInternal(const std::string &uri,
+                                const HttpHeaders &headers,
+                                Orthanc::IDynamicObject *payload,
+                                OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
+                                OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL,
+                                unsigned int timeoutInSeconds = 60);
+
+  virtual void NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedHttpMessage,
+                                      Orthanc::IDynamicObject *payload, // takes ownership
+                                      OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallback);
+};
+} // namespace Deprecated
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmWebService.js	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,108 @@
+mergeInto(LibraryManager.library, {
+  WasmWebService_GetAsync: function(callableSuccess, callableFailure, url, headersInJsonString, payload, timeoutInSeconds) {
+    // 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 url_ = UTF8ToString(url);
+    var headersInJsonString_ = UTF8ToString(headersInJsonString);
+
+    xhr.open('GET', url_, true);
+    xhr.responseType = 'arraybuffer';
+    xhr.timeout = timeoutInSeconds * 1000;
+    var headers = JSON.parse(headersInJsonString_);
+    for (var key in headers) {
+      xhr.setRequestHeader(key, headers[key]);
+    }
+    //console.log(xhr); 
+    xhr.onreadystatechange = function() {
+      if (this.readyState == XMLHttpRequest.DONE) {
+        if (xhr.status === 200) {
+          var s = xhr.getAllResponseHeaders();
+          var headers = _malloc(s.length + 1);
+          stringToUTF8(s, headers, s.length + 1);
+          
+          // TODO - Is "new Uint8Array()" necessary? This copies the
+          // answer to the WebAssembly stack, hence necessitating
+          // increasing the TOTAL_STACK parameter of Emscripten
+          window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response),
+                                       this.response.byteLength, headers, payload);
+        } else {
+          window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload);
+        }
+      }
+    }
+    
+    xhr.send();
+  },
+
+  WasmWebService_ScheduleLaterCachedSuccessNotification: function (brol) {
+    setTimeout(function() {
+      window.WasmWebService_NotifyCachedSuccess(brol);
+    }, 0);
+  },
+
+  WasmWebService_PostAsync: function(callableSuccess, callableFailure, url, headersInJsonString, body, bodySize, payload, timeoutInSeconds) {
+    var xhr = new XMLHttpRequest();
+    var url_ = UTF8ToString(url);
+    var headersInJsonString_ = UTF8ToString(headersInJsonString);
+    xhr.open('POST', url_, true);
+    xhr.timeout = timeoutInSeconds * 1000;
+    xhr.responseType = 'arraybuffer';
+    xhr.setRequestHeader('Content-type', 'application/octet-stream');
+
+    var headers = JSON.parse(headersInJsonString_);
+    for (var key in headers) {
+      xhr.setRequestHeader(key, headers[key]);
+    }
+    
+    xhr.onreadystatechange = function() {
+      if (this.readyState == XMLHttpRequest.DONE) {
+        if (xhr.status === 200) {
+          var s = xhr.getAllResponseHeaders();
+          var headers = _malloc(s.length + 1);
+          stringToUTF8(s, headers, s.length + 1);
+
+          window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response),
+                                       this.response.byteLength, headers, payload);
+        } else {
+          window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload);
+        }
+      }
+    }
+
+    xhr.send(new Uint8ClampedArray(HEAPU8.buffer, body, bodySize));
+  },
+
+  WasmWebService_DeleteAsync: function(callableSuccess, callableFailure, url, headersInJsonString, payload, timeoutInSeconds) {
+    var xhr = new XMLHttpRequest();
+    var url_ = UTF8ToString(url);
+    var headersInJsonString_ = UTF8ToString(headersInJsonString);
+    xhr.open('DELETE', url_, true);
+    xhr.timeout = timeoutInSeconds * 1000;
+    xhr.responseType = 'arraybuffer';
+    xhr.setRequestHeader('Content-type', 'application/octet-stream');
+  
+    var headers = JSON.parse(headersInJsonString_);
+    for (var key in headers) {
+      xhr.setRequestHeader(key, headers[key]);
+    }
+    
+    xhr.onreadystatechange = function() {
+      if (this.readyState == XMLHttpRequest.DONE) {
+        if (xhr.status === 200) {
+          var s = xhr.getAllResponseHeaders();
+          var headers = _malloc(s.length + 1);
+          stringToUTF8(s, headers, s.length + 1);
+
+          window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response),
+                                       this.response.byteLength, headers, payload);
+        } else {
+          window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload);
+        }
+      }
+    }
+  
+    xhr.send();
+  }
+  
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/default-library.js	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,49 @@
+// this file contains the JS method you want to expose to C++ code
+
+mergeInto(LibraryManager.library, {
+
+  ScheduleWebViewportRedrawFromCpp: function(cppViewportHandle) {
+    window.ScheduleWebViewportRedraw(cppViewportHandle);
+  },
+
+  CreateWasmViewportFromCpp: function(htmlCanvasId) {
+    return window.CreateWasmViewport(htmlCanvasId);
+  },
+
+  // each time the StoneApplication updates its status, it may signal it 
+  // through this method. i.e, to change the status of a button in the web interface
+  UpdateStoneApplicationStatusFromCppWithString: function(statusUpdateMessage) {
+    var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage);
+    window.UpdateWebApplicationWithString(statusUpdateMessage_);
+  },
+
+  // same, but with a serialized message
+  UpdateStoneApplicationStatusFromCppWithSerializedMessage: function(statusUpdateMessage) {
+    var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage);
+    window.UpdateWebApplicationWithSerializedMessage(statusUpdateMessage_);
+  },
+
+  // These functions are called from C++ (through an extern declaration) 
+  // and call the standard logger that, here, routes to the console.
+
+  stone_console_error : function(message) {
+    var text = UTF8ToString(message);
+    window.errorFromCpp(text);
+  },
+
+  stone_console_warning : function(message) {
+    var text = UTF8ToString(message);
+    window.warningFromCpp(text);
+  },
+
+  stone_console_info: function(message) {
+    var text = UTF8ToString(message);
+    window.infoFromCpp(text);
+  },
+  
+  stone_console_trace : function(message) {
+    var text = UTF8ToString(message);
+    window.debugFromCpp(text);
+  }
+
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/logger.ts	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,111 @@
+export enum LogSource {
+  Cpp,
+  Typescript
+}
+
+export class StandardConsoleLogger {
+  public showSource: boolean = true;
+
+  public debug(...args: any[]): void {
+    this._debug(LogSource.Typescript, ...args);
+  }
+
+  public debugFromCpp(...args: any[]): void {
+    this._debug(LogSource.Cpp, ...args);
+  }
+
+  public info(...args: any[]): void {
+    this._info(LogSource.Typescript, ...args);
+  }
+
+  public infoFromCpp(message: string): void {
+    this._info(LogSource.Cpp, message);
+  }
+
+  public warning(...args: any[]): void {
+    this._warning(LogSource.Typescript, ...args);
+  }
+
+  public warningFromCpp(message: string): void {
+    this._warning(LogSource.Cpp, message);
+  }
+
+  public error(...args: any[]): void {
+    this._error(LogSource.Typescript, ...args);
+  }
+
+  public errorFromCpp(message: string): void {
+    this._error(LogSource.Cpp, message);
+  }
+
+  public _debug(source: LogSource, ...args: any[]): void {
+    if ((<any> window).IsTraceLevelEnabled)
+    {
+      if ((<any> window).IsTraceLevelEnabled())
+      {
+        var output = this.getOutput(source, args);
+        console.debug(...output);
+      }
+    }
+  }
+
+  private _info(source: LogSource, ...args: any[]): void {
+    if ((<any> window).IsInfoLevelEnabled)
+    {
+      if ((<any> window).IsInfoLevelEnabled())
+      {
+        var output = this.getOutput(source, args);
+        console.info(...output);
+      }
+    }
+  }
+
+  public _warning(source: LogSource, ...args: any[]): void {
+    var output = this.getOutput(source, args);
+    console.warn(...output);
+  }
+
+  public _error(source: LogSource, ...args: any[]): void {
+    var output = this.getOutput(source, args);
+    console.error(...output);
+  }
+
+
+  private getOutput(source: LogSource, args: any[]): any[] {
+    var prefix = this.getPrefix();
+    var prefixAndSource = Array<string>();
+
+    if (prefix != null) {
+      prefixAndSource = [prefix];
+    } 
+
+    if (this.showSource) {
+      if (source == LogSource.Typescript) {
+        prefixAndSource = [...prefixAndSource, "TS "];
+      } else if (source == LogSource.Cpp) {
+        prefixAndSource = [...prefixAndSource, "C++"];
+      }
+    }
+
+    if (prefixAndSource.length > 0) {
+      prefixAndSource = [...prefixAndSource, "|"];
+    }
+
+    return [...prefixAndSource, ...args];
+  }
+
+  protected getPrefix(): string | null {
+    return null;
+  }
+}
+
+export class TimeConsoleLogger extends StandardConsoleLogger {
+  protected getPrefix(): string {
+    let now = new Date();
+    let timeString = now.getHours().toString().padStart(2, "0") + ":" + now.getMinutes().toString().padStart(2, "0") + ":" + now.getSeconds().toString().padStart(2, "0") + "." + now.getMilliseconds().toString().padStart(3, "0");
+    return timeString;
+  }
+}
+
+export var defaultLogger: StandardConsoleLogger = new TimeConsoleLogger();
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/stone-framework-loader.ts	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,95 @@
+/**
+ * This file contains primitives to interface with WebAssembly and
+ * with the Stone framework.
+ **/
+import * as Logger from './logger'
+
+export declare type InitializationCallback = () => void;
+
+//export declare var StoneFrameworkModule : any;
+export var StoneFrameworkModule : any;
+
+//const ASSETS_FOLDER : string = "assets/lib";
+//const WASM_FILENAME : string = "orthanc-framework";
+
+export class Framework
+{
+  private static singleton_ : Framework = null;
+  private static wasmModuleName_ : string = null;
+
+  public static Configure(wasmModuleName: string) {
+    this.wasmModuleName_ = wasmModuleName;
+  }
+
+  private constructor(verbose : boolean) 
+  {
+    //this.ccall('Initialize', null, [ 'number' ], [ verbose ]);
+  }
+
+  
+  public ccall( name: string,
+                returnType: string,
+                argTypes: Array<string>,
+                argValues: Array<any>) : any
+  {
+    return (<any> window).StoneFrameworkModule.ccall(name, returnType, argTypes, argValues);
+  }
+
+  
+  public cwrap( name: string,
+                returnType: string,
+                argTypes: Array<string>) : any
+  {
+    return (<any> window).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)
+  {
+    Logger.defaultLogger.debug('Initializing WebAssembly Module');
+
+    (<any> window).errorFromCpp = function(text:any) { Logger.defaultLogger.errorFromCpp(text); };
+    (<any> window).warningFromCpp = function(text:any) { Logger.defaultLogger.warningFromCpp(text); };
+    (<any> window).infoFromCpp = function(text:any) { Logger.defaultLogger.infoFromCpp(text); };
+    (<any> window).debugFromCpp = function(text:any) { Logger.defaultLogger.debugFromCpp(text); };
+
+    // (<any> window).
+    (<any> window).StoneFrameworkModule = {
+      preRun: [ 
+        function() {
+          Logger.defaultLogger.debug('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
+          Logger.defaultLogger.debug('WebAssembly is ready');
+          Framework.singleton_ = new Framework(verbose);
+          callback();
+        }
+      ],
+      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.src = this.wasmModuleName_ + ".js";//  "OrthancStoneSimpleViewer.js"; // ASSETS_FOLDER + '/' + WASM_FILENAME + '.js';
+    script.async = true;
+    document.head.appendChild(script);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/tsconfig-stone.json	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,8 @@
+{
+    "include" : [
+        "stone-framework-loader.ts",
+        "logger.ts",
+        "wasm-application-runner.ts",
+        "wasm-viewport.ts"
+    ]
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/wasm-application-runner.ts	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,141 @@
+import * as Stone from './stone-framework-loader'
+import * as StoneViewport from './wasm-viewport'
+import * as Logger from './logger'
+
+if (!('WebAssembly' in window)) {
+  alert('Sorry, your browser does not support WebAssembly :(');
+}
+
+//var StoneFrameworkModule : Stone.Framework = (<any>window).StoneFrameworkModule;
+//export declare var StoneFrameworkModule : Stone.Framework;
+
+// global functions
+var WasmWebService_NotifyError: Function = null;
+var WasmWebService_NotifySuccess: Function = null;
+var WasmWebService_NotifyCachedSuccess: Function = null;
+var WasmDelayedCallExecutor_ExecuteCallback: Function = null;
+var WasmDoAnimation: Function = null;
+var SetStartupParameter: Function = null;
+var CreateWasmApplication: Function = null;
+export var CreateCppViewport: Function = null;
+var ReleaseCppViewport: Function = null;
+var StartWasmApplication: Function = null;
+export var SendSerializedMessageToStoneApplication: Function = null;
+
+var auxiliaryParameters : Map<string,string>  = null;
+
+export function SetApplicationParameters(params : Map<string,string>) {
+  if (auxiliaryParameters != null) {
+    console.warn("wasm-application-runner.SetApplicationParameters: about to overwrite the existing application parameters!")
+  }
+  auxiliaryParameters = params;
+}
+
+function DoAnimationThread() {
+  if (WasmDoAnimation != null) {
+    WasmDoAnimation();
+  }
+
+  // Update the viewport content every 100ms if need be
+  setTimeout(DoAnimationThread, 100);  
+}
+
+
+function GetUriParameters(): Map<string, string> {
+  var parameters = window.location.search.substr(1);
+
+  if (parameters != null &&
+    parameters != '') {
+    var result = new Map<string, string>();
+    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]);
+      } else if(tmp.length == 1) {
+        // if there is no '=', we treat ot afterwards as a flag-style param
+        result[tmp[0]] = "";
+      }
+    }
+  return result;
+  }
+  else {
+    return new Map<string, string>();
+  }
+}
+
+// function UpdateWebApplication(statusUpdateMessage: string) {
+//   console.log(statusUpdateMessage);
+// }
+
+function _InitializeWasmApplication(orthancBaseUrl: string): void {
+
+  CreateWasmApplication();
+
+  // transmit the API-specified parameters to the app before initializing it
+  for (let key in auxiliaryParameters) {
+    if (auxiliaryParameters.hasOwnProperty(key)) {
+      Logger.defaultLogger.debug(
+        `About to call SetStartupParameter("${key}","${auxiliaryParameters[key]}")`);
+      SetStartupParameter(key, auxiliaryParameters[key]);
+    }
+  }
+
+  // parse uri and transmit the URI parameters to the app before initializing it
+  let parameters = GetUriParameters();
+
+  for (let key in parameters) {
+    if (parameters.hasOwnProperty(key)) {
+      Logger.defaultLogger.debug(
+        `About to call SetStartupParameter("${key}","${parameters[key]}")`);
+      SetStartupParameter(key, parameters[key]);
+    }
+  }
+
+  StartWasmApplication(orthancBaseUrl);
+
+  // trigger a first resize of the canvas that has just been initialized
+  StoneViewport.WasmViewport.ResizeAll();
+
+  DoAnimationThread();
+}
+
+export function InitializeWasmApplication(wasmModuleName: string, orthancBaseUrl: string) {
+  
+  Stone.Framework.Configure(wasmModuleName);
+
+  // 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 () {
+
+    Logger.defaultLogger.debug("Connecting C++ methods to JS methods");
+    
+    SetStartupParameter = (<any> window).StoneFrameworkModule.cwrap('SetStartupParameter', null, ['string', 'string']);
+    CreateWasmApplication = (<any> window).StoneFrameworkModule.cwrap('CreateWasmApplication', null, ['number']);
+    CreateCppViewport = (<any> window).StoneFrameworkModule.cwrap('CreateCppViewport', 'number', []);
+    ReleaseCppViewport = (<any> window).StoneFrameworkModule.cwrap('ReleaseCppViewport', null, ['number']);
+    StartWasmApplication = (<any> window).StoneFrameworkModule.cwrap('StartWasmApplication', null, ['string']);
+    (<any> window).IsTraceLevelEnabled = (<any> window).StoneFrameworkModule.cwrap('WasmIsTraceLevelEnabled', 'boolean', null);
+    (<any> window).IsInfoLevelEnabled = (<any> window).StoneFrameworkModule.cwrap('WasmIsInfoLevelEnabled', 'boolean', null);
+
+    (<any> window).WasmWebService_NotifyCachedSuccess = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifyCachedSuccess', null, ['number']);
+    (<any> window).WasmWebService_NotifySuccess = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifySuccess', null, ['number', 'string', 'array', 'number', 'number']);
+    (<any> window).WasmWebService_NotifyError = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifyError', null, ['number', 'string', 'number', 'number']);
+    (<any> window).WasmDelayedCallExecutor_ExecuteCallback = (<any> window).StoneFrameworkModule.cwrap('WasmDelayedCallExecutor_ExecuteCallback', null, ['number']);
+    // no need to put this into the globals for it's only used in this very module
+    WasmDoAnimation = (<any> window).StoneFrameworkModule.cwrap('WasmDoAnimation', null, []);
+
+    SendSerializedMessageToStoneApplication = (<any> window).StoneFrameworkModule.cwrap('SendSerializedMessageToStoneApplication', 'string', ['string']);
+
+    Logger.defaultLogger.debug("Connecting C++ methods to JS methods - done");
+
+    _InitializeWasmApplication(orthancBaseUrl);
+  });
+}
+
+
+// exports.InitializeWasmApplication = InitializeWasmApplication;
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/wasm-viewport.ts	Wed Apr 29 20:45:14 2020 +0200
@@ -0,0 +1,359 @@
+import * as wasmApplicationRunner from './wasm-application-runner'
+import * as Logger from './logger'
+
+var isPendingRedraw = false;
+
+function ScheduleWebViewportRedraw(cppViewportHandle: any) : void
+{
+  if (!isPendingRedraw) {
+    isPendingRedraw = true;
+    Logger.defaultLogger.debug('Scheduling a refresh of the viewport, as its content changed');
+    window.requestAnimationFrame(function() {
+      isPendingRedraw = false;
+      let viewport = WasmViewport.GetFromCppViewport(cppViewportHandle);
+      if (viewport) {
+        viewport.Redraw();
+      }
+    });
+  }
+}
+
+(<any>window).ScheduleWebViewportRedraw = ScheduleWebViewportRedraw;
+
+declare function UTF8ToString(v: any): string;
+
+function CreateWasmViewport(htmlCanvasId: string) : any {
+  var cppViewportHandle = wasmApplicationRunner.CreateCppViewport();
+  var canvasId = UTF8ToString(htmlCanvasId);
+  var webViewport = new WasmViewport((<any> window).StoneFrameworkModule, canvasId, cppViewportHandle);  // viewports are stored in a static map in WasmViewport -> won't be deleted
+  webViewport.Initialize();
+
+  return cppViewportHandle;
+}
+ 
+(<any>window).CreateWasmViewport = CreateWasmViewport;
+
+export class WasmViewport {
+
+    private static viewportsMapByCppHandle_ : Map<number, WasmViewport> = new Map<number, WasmViewport>(); // key = the C++ handle
+    private static viewportsMapByCanvasId_ : Map<string, WasmViewport> = new Map<string, WasmViewport>(); // key = the canvasId
+
+    private module_ : any;
+    private canvasId_ : string;
+    private htmlCanvas_ : HTMLCanvasElement;
+    private context_ : CanvasRenderingContext2D | null;
+    private imageData_ : any = null;
+    private renderingBuffer_ : any = null;
+    
+    private touchGestureInProgress_: boolean = false;
+    private touchCount_: number = 0;
+    private touchGestureLastCoordinates_: [number, number][] = []; // last x,y coordinates of each touch
+    
+    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 ViewportTouchStart : Function;
+    private ViewportTouchMove : Function;
+    private ViewportTouchEnd : Function;
+
+    private pimpl_ : any; // Private pointer to the underlying WebAssembly C++ object
+
+    public constructor(module: any, canvasId: string, cppViewport: any) {
+      
+      this.pimpl_ = cppViewport;
+      WasmViewport.viewportsMapByCppHandle_[this.pimpl_] = this;
+      WasmViewport.viewportsMapByCanvasId_[canvasId] = this;
+
+      this.module_ = module;
+      this.canvasId_ = canvasId;
+      this.htmlCanvas_ = document.getElementById(this.canvasId_) as HTMLCanvasElement;
+      if (this.htmlCanvas_ == null) {
+        Logger.defaultLogger.error("Can not create WasmViewport, did not find the canvas whose id is '", this.canvasId_, "'");
+      }
+      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', 'number', 'string', 'number', 'number' ]);
+      this.ViewportTouchStart = this.module_.cwrap('ViewportTouchStart', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]);
+      this.ViewportTouchMove = this.module_.cwrap('ViewportTouchMove', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]);
+      this.ViewportTouchEnd = this.module_.cwrap('ViewportTouchEnd', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]);
+    }
+
+    public GetCppViewport() : number {
+      return this.pimpl_;
+    }
+
+    public static GetFromCppViewport(cppViewportHandle: number) : WasmViewport | null {
+      if (WasmViewport.viewportsMapByCppHandle_[cppViewportHandle] !== undefined) {
+        return WasmViewport.viewportsMapByCppHandle_[cppViewportHandle];
+      }
+      Logger.defaultLogger.error("WasmViewport not found !");
+      return null;
+    }
+
+    public static GetFromCanvasId(canvasId: string) : WasmViewport | null {
+      if (WasmViewport.viewportsMapByCanvasId_[canvasId] !== undefined) {
+        return WasmViewport.viewportsMapByCanvasId_[canvasId];
+      }
+      Logger.defaultLogger.error("WasmViewport not found !");
+      return null;
+    }
+
+    public static ResizeAll() {
+      for (let canvasId in WasmViewport.viewportsMapByCanvasId_) {
+        WasmViewport.viewportsMapByCanvasId_[canvasId].Resize();
+      }
+    }
+
+    public Redraw() {
+      if (this.imageData_ === null ||
+          this.renderingBuffer_ === null ||
+          this.ViewportRender(this.pimpl_,
+                         this.imageData_.width,
+                         this.imageData_.height,
+                         this.renderingBuffer_) == 0) {
+        Logger.defaultLogger.error('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_.HEAPU8.buffer,
+          this.renderingBuffer_,
+          this.imageData_.width * this.imageData_.height * 4));
+        
+        if (this.context_) {
+          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 is defined by the parent width/height
+      if (this.htmlCanvas_.parentElement) {
+        this.htmlCanvas_.width = this.htmlCanvas_.parentElement.offsetWidth;  
+        this.htmlCanvas_.height = this.htmlCanvas_.parentElement.offsetHeight;  
+
+        Logger.defaultLogger.debug("resizing WasmViewport: ", this.htmlCanvas_.width, "x", this.htmlCanvas_.height);
+
+        if (this.imageData_ === null && this.context_) {
+          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);
+        } else {
+          this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height);
+        }
+        
+        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 detect modifier keys*/);    
+      });
+    
+      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) {
+        var keyChar: string | null = event.key;
+        var keyCode = event.keyCode
+        if (keyChar.length == 1) {
+          keyCode = 0; // maps to OrthancStone::KeyboardKeys_Generic
+        } else {
+          keyChar = null;
+        }
+//        console.log("key: ", keyCode, keyChar);
+        that.ViewportKeyPressed(that.pimpl_, keyCode, keyChar, 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();
+      }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface
+
+      this.htmlCanvas_.addEventListener('touchstart', function(event: TouchEvent) {
+        // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport)
+        event.preventDefault();
+        event.stopPropagation();
+
+        // TODO: find a way to pass the coordinates as an array between JS and C++
+        var x0 = 0;
+        var y0 = 0;
+        var x1 = 0;
+        var y1 = 0;
+        var x2 = 0;
+        var y2 = 0;
+        if (event.targetTouches.length > 0) {
+          x0 = event.targetTouches[0].pageX;
+          y0 = event.targetTouches[0].pageY;
+        }
+        if (event.targetTouches.length > 1) {
+          x1 = event.targetTouches[1].pageX;
+          y1 = event.targetTouches[1].pageY;
+        }
+        if (event.targetTouches.length > 2) {
+          x2 = event.targetTouches[2].pageX;
+          y2 = event.targetTouches[2].pageY;
+        }
+
+        that.ViewportTouchStart(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2);
+      }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface
+    
+      this.htmlCanvas_.addEventListener('touchend', function(event) {
+        // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport)
+        event.preventDefault();
+        event.stopPropagation();
+
+        // TODO: find a way to pass the coordinates as an array between JS and C++
+        var x0 = 0;
+        var y0 = 0;
+        var x1 = 0;
+        var y1 = 0;
+        var x2 = 0;
+        var y2 = 0;
+        if (event.targetTouches.length > 0) {
+          x0 = event.targetTouches[0].pageX;
+          y0 = event.targetTouches[0].pageY;
+        }
+        if (event.targetTouches.length > 1) {
+          x1 = event.targetTouches[1].pageX;
+          y1 = event.targetTouches[1].pageY;
+        }
+        if (event.targetTouches.length > 2) {
+          x2 = event.targetTouches[2].pageX;
+          y2 = event.targetTouches[2].pageY;
+        }
+
+        that.ViewportTouchEnd(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2);
+      });
+    
+      this.htmlCanvas_.addEventListener('touchmove', function(event: TouchEvent) {
+
+        // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport)
+        event.preventDefault();
+        event.stopPropagation();
+
+
+        // TODO: find a way to pass the coordinates as an array between JS and C++
+        var x0 = 0;
+        var y0 = 0;
+        var x1 = 0;
+        var y1 = 0;
+        var x2 = 0;
+        var y2 = 0;
+        if (event.targetTouches.length > 0) {
+          x0 = event.targetTouches[0].pageX;
+          y0 = event.targetTouches[0].pageY;
+        }
+        if (event.targetTouches.length > 1) {
+          x1 = event.targetTouches[1].pageX;
+          y1 = event.targetTouches[1].pageY;
+        }
+        if (event.targetTouches.length > 2) {
+          x2 = event.targetTouches[2].pageX;
+          y2 = event.targetTouches[2].pageY;
+        }
+
+        that.ViewportTouchMove(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2);
+        return;
+
+      }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface
+    }  
+
+  public ResetTouch() {
+    if (this.touchTranslation_ ||
+        this.touchZoom_) {
+      this.ViewportMouseUp(this.pimpl_);
+    }
+
+    this.touchTranslation_ = false;
+    this.touchZoom_ = false;
+  }
+  
+  public GetTouchTranslation(event: any) {
+    var touch = event.targetTouches[0];
+    return [
+      touch.pageX,
+      touch.pageY
+    ];
+  }
+    
+  public GetTouchZoom(event: any) {
+    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
+    ];
+  }
+   
+}
--- a/Platforms/Generic/DelayedCallCommand.cpp	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "DelayedCallCommand.h"
-#include "boost/thread/thread.hpp"
-
-#include <iostream>
-
-namespace Deprecated
-{
-  DelayedCallCommand::DelayedCallCommand(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,  // takes ownership
-                                         unsigned int timeoutInMs,
-                                         Orthanc::IDynamicObject* payload /* takes ownership */,
-                                         OrthancStone::NativeStoneApplicationContext& context
-                                         ) :
-    callback_(callback),
-    payload_(payload),
-    context_(context),
-    expirationTimePoint_(boost::posix_time::microsec_clock::local_time() + boost::posix_time::milliseconds(timeoutInMs)),
-    timeoutInMs_(timeoutInMs)
-  {
-  }
-
-
-  void DelayedCallCommand::Execute()
-  {
-    while (boost::posix_time::microsec_clock::local_time() < expirationTimePoint_)
-    {
-      boost::this_thread::sleep(boost::posix_time::milliseconds(1));
-    }
-  }
-
-  void DelayedCallCommand::Commit()
-  {
-    // We want to make sure that, i.e, the UpdateThread is not
-    // triggered while we are updating the "model" with the result of
-    // an OracleCommand
-    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_);
-
-    if (callback_.get() != NULL)
-    {
-      IDelayedCallExecutor::TimeoutMessage message; // TODO: add payload
-      callback_->Apply(message);
-    }
-  }
-}
--- a/Platforms/Generic/DelayedCallCommand.h	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "IOracleCommand.h"
-
-#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h"
-#include "../../Framework/Messages/IObservable.h"
-#include "../../Framework/Messages/ICallable.h"
-#include "../../Applications/Generic/NativeStoneApplicationContext.h"
-
-#include <boost/date_time/posix_time/posix_time.hpp>
-
-namespace Deprecated
-{
-  class DelayedCallCommand : public IOracleCommand, OrthancStone::IObservable
-  {
-  protected:
-    std::unique_ptr<MessageHandler<IDelayedCallExecutor::TimeoutMessage> >  callback_;
-    std::unique_ptr<Orthanc::IDynamicObject>  payload_;
-    OrthancStone::NativeStoneApplicationContext&          context_;
-    boost::posix_time::ptime                expirationTimePoint_;
-    unsigned int                            timeoutInMs_;
-
-  public:
-    DelayedCallCommand(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,  // takes ownership
-                       unsigned int timeoutInMs,
-                       Orthanc::IDynamicObject* payload /* takes ownership */,
-                       OrthancStone::NativeStoneApplicationContext& context
-                       );
-
-    virtual void Execute();
-
-    virtual void Commit();
-  };
-
-}
--- a/Platforms/Generic/IOracleCommand.h	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include <Core/IDynamicObject.h>
-
-namespace Deprecated
-{
-  class IOracleCommand : public Orthanc::IDynamicObject
-  {
-  public:
-    virtual ~IOracleCommand()
-    {
-    }
-
-    // This part of the command can be invoked simultaneously, and
-    // must not modify the Stone context
-    virtual void Execute() = 0;
-
-    // This part of the command must be invoked in mutual exclusion
-    virtual void Commit() = 0;
-  };
-}
--- a/Platforms/Generic/Oracle.cpp	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,212 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "Oracle.h"
-
-#include <Core/Logging.h>
-#include <Core/MultiThreading/SharedMessageQueue.h>
-#include <Core/OrthancException.h>
-
-#include <vector>
-#include <stdio.h>
-#include <boost/thread/mutex.hpp>
-
-namespace Deprecated
-{
-  class Oracle::PImpl
-  {
-  private:
-    enum State
-    {
-      State_Init,
-      State_Started,
-      State_Stopped
-    };
-
-    boost::mutex                   oracleMutex_;
-    State                          state_;
-    std::vector<boost::thread*>    threads_;
-    Orthanc::SharedMessageQueue    queue_;
-
-    static void Worker(PImpl* that)
-    {
-      for (;;)
-      {
-        State state;
-        
-        {
-          boost::mutex::scoped_lock lock(that->oracleMutex_);
-          state = that->state_;
-        }
-
-        if (state == State_Stopped)
-        {
-          break;
-        }
-
-        std::unique_ptr<Orthanc::IDynamicObject> item(that->queue_.Dequeue(100));
-        if (item.get() != NULL)
-        {
-          IOracleCommand& command = dynamic_cast<IOracleCommand&>(*item);
-          try
-          {
-            command.Execute();
-          }
-          catch (Orthanc::OrthancException& /*ex*/)
-          {
-            // this is probably a curl error that has been triggered.  We may just ignore it.
-            // The command.success_ will stay at false and this will be handled in the command.Commit
-          }
-
-          // Random sleeping to test
-          //boost::this_thread::sleep(boost::posix_time::milliseconds(50 * (1 + rand() % 10)));
-
-          command.Commit();
-        }
-      }
-    }
-    
-  public:
-    PImpl(unsigned int threadCount) :
-      state_(State_Init),
-      threads_(threadCount)
-    {
-    }
-
-    ~PImpl()
-    {
-      if (state_ == State_Started)
-      {
-        LOG(ERROR) << "You should have manually called Oracle::Stop()";
-        Stop();
-      }
-    }
-
-    Orthanc::SharedMessageQueue& GetQueue()
-    {
-      return queue_;
-    }
-
-    void Submit(IOracleCommand* command)
-    {
-      std::unique_ptr<IOracleCommand> protection(command);
-
-      if (command == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-      }
-      
-      boost::mutex::scoped_lock lock(oracleMutex_);
-
-      switch (state_)
-      {
-        case State_Init:
-        case State_Started:
-          queue_.Enqueue(protection.release());
-          break;
-
-        case State_Stopped:
-          LOG(ERROR) << "Cannot schedule a request to the Oracle after having "
-                     << "called Oracle::Stop()";
-          break;
-
-        default:
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-      
-    }
-
-    void Start()
-    {
-      boost::mutex::scoped_lock lock(oracleMutex_);
-
-      if (state_ != State_Init)
-      {
-        LOG(ERROR) << "Oracle::PImpl::Start: (state_ != State_Init)";
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
-      
-      for (size_t i = 0; i < threads_.size(); i++)
-      {
-        threads_[i] = new boost::thread(Worker, this);
-      }
-
-      state_ = State_Started;
-    }
-
-    void Stop()
-    {
-      {
-        boost::mutex::scoped_lock lock(oracleMutex_);
-
-        if (state_ != State_Started)
-        {
-          LOG(ERROR) << "Oracle::PImpl::Stop(): (state_ != State_Started)";
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-        }
-
-        state_ = State_Stopped;
-      }
-      
-      for (size_t i = 0; i < threads_.size(); i++)
-      {
-        if (threads_[i] != NULL)
-        {
-          if (threads_[i]->joinable())
-          {
-            threads_[i]->join();
-          }
-
-          delete threads_[i];
-        }
-      }
-    }
-  };
-  
-
-  Oracle::Oracle(unsigned int threadCount) :
-    pimpl_(new PImpl(threadCount))
-  {
-  }
-
-  void Oracle::Start()
-  {
-    pimpl_->Start();
-  }
-
-
-  void Oracle::Submit(IOracleCommand* command)
-  {
-    pimpl_->Submit(command);
-  }
-     
-   
-  void Oracle::Stop()
-  {
-    pimpl_->Stop();
-  }
-
-
-  void Oracle::WaitEmpty()
-  {
-    pimpl_->GetQueue().WaitEmpty(50);
-  }
-}
--- a/Platforms/Generic/Oracle.h	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "IOracleCommand.h"
-
-#include <boost/shared_ptr.hpp>
-
-namespace Deprecated
-{
-  class Oracle : public boost::noncopyable
-  {
-  private:
-    class PImpl;
-  
-    boost::shared_ptr<PImpl>  pimpl_;
-
-  public:
-    Oracle(unsigned int threadCount);
-
-    void Start();
-
-    void Submit(IOracleCommand* command);
-        
-    void WaitEmpty();  // For unit tests
-
-    void Stop();
-  };
-}
--- a/Platforms/Generic/OracleDelayedCallExecutor.h	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h"
-#include "Oracle.h"
-#include "../../Applications/Generic/NativeStoneApplicationContext.h"
-#include "DelayedCallCommand.h"
-
-namespace Deprecated
-{
-  // The OracleTimeout executes callbacks after a delay.
-  class OracleDelayedCallExecutor : public IDelayedCallExecutor
-  {
-  private:
-    Oracle&                        oracle_;
-    OrthancStone::NativeStoneApplicationContext& context_;
-
-  public:
-    OracleDelayedCallExecutor(Oracle& oracle,
-                              OrthancStone::NativeStoneApplicationContext& context) :
-      oracle_(oracle),
-      context_(context)
-    {
-    }
-
-    virtual void Schedule(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
-                          unsigned int timeoutInMs = 1000)
-    {
-      oracle_.Submit(new DelayedCallCommand(callback, timeoutInMs, NULL, context_));
-    }
-  };
-}
--- a/Platforms/Generic/OracleWebService.cpp	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "OracleWebService.h"
-#include "../../Framework/Deprecated/Toolbox/IWebService.h"
-
-namespace Deprecated
-{
-
-
-  class OracleWebService::WebServiceCachedGetCommand : public IOracleCommand, OrthancStone::IObservable
-  {
-  protected:
-    std::unique_ptr<MessageHandler<IWebService::HttpRequestSuccessMessage> >  successCallback_;
-    std::unique_ptr<Orthanc::IDynamicObject>                                  payload_;
-    boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage>      cachedMessage_;
-    OrthancStone::NativeStoneApplicationContext&                                          context_;
-
-  public:
-    WebServiceCachedGetCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                               boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage,
-                               Orthanc::IDynamicObject* payload /* takes ownership */,
-                               OrthancStone::NativeStoneApplicationContext& context
-                               ) :
-      successCallback_(successCallback),
-      payload_(payload),
-      cachedMessage_(cachedMessage),
-      context_(context)
-    {
-    }
-
-    virtual void Execute()
-    {
-      // nothing to do, everything is in the commit
-    }
-
-    virtual void Commit()
-    {
-      // We want to make sure that, i.e, the UpdateThread is not
-      // triggered while we are updating the "model" with the result of
-      // a WebServiceCommand
-      OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_);
-
-      IWebService::HttpRequestSuccessMessage successMessage(cachedMessage_->GetUri(),
-                                                            cachedMessage_->GetAnswer(),
-                                                            cachedMessage_->GetAnswerSize(),
-                                                            cachedMessage_->GetAnswerHttpHeaders(),
-                                                            payload_.get());
-
-      successCallback_->Apply(successMessage);
-    }
-  };
-
-  void OracleWebService::NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage,
-                                                Orthanc::IDynamicObject* payload, // takes ownership
-                                                MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback)
-  {
-    oracle_.Submit(new WebServiceCachedGetCommand(successCallback, cachedMessage, payload, context_));
-  }
-
-
-}
--- a/Platforms/Generic/OracleWebService.h	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../../Framework/Deprecated/Toolbox/BaseWebService.h"
-#include "Oracle.h"
-#include "WebServiceGetCommand.h"
-#include "WebServicePostCommand.h"
-#include "WebServiceDeleteCommand.h"
-#include "../../Applications/Generic/NativeStoneApplicationContext.h"
-
-namespace Deprecated
-{
-  // The OracleWebService performs HTTP requests in a native environment.
-  // It uses a thread pool to handle multiple HTTP requests in a same time.
-  // It works asynchronously to mimick the behaviour of the WebService running in a WASM environment.
-  class OracleWebService : public BaseWebService
-  {
-  private:
-    Oracle&                        oracle_;
-    OrthancStone::NativeStoneApplicationContext& context_;
-    Orthanc::WebServiceParameters  parameters_;
-
-    class WebServiceCachedGetCommand;
-
-  public:
-    OracleWebService(Oracle& oracle,
-                     const Orthanc::WebServiceParameters& parameters,
-                     OrthancStone::NativeStoneApplicationContext& context) :
-      oracle_(oracle),
-      context_(context),
-      parameters_(parameters)
-    {
-    }
-
-    virtual void PostAsync(const std::string& uri,
-                           const HttpHeaders& headers,
-                           const std::string& body,
-                           Orthanc::IDynamicObject* payload, // takes ownership
-                           MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership
-                           MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL, // takes ownership
-                           unsigned int timeoutInSeconds = 60)
-    {
-      oracle_.Submit(new WebServicePostCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, body, payload, context_));
-    }
-
-    virtual void DeleteAsync(const std::string& uri,
-                             const HttpHeaders& headers,
-                             Orthanc::IDynamicObject* payload,
-                             MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
-                             MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
-                             unsigned int timeoutInSeconds = 60)
-    {
-      oracle_.Submit(new WebServiceDeleteCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_));
-    }
-
-  protected:
-    virtual void GetAsyncInternal(const std::string& uri,
-                                  const HttpHeaders& headers,
-                                  Orthanc::IDynamicObject* payload, // takes ownership
-                                  MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,   // takes ownership
-                                  MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,// takes ownership
-                                  unsigned int timeoutInSeconds = 60)
-    {
-      oracle_.Submit(new WebServiceGetCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_));
-    }
-
-    virtual void NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedHttpMessage,
-                                        Orthanc::IDynamicObject* payload, // takes ownership
-                                        MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback);
-
-  };
-}
--- a/Platforms/Generic/WebServiceCommandBase.cpp	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "WebServiceCommandBase.h"
-
-#include <Core/HttpClient.h>
-
-namespace Deprecated
-{
-  WebServiceCommandBase::WebServiceCommandBase(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
-                                               MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
-                                               const Orthanc::WebServiceParameters& parameters,
-                                               const std::string& url,
-                                               const IWebService::HttpHeaders& headers,
-                                               unsigned int timeoutInSeconds,
-                                               Orthanc::IDynamicObject* payload /* takes ownership */,
-                                               OrthancStone::NativeStoneApplicationContext& context) :
-    successCallback_(successCallback),
-    failureCallback_(failureCallback),
-    parameters_(parameters),
-    url_(url),
-    headers_(headers),
-    payload_(payload),
-    success_(false),
-    httpStatus_(Orthanc::HttpStatus_None),
-    context_(context),
-    timeoutInSeconds_(timeoutInSeconds)
-  {
-  }
-
-
-  void WebServiceCommandBase::Commit()
-  {
-    // We want to make sure that, i.e, the UpdateThread is not
-    // triggered while we are updating the "model" with the result of
-    // a WebServiceCommand
-    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); 
-
-    if (success_ && successCallback_.get() != NULL)
-    {
-      IWebService::HttpRequestSuccessMessage message
-        (url_, answer_.c_str(), answer_.size(), answerHeaders_, payload_.get());
-      successCallback_->Apply(message);
-    }
-    else if (!success_ && failureCallback_.get() != NULL)
-    {
-      IWebService::HttpRequestErrorMessage message(url_, httpStatus_, payload_.get());
-      failureCallback_->Apply(message);
-    }
-  }
-}
--- a/Platforms/Generic/WebServiceCommandBase.h	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "IOracleCommand.h"
-
-#include "../../Framework/Deprecated/Toolbox/IWebService.h"
-#include "../../Framework/Messages/IObservable.h"
-#include "../../Framework/Messages/ICallable.h"
-#include "../../Applications/Generic/NativeStoneApplicationContext.h"
-
-#include <Core/WebServiceParameters.h>
-
-#include <memory>
-
-namespace Deprecated
-{
-  class WebServiceCommandBase : public IOracleCommand, OrthancStone::IObservable
-  {
-  protected:
-    std::unique_ptr<MessageHandler<IWebService::HttpRequestSuccessMessage> >  successCallback_;
-    std::unique_ptr<MessageHandler<IWebService::HttpRequestErrorMessage> >    failureCallback_;
-    Orthanc::WebServiceParameters           parameters_;
-    std::string                             url_;
-    IWebService::HttpHeaders                headers_;
-    std::unique_ptr<Orthanc::IDynamicObject>  payload_;
-    bool                                    success_;
-    Orthanc::HttpStatus                     httpStatus_;
-    std::string                             answer_;
-    IWebService::HttpHeaders                answerHeaders_;
-    OrthancStone::NativeStoneApplicationContext&          context_;
-    unsigned int                            timeoutInSeconds_;
-
-  public:
-    WebServiceCommandBase(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
-                          const Orthanc::WebServiceParameters& parameters,
-                          const std::string& url,
-                          const IWebService::HttpHeaders& headers,
-                          unsigned int timeoutInSeconds,
-                          Orthanc::IDynamicObject* payload /* takes ownership */,
-                          OrthancStone::NativeStoneApplicationContext& context
-                          );
-
-    virtual void Execute() = 0;
-
-    virtual void Commit();
-  };
-
-}
--- a/Platforms/Generic/WebServiceDeleteCommand.cpp	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "WebServiceDeleteCommand.h"
-
-#include <Core/HttpClient.h>
-
-namespace Deprecated
-{
-  WebServiceDeleteCommand::WebServiceDeleteCommand(MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                                                   MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
-                                                   const Orthanc::WebServiceParameters& parameters,
-                                                   const std::string& url,
-                                                   const Deprecated::IWebService::HttpHeaders& headers,
-                                                   unsigned int timeoutInSeconds,
-                                                   Orthanc::IDynamicObject* payload /* takes ownership */,
-                                                   OrthancStone::NativeStoneApplicationContext& context) :
-    WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context)
-  {
-  }
-
-  void WebServiceDeleteCommand::Execute()
-  {
-    Orthanc::HttpClient client(parameters_, "/");
-    client.SetUrl(url_);
-    client.SetTimeout(timeoutInSeconds_);
-    client.SetMethod(Orthanc::HttpMethod_Delete);
-
-    for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
-    {
-      client.AddHeader(it->first, it->second);
-    }
-
-    success_ = client.Apply(answer_, answerHeaders_);
-    httpStatus_ = client.GetLastStatus();
-  }
-
-}
--- a/Platforms/Generic/WebServiceDeleteCommand.h	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "WebServiceCommandBase.h"
-
-namespace Deprecated
-{
-  class WebServiceDeleteCommand : public WebServiceCommandBase
-  {
-  public:
-    WebServiceDeleteCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                            MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
-                            const Orthanc::WebServiceParameters& parameters,
-                            const std::string& url,
-                            const IWebService::HttpHeaders& headers,
-                            unsigned int timeoutInSeconds,
-                            Orthanc::IDynamicObject* payload /* takes ownership */,
-                            OrthancStone::NativeStoneApplicationContext& context);
-
-    virtual void Execute();
-  };
-}
--- a/Platforms/Generic/WebServiceGetCommand.cpp	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "WebServiceGetCommand.h"
-
-#include <Core/HttpClient.h>
-
-namespace Deprecated
-{
-  WebServiceGetCommand::WebServiceGetCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                                             MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
-                                             const Orthanc::WebServiceParameters& parameters,
-                                             const std::string& url,
-                                             const IWebService::HttpHeaders& headers,
-                                             unsigned int timeoutInSeconds,
-                                             Orthanc::IDynamicObject* payload /* takes ownership */,
-                                             OrthancStone::NativeStoneApplicationContext& context) :
-    WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context)
-  {
-  }
-
-
-  void WebServiceGetCommand::Execute()
-  {
-    Orthanc::HttpClient client(parameters_, "/");
-    client.SetUrl(url_);
-    client.SetTimeout(timeoutInSeconds_);
-    client.SetMethod(Orthanc::HttpMethod_Get);
-
-    for (IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
-    {
-      client.AddHeader(it->first, it->second);
-    }
-
-    success_ = client.Apply(answer_, answerHeaders_);
-    httpStatus_ = client.GetLastStatus();
-  }
-
-}
--- a/Platforms/Generic/WebServiceGetCommand.h	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "WebServiceCommandBase.h"
-
-namespace Deprecated
-{
-  class WebServiceGetCommand : public WebServiceCommandBase
-  {
-  public:
-    WebServiceGetCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                         MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
-                         const Orthanc::WebServiceParameters& parameters,
-                         const std::string& url,
-                         const IWebService::HttpHeaders& headers,
-                         unsigned int timeoutInSeconds,
-                         Orthanc::IDynamicObject* payload /* takes ownership */,
-                         OrthancStone::NativeStoneApplicationContext& context);
-
-    virtual void Execute();
-  };
-
-}
--- a/Platforms/Generic/WebServicePostCommand.cpp	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "WebServicePostCommand.h"
-
-#include <Core/HttpClient.h>
-
-namespace Deprecated
-{
-  WebServicePostCommand::WebServicePostCommand(MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                                               MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
-                                               const Orthanc::WebServiceParameters& parameters,
-                                               const std::string& url,
-                                               const Deprecated::IWebService::HttpHeaders& headers,
-                                               unsigned int timeoutInSeconds,
-                                               const std::string& body,
-                                               Orthanc::IDynamicObject* payload /* takes ownership */,
-                                               OrthancStone::NativeStoneApplicationContext& context) :
-    WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context),
-    body_(body)
-  {
-  }
-
-  void WebServicePostCommand::Execute()
-  {
-    Orthanc::HttpClient client(parameters_, "/");
-    client.SetUrl(url_);
-    client.SetTimeout(timeoutInSeconds_);
-    client.SetMethod(Orthanc::HttpMethod_Post);
-    client.GetBody().swap(body_);
-
-    for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
-    {
-      client.AddHeader(it->first, it->second);
-    }
-
-    success_ = client.Apply(answer_, answerHeaders_);
-    httpStatus_ = client.GetLastStatus();
-  }
-}
--- a/Platforms/Generic/WebServicePostCommand.h	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "WebServiceCommandBase.h"
-
-namespace Deprecated
-{
-  class WebServicePostCommand : public WebServiceCommandBase
-  {
-  protected:
-    std::string  body_;
-
-  public:
-    WebServicePostCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
-                          const Orthanc::WebServiceParameters& parameters,
-                          const std::string& url,
-                          const IWebService::HttpHeaders& headers,
-                          unsigned int timeoutInSeconds,
-                          const std::string& body,
-                          Orthanc::IDynamicObject* payload /* takes ownership */,
-                          OrthancStone::NativeStoneApplicationContext& context);
-
-    virtual void Execute();
-  };
-}
--- a/Platforms/Wasm/Defaults.cpp	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,416 +0,0 @@
-#include "Defaults.h"
-
-#include "WasmWebService.h"
-#include "WasmDelayedCallExecutor.h"
-#include "../../Framework/Deprecated/Widgets/TestCairoWidget.h"
-#include <Framework/Deprecated/Viewport/WidgetViewport.h>
-#include <Applications/Wasm/StartupParametersBuilder.h>
-#include <Platforms/Wasm/WasmPlatformApplicationAdapter.h>
-#include <Framework/StoneInitialization.h>
-#include <Core/Logging.h>
-#include <sstream>
-
-#include <algorithm>
-
-
-static unsigned int width_ = 0;
-static unsigned int height_ = 0;
-
-/**********************************/
-
-static std::unique_ptr<OrthancStone::IStoneApplication> application;
-static std::unique_ptr<OrthancStone::WasmPlatformApplicationAdapter> applicationWasmAdapter = NULL;
-static std::unique_ptr<OrthancStone::StoneApplicationContext> context;
-static OrthancStone::StartupParametersBuilder startupParametersBuilder;
-static OrthancStone::MessageBroker broker;
-
-static OrthancStone::ViewportContentChangedObserver viewportContentChangedObserver_(broker);
-static OrthancStone::StatusBar statusBar_;
-
-static std::list<std::shared_ptr<Deprecated::WidgetViewport>> viewports_;
-
-std::shared_ptr<Deprecated::WidgetViewport> FindViewportSharedPtr(ViewportHandle viewport) {
-  for (const auto& v : viewports_) {
-    if (v.get() == viewport) {
-      return v;
-    }
-  }
-  assert(false);
-  return std::shared_ptr<Deprecated::WidgetViewport>();
-}
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if 0
-  // rewrite malloc/free in order to monitor allocations.  We actually only monitor large allocations (like images ...)
-
-  size_t bigChunksTotalSize = 0;
-  std::map<void*, size_t> allocatedBigChunks;
-
-  extern void* emscripten_builtin_malloc(size_t bytes);
-  extern void emscripten_builtin_free(void* mem);
-
-  void * __attribute__((noinline)) malloc(size_t size)
-  {
-    void *ptr = emscripten_builtin_malloc(size);
-    if (size > 100000)
-    {
-      bigChunksTotalSize += size;
-      printf("++ Allocated %zu bytes, got %p. (%zu MB consumed by big chunks)\n", size, ptr, bigChunksTotalSize/(1024*1024));
-      allocatedBigChunks[ptr] = size;
-    }
-    return ptr;
-  }
-
-  void __attribute__((noinline)) free(void *ptr)
-  {
-    emscripten_builtin_free(ptr);
-
-    std::map<void*, size_t>::iterator it = allocatedBigChunks.find(ptr);
-    if (it != allocatedBigChunks.end())
-    {
-      bigChunksTotalSize -= it->second;
-      printf("--     Freed %zu bytes at %p.   (%zu MB consumed by big chunks)\n", it->second, ptr, bigChunksTotalSize/(1024*1024));
-      allocatedBigChunks.erase(it);
-    }
-  }
-#endif // 0
-
-  using namespace OrthancStone;
-
-  // when WASM needs a C++ viewport
-  ViewportHandle EMSCRIPTEN_KEEPALIVE CreateCppViewport() {
-    
-    std::shared_ptr<Deprecated::WidgetViewport> viewport(new Deprecated::WidgetViewport(broker));
-    printf("viewport %x\n", (int)viewport.get());
-
-    viewports_.push_back(viewport);
-
-    printf("There are now %lu viewports in C++\n", viewports_.size());
-
-    viewport->SetStatusBar(statusBar_);
-
-    viewport->RegisterObserverCallback(
-      new Callable<ViewportContentChangedObserver, Deprecated::IViewport::ViewportChangedMessage>
-      (viewportContentChangedObserver_, &ViewportContentChangedObserver::OnViewportChanged));
-
-    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<Deprecated::WidgetViewport>& v) { return v.get() == viewport;});
-
-    printf("There are now %lu viewports in C++\n", viewports_.size());
-  }
-
-  void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle viewport) {
-    printf("Initializing Stone\n");
-    OrthancStone::StoneInitialize();
-    printf("CreateWasmApplication\n");
-
-    application.reset(CreateUserApplication(broker));
-    applicationWasmAdapter.reset(CreateWasmApplicationAdapter(broker, application.get())); 
-    Deprecated::WasmWebService::SetBroker(broker);
-    Deprecated::WasmDelayedCallExecutor::SetBroker(broker);
-
-    startupParametersBuilder.Clear();
-  }
-
-  void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc,
-                                                  const char* value) {
-    startupParametersBuilder.SetStartupParameter(keyc, value);
-  }
-
-  void EMSCRIPTEN_KEEPALIVE StartWasmApplication(const char* baseUri) {
-
-    printf("StartWasmApplication\n");
-
-    Orthanc::Logging::SetErrorWarnInfoTraceLoggingFunctions(
-      stone_console_error, stone_console_warning,
-      stone_console_info, stone_console_trace);
-
-    // recreate a command line from uri arguments and parse it
-    boost::program_options::variables_map parameters;
-    boost::program_options::options_description options;
-    application->DeclareStartupOptions(options);
-    startupParametersBuilder.GetStartupParameters(parameters, options);
-
-    context.reset(new OrthancStone::StoneApplicationContext(broker));
-    context->SetOrthancBaseUrl(baseUri);
-    printf("Base URL to Orthanc API: [%s]\n", baseUri);
-    context->SetWebService(Deprecated::WasmWebService::GetInstance());
-    context->SetDelayedCallExecutor(Deprecated::WasmDelayedCallExecutor::GetInstance());
-    application->Initialize(context.get(), statusBar_, parameters);
-    application->InitializeWasm();
-
-//    viewport->SetSize(width_, height_);
-    printf("StartWasmApplication - completed\n");
-  }
-  
-  bool EMSCRIPTEN_KEEPALIVE WasmIsTraceLevelEnabled()
-  {
-    return Orthanc::Logging::IsTraceLevelEnabled();
-  }
-
-  bool EMSCRIPTEN_KEEPALIVE WasmIsInfoLevelEnabled()
-  {
-    return Orthanc::Logging::IsInfoLevelEnabled();
-  }
-  
-  void EMSCRIPTEN_KEEPALIVE WasmDoAnimation()
-  {
-    for (auto viewport : viewports_) {
-      // TODO Only launch the JavaScript timer if "HasAnimation()"
-      if (viewport->HasAnimation())
-      {
-        viewport->DoAnimation();
-      }
-
-    }
-
-  }
-  
-
-  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)
-  {
-    viewportContentChangedObserver_.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, std::vector<Deprecated::Touch>());
-  }
-  
-
-  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, std::vector<Deprecated::Touch>());
-  }
-
-  void GetTouchVector(std::vector<Deprecated::Touch>& output,
-                      int touchCount,
-                      float x0,
-                      float y0,
-                      float x1,
-                      float y1,
-                      float x2,
-                      float y2)
-  {
-    // TODO: it might be nice to try to pass all the x0,y0 coordinates as arrays but that's not so easy to pass array between JS and C++
-    if (touchCount > 0)
-    {
-      output.push_back(Deprecated::Touch(x0, y0));
-    }
-    if (touchCount > 1)
-    {
-      output.push_back(Deprecated::Touch(x1, y1));
-    }
-    if (touchCount > 2)
-    {
-      output.push_back(Deprecated::Touch(x2, y2));
-    }
-
-  }
-
-  void EMSCRIPTEN_KEEPALIVE ViewportTouchStart(ViewportHandle viewport,
-                                              int touchCount,
-                                              float x0,
-                                              float y0,
-                                              float x1,
-                                              float y1,
-                                              float x2,
-                                              float y2)
-  {
-    // printf("touch start with %d touches\n", touchCount);
-
-    std::vector<Deprecated::Touch> touches;
-    GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2);
-    viewport->TouchStart(touches);
-  }
-
-  void EMSCRIPTEN_KEEPALIVE ViewportTouchMove(ViewportHandle viewport,
-                                              int touchCount,
-                                              float x0,
-                                              float y0,
-                                              float x1,
-                                              float y1,
-                                              float x2,
-                                              float y2)
-  {
-    // printf("touch move with %d touches\n", touchCount);
-
-    std::vector<Deprecated::Touch> touches;
-    GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2);
-    viewport->TouchMove(touches);
-  }
-
-  void EMSCRIPTEN_KEEPALIVE ViewportTouchEnd(ViewportHandle viewport,
-                                              int touchCount,
-                                              float x0,
-                                              float y0,
-                                              float x1,
-                                              float y1,
-                                              float x2,
-                                              float y2)
-  {
-    // printf("touch end with %d touches remaining\n", touchCount);
-
-    std::vector<Deprecated::Touch> touches;
-    GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2);
-    viewport->TouchEnd(touches);
-  }
-
-  void EMSCRIPTEN_KEEPALIVE ViewportKeyPressed(ViewportHandle viewport,
-                                               int key,
-                                               const char* keyChar, 
-                                               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);
-    }
-
-    char c = 0;
-    if (keyChar != NULL && key == OrthancStone::KeyboardKeys_Generic) {
-      c = keyChar[0];
-    }
-    viewport->KeyPressed(static_cast<OrthancStone::KeyboardKeys>(key), c, 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();
-  }
-
-  const char* EMSCRIPTEN_KEEPALIVE SendSerializedMessageToStoneApplication(const char* message) 
-  {
-    static std::string output; // we don't want the string to be deallocated when we return to JS code so we always use the same string (this is fine since JS is single-thread)
-
-    //printf("SendSerializedMessageToStoneApplication\n");
-    //printf("%s", message);
-
-    if (applicationWasmAdapter.get() != NULL) {
-      applicationWasmAdapter->HandleSerializedMessageFromWeb(output, std::string(message));
-      return output.c_str();
-    }
-    printf("This Stone application does not have a Web Adapter, unable to send messages");
-    return NULL;
-  }
-
-#ifdef __cplusplus
-}
-#endif
--- a/Platforms/Wasm/Defaults.h	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-#pragma once
-
-#include <emscripten/emscripten.h>
-
-#include "../../Framework/Deprecated/Viewport/WidgetViewport.h"
-#include "../../Framework/Deprecated/Widgets/LayoutWidget.h"
-#include <Applications/IStoneApplication.h>
-#include <Platforms/Wasm/WasmPlatformApplicationAdapter.h>
-
-typedef Deprecated::WidgetViewport* ViewportHandle; // the objects exchanged between JS and C++
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-  
-  // JS methods accessible from C++
-  extern void ScheduleWebViewportRedrawFromCpp(ViewportHandle cppViewportHandle);
-  extern void UpdateStoneApplicationStatusFromCppWithString(const char* statusUpdateMessage);
-  extern void UpdateStoneApplicationStatusFromCppWithSerializedMessage(const char* statusUpdateMessage);
-  extern void stone_console_error(const char*);
-  extern void stone_console_warning(const char*);
-  extern void stone_console_info(const char*);
-  extern void stone_console_trace(const char*);
-
-  // C++ methods accessible from JS
-  extern void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle cppViewportHandle);
-  extern void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc, const char* value);
-  
-
-#ifdef __cplusplus
-}
-#endif
-
-// these methods must be implemented in the custom app "mainWasm.cpp"
-extern OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker);
-extern OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, OrthancStone::IStoneApplication* application);
-
-namespace OrthancStone {
-
-  // default Observer to trigger Viewport redraw when something changes in the Viewport
-  class ViewportContentChangedObserver : public IObserver
-  {
-  private:
-    // Flag to avoid flooding JavaScript with redundant Redraw requests
-    bool isScheduled_; 
-
-  public:
-    ViewportContentChangedObserver(MessageBroker& broker) :
-      IObserver(broker),
-      isScheduled_(false)
-    {
-    }
-
-    void Reset()
-    {
-      isScheduled_ = false;
-    }
-
-    void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message)
-    {
-      if (!isScheduled_)
-      {
-        ScheduleWebViewportRedrawFromCpp((ViewportHandle)&message.GetOrigin());  // loosing constness when transmitted to Web
-        isScheduled_ = true;
-      }
-    }
-  };
-
-  // default status bar to log messages on the console/stdout
-  class StatusBar : public Deprecated::IStatusBar
-  {
-  public:
-    virtual void ClearMessage()
-    {
-    }
-
-    virtual void SetMessage(const std::string& message)
-    {
-      printf("%s\n", message.c_str());
-    }
-  };
-}
--- a/Platforms/Wasm/WasmDelayedCallExecutor.cpp	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-#include "WasmDelayedCallExecutor.h"
-#include "json/value.h"
-#include "json/writer.h"
-#include <emscripten/emscripten.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  extern void WasmDelayedCallExecutor_Schedule(void* callable,
-                                      unsigned int timeoutInMs
-                                      /*void* payload*/);
-
-  void EMSCRIPTEN_KEEPALIVE WasmDelayedCallExecutor_ExecuteCallback(void* callable
-                                                       //void* payload
-                                                       )
-  {
-    if (callable == NULL)
-    {
-      throw;
-    }
-    else
-    {
-      reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IDelayedCallExecutor::TimeoutMessage>*>(callable)->
-        Apply(Deprecated::IDelayedCallExecutor::TimeoutMessage()); // uri, reinterpret_cast<Orthanc::IDynamicObject*>(payload)));
-    }
-  }
-
-
-#ifdef __cplusplus
-}
-#endif
-
-
-
-namespace Deprecated
-{
-  OrthancStone::MessageBroker* WasmDelayedCallExecutor::broker_ = NULL;
-
-
-  void WasmDelayedCallExecutor::Schedule(OrthancStone::MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
-                         unsigned int timeoutInMs)
-  {
-    WasmDelayedCallExecutor_Schedule(callback, timeoutInMs);
-  }
-}
--- a/Platforms/Wasm/WasmDelayedCallExecutor.h	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-#pragma once
-
-#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h"
-#include <Core/OrthancException.h>
-
-namespace Deprecated
-{
-  class WasmDelayedCallExecutor : public IDelayedCallExecutor
-  {
-  private:
-    static OrthancStone::MessageBroker* broker_;
-
-    // Private constructor => Singleton design pattern
-    WasmDelayedCallExecutor(OrthancStone::MessageBroker& broker) :
-      IDelayedCallExecutor(broker)
-    {
-    }
-
-  public:
-    static WasmDelayedCallExecutor& GetInstance()
-    {
-      if (broker_ == NULL)
-      {
-        printf("WasmDelayedCallExecutor::GetInstance(): broker not initialized\n");
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
-      static WasmDelayedCallExecutor instance(*broker_);
-      return instance;
-    }
-
-    static void SetBroker(OrthancStone::MessageBroker& broker)
-    {
-      broker_ = &broker;
-    }
-
-    virtual void Schedule(OrthancStone::MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
-                         unsigned int timeoutInMs = 1000);
-
-  };
-}
--- a/Platforms/Wasm/WasmDelayedCallExecutor.js	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-mergeInto(LibraryManager.library, {
-  WasmDelayedCallExecutor_Schedule: function(callable, timeoutInMs/*, payload*/) {
-    setTimeout(function() {
-      window.WasmDelayedCallExecutor_ExecuteCallback(callable/*, payload*/);
-    }, timeoutInMs);
-  }
-});
--- a/Platforms/Wasm/WasmPlatformApplicationAdapter.cpp	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-#include "WasmPlatformApplicationAdapter.h"
-
-#include "Framework/StoneException.h"
-#include <stdio.h>
-#include "Platforms/Wasm/Defaults.h"
-
-namespace OrthancStone
-{
-  WasmPlatformApplicationAdapter::WasmPlatformApplicationAdapter(MessageBroker& broker, IStoneApplication& application)
-  : IObserver(broker),
-    application_(application)
-  {
-  }
-
-  void WasmPlatformApplicationAdapter::HandleSerializedMessageFromWeb(std::string& output, const std::string& input)
-  {
-    try
-    {
-      application_.HandleSerializedMessage(input.c_str());
-    }
-    catch (StoneException& exc)
-    {
-      printf("Error while handling message from web (error code = %d):\n", exc.GetErrorCode());
-      printf("While interpreting input: '%s'\n", input.c_str());
-      output = std::string("ERROR : ");
-    }
-    catch (std::exception& exc)
-    {
-      printf("Error while handling message from web (error text = %s):\n", exc.what());
-      printf("While interpreting input: '%s'\n", input.c_str());
-      output = std::string("ERROR : ");
-    }
-  }
-
-  void WasmPlatformApplicationAdapter::NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage)
-  {
-    try
-    {
-      UpdateStoneApplicationStatusFromCppWithString(statusUpdateMessage.c_str());
-    }
-    catch (...)
-    {
-      printf("Error while handling string message to web\n");
-    }
-  }
-
-  void WasmPlatformApplicationAdapter::NotifyStatusUpdateFromCppToWebWithSerializedMessage(const std::string& statusUpdateMessage)
-  {
-    try
-    {
-      UpdateStoneApplicationStatusFromCppWithSerializedMessage(statusUpdateMessage.c_str());
-    }
-    catch (...)
-    {
-      printf("Error while handling serialized message to web\n");
-    }
-  }
-
-}
--- a/Platforms/Wasm/WasmPlatformApplicationAdapter.h	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-#pragma once
-
-#include <string>
-#include <Framework/Messages/IObserver.h>
-#include <Applications/IStoneApplication.h>
-
-namespace OrthancStone
-{
-  class WasmPlatformApplicationAdapter : public IObserver
-  {
-      IStoneApplication&  application_;
-    public:
-      WasmPlatformApplicationAdapter(MessageBroker& broker, IStoneApplication& application);
-
-      virtual void HandleSerializedMessageFromWeb(std::string& output, const std::string& input);
-      virtual void NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage);
-      virtual void NotifyStatusUpdateFromCppToWebWithSerializedMessage(const std::string& statusUpdateMessage);
-  };
-}
\ No newline at end of file
--- a/Platforms/Wasm/WasmViewport.cpp	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-#include "WasmViewport.h"
-
-#include <vector>
-#include <memory>
-
-std::vector<std::shared_ptr<Deprecated::WidgetViewport>> wasmViewports;
-
-void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget) {
-    std::shared_ptr<Deprecated::WidgetViewport> viewport(CreateWasmViewportFromCpp(htmlCanvasId));
-    viewport->SetCentralWidget(centralWidget);
-
-    wasmViewports.push_back(viewport);
-}
--- a/Platforms/Wasm/WasmViewport.h	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-#pragma once
-
-#include "../../Framework/Deprecated/Viewport/WidgetViewport.h"
-
-#include <emscripten/emscripten.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  // JS methods accessible from C++
-  extern Deprecated::WidgetViewport* CreateWasmViewportFromCpp(const char* htmlCanvasId);
-
-#ifdef __cplusplus
-}
-#endif
-
-extern void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget);
--- a/Platforms/Wasm/WasmWebService.cpp	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,168 +0,0 @@
-#include "WasmWebService.h"
-#include "json/value.h"
-#include "json/writer.h"
-#include <emscripten/emscripten.h>
-#include <boost/shared_ptr.hpp>
-
-struct CachedSuccessNotification
-{
-  boost::shared_ptr<Deprecated::BaseWebService::CachedHttpRequestSuccessMessage>    cachedMessage;
-  std::unique_ptr<Orthanc::IDynamicObject>                                              payload;
-  OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback;
-};
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  extern void WasmWebService_GetAsync(void* callableSuccess,
-                                      void* callableFailure,
-                                      const char* uri,
-                                      const char* headersInJsonString,
-                                      void* payload,
-                                      unsigned int timeoutInSeconds);
-
-  extern void WasmWebService_ScheduleLaterCachedSuccessNotification(void* brol);
-
-  extern void WasmWebService_PostAsync(void* callableSuccess,
-                                       void* callableFailure,
-                                       const char* uri,
-                                       const char* headersInJsonString,
-                                       const void* body,
-                                       size_t bodySize,
-                                       void* payload,
-                                       unsigned int timeoutInSeconds);
-
-  extern void WasmWebService_DeleteAsync(void* callableSuccess,
-                                         void* callableFailure,
-                                         const char* uri,
-                                         const char* headersInJsonString,
-                                         void* payload,
-                                         unsigned int timeoutInSeconds);
-
-  void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyError(void* failureCallable,
-                                                       const char* uri,
-                                                       unsigned int httpStatus,
-                                                       void* payload)
-  {
-    if (failureCallable != NULL)
-    {
-      reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>*>(failureCallable)->
-        Apply(Deprecated::IWebService::HttpRequestErrorMessage(uri, static_cast<Orthanc::HttpStatus>(httpStatus), reinterpret_cast<Orthanc::IDynamicObject*>(payload)));
-    }
-  }
-
-  void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyCachedSuccess(void* notification_)
-  {
-    // notification has been allocated in C++ and passed to JS.  It must be deleted by this method
-    std::unique_ptr<CachedSuccessNotification> notification(reinterpret_cast<CachedSuccessNotification*>(notification_));
-
-    notification->successCallback->Apply(Deprecated::IWebService::HttpRequestSuccessMessage(
-      notification->cachedMessage->GetUri(), 
-      notification->cachedMessage->GetAnswer(),
-      notification->cachedMessage->GetAnswerSize(),
-      notification->cachedMessage->GetAnswerHttpHeaders(),
-      notification->payload.get()
-      ));
-  }
-
-  void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifySuccess(void* successCallable,
-                                                         const char* uri,
-                                                         const void* body,
-                                                         size_t bodySize,
-                                                         const char* answerHeaders,
-                                                         void* payload)
-  {
-    if (successCallable != NULL)
-    {
-      Deprecated::IWebService::HttpHeaders headers;
-
-      // TODO - Parse "answerHeaders"
-      //printf("TODO: parse headers [%s]\n", answerHeaders);
-      
-      reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>*>(successCallable)->
-        Apply(Deprecated::IWebService::HttpRequestSuccessMessage(uri, body, bodySize, headers,
-                                                                   reinterpret_cast<Orthanc::IDynamicObject*>(payload)));
-    }
-  }
-
-#ifdef __cplusplus
-}
-#endif
-
-
-
-namespace Deprecated
-{
-  OrthancStone::MessageBroker* WasmWebService::broker_ = NULL;
-
-  void ToJsonString(std::string& output, const IWebService::HttpHeaders& headers)
-  {
-    Json::Value jsonHeaders;
-    for (IWebService::HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); it++ )
-    {
-      jsonHeaders[it->first] = it->second;
-    }
-
-    Json::StreamWriterBuilder builder;
-    std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
-    std::ostringstream outputStr;
-
-    writer->write(jsonHeaders, &outputStr);
-    output = outputStr.str();
-  }
-
-  void WasmWebService::PostAsync(const std::string& relativeUri,
-                                 const HttpHeaders& headers,
-                                 const std::string& body,
-                                 Orthanc::IDynamicObject* payload,
-                                 OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
-                                 OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable,
-                                 unsigned int timeoutInSeconds)
-  {
-    std::string headersInJsonString;
-    ToJsonString(headersInJsonString, headers);
-    WasmWebService_PostAsync(successCallable, failureCallable, relativeUri.c_str(), headersInJsonString.c_str(),
-                             body.c_str(), body.size(), payload, timeoutInSeconds);
-  }
-
-  void WasmWebService::DeleteAsync(const std::string& relativeUri,
-                                   const HttpHeaders& headers,
-                                   Orthanc::IDynamicObject* payload,
-                                   OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
-                                   OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable,
-                                   unsigned int timeoutInSeconds)
-  {
-    std::string headersInJsonString;
-    ToJsonString(headersInJsonString, headers);
-    WasmWebService_DeleteAsync(successCallable, failureCallable, relativeUri.c_str(), headersInJsonString.c_str(),
-                               payload, timeoutInSeconds);
-  }
-
-  void WasmWebService::GetAsyncInternal(const std::string &relativeUri,
-                                        const HttpHeaders &headers,
-                                        Orthanc::IDynamicObject *payload,
-                                        OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
-                                        OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable,
-                                        unsigned int timeoutInSeconds)
-  {
-    std::string headersInJsonString;
-    ToJsonString(headersInJsonString, headers);
-    WasmWebService_GetAsync(successCallable, failureCallable, relativeUri.c_str(),
-                            headersInJsonString.c_str(), payload, timeoutInSeconds);
-  }
-
-  void WasmWebService::NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage,
-                                              Orthanc::IDynamicObject* payload, // takes ownership
-                                              OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback)
-  {
-    CachedSuccessNotification* notification = new CachedSuccessNotification();  // allocated on the heap, it will be passed to JS and deleted when coming back to C++
-    notification->cachedMessage = cachedMessage;
-    notification->payload.reset(payload);
-    notification->successCallback = successCallback;
-
-    WasmWebService_ScheduleLaterCachedSuccessNotification(notification);
-  }
-
-}
--- a/Platforms/Wasm/WasmWebService.h	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-#pragma once
-
-#include "../../Framework/Deprecated/Toolbox/BaseWebService.h"
-#include <Core/OrthancException.h>
-
-namespace Deprecated
-{
-class WasmWebService : public BaseWebService
-{
-private:
-  static OrthancStone::MessageBroker *broker_;
-
-  // Private constructor => Singleton design pattern
-  WasmWebService(OrthancStone::MessageBroker &broker) : BaseWebService(broker)
-  {
-  }
-
-public:
-  static WasmWebService &GetInstance()
-  {
-    if (broker_ == NULL)
-    {
-      printf("WasmWebService::GetInstance(): broker not initialized\n");
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-    static WasmWebService instance(*broker_);
-    return instance;
-  }
-
-  static void SetBroker(OrthancStone::MessageBroker &broker)
-  {
-    broker_ = &broker;
-  }
-
-  virtual void PostAsync(const std::string &uri,
-                         const HttpHeaders &headers,
-                         const std::string &body,
-                         Orthanc::IDynamicObject *payload,
-                         OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
-                         OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL,
-                         unsigned int timeoutInSeconds = 60);
-
-  virtual void DeleteAsync(const std::string &uri,
-                           const HttpHeaders &headers,
-                           Orthanc::IDynamicObject *payload,
-                           OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
-                           OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL,
-                           unsigned int timeoutInSeconds = 60);
-
-protected:
-  virtual void GetAsyncInternal(const std::string &uri,
-                                const HttpHeaders &headers,
-                                Orthanc::IDynamicObject *payload,
-                                OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
-                                OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL,
-                                unsigned int timeoutInSeconds = 60);
-
-  virtual void NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedHttpMessage,
-                                      Orthanc::IDynamicObject *payload, // takes ownership
-                                      OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallback);
-};
-} // namespace Deprecated
--- a/Platforms/Wasm/WasmWebService.js	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,108 +0,0 @@
-mergeInto(LibraryManager.library, {
-  WasmWebService_GetAsync: function(callableSuccess, callableFailure, url, headersInJsonString, payload, timeoutInSeconds) {
-    // 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 url_ = UTF8ToString(url);
-    var headersInJsonString_ = UTF8ToString(headersInJsonString);
-
-    xhr.open('GET', url_, true);
-    xhr.responseType = 'arraybuffer';
-    xhr.timeout = timeoutInSeconds * 1000;
-    var headers = JSON.parse(headersInJsonString_);
-    for (var key in headers) {
-      xhr.setRequestHeader(key, headers[key]);
-    }
-    //console.log(xhr); 
-    xhr.onreadystatechange = function() {
-      if (this.readyState == XMLHttpRequest.DONE) {
-        if (xhr.status === 200) {
-          var s = xhr.getAllResponseHeaders();
-          var headers = _malloc(s.length + 1);
-          stringToUTF8(s, headers, s.length + 1);
-          
-          // TODO - Is "new Uint8Array()" necessary? This copies the
-          // answer to the WebAssembly stack, hence necessitating
-          // increasing the TOTAL_STACK parameter of Emscripten
-          window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response),
-                                       this.response.byteLength, headers, payload);
-        } else {
-          window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload);
-        }
-      }
-    }
-    
-    xhr.send();
-  },
-
-  WasmWebService_ScheduleLaterCachedSuccessNotification: function (brol) {
-    setTimeout(function() {
-      window.WasmWebService_NotifyCachedSuccess(brol);
-    }, 0);
-  },
-
-  WasmWebService_PostAsync: function(callableSuccess, callableFailure, url, headersInJsonString, body, bodySize, payload, timeoutInSeconds) {
-    var xhr = new XMLHttpRequest();
-    var url_ = UTF8ToString(url);
-    var headersInJsonString_ = UTF8ToString(headersInJsonString);
-    xhr.open('POST', url_, true);
-    xhr.timeout = timeoutInSeconds * 1000;
-    xhr.responseType = 'arraybuffer';
-    xhr.setRequestHeader('Content-type', 'application/octet-stream');
-
-    var headers = JSON.parse(headersInJsonString_);
-    for (var key in headers) {
-      xhr.setRequestHeader(key, headers[key]);
-    }
-    
-    xhr.onreadystatechange = function() {
-      if (this.readyState == XMLHttpRequest.DONE) {
-        if (xhr.status === 200) {
-          var s = xhr.getAllResponseHeaders();
-          var headers = _malloc(s.length + 1);
-          stringToUTF8(s, headers, s.length + 1);
-
-          window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response),
-                                       this.response.byteLength, headers, payload);
-        } else {
-          window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload);
-        }
-      }
-    }
-
-    xhr.send(new Uint8ClampedArray(HEAPU8.buffer, body, bodySize));
-  },
-
-  WasmWebService_DeleteAsync: function(callableSuccess, callableFailure, url, headersInJsonString, payload, timeoutInSeconds) {
-    var xhr = new XMLHttpRequest();
-    var url_ = UTF8ToString(url);
-    var headersInJsonString_ = UTF8ToString(headersInJsonString);
-    xhr.open('DELETE', url_, true);
-    xhr.timeout = timeoutInSeconds * 1000;
-    xhr.responseType = 'arraybuffer';
-    xhr.setRequestHeader('Content-type', 'application/octet-stream');
-  
-    var headers = JSON.parse(headersInJsonString_);
-    for (var key in headers) {
-      xhr.setRequestHeader(key, headers[key]);
-    }
-    
-    xhr.onreadystatechange = function() {
-      if (this.readyState == XMLHttpRequest.DONE) {
-        if (xhr.status === 200) {
-          var s = xhr.getAllResponseHeaders();
-          var headers = _malloc(s.length + 1);
-          stringToUTF8(s, headers, s.length + 1);
-
-          window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response),
-                                       this.response.byteLength, headers, payload);
-        } else {
-          window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload);
-        }
-      }
-    }
-  
-    xhr.send();
-  }
-  
-});
--- a/Platforms/Wasm/default-library.js	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-// this file contains the JS method you want to expose to C++ code
-
-mergeInto(LibraryManager.library, {
-
-  ScheduleWebViewportRedrawFromCpp: function(cppViewportHandle) {
-    window.ScheduleWebViewportRedraw(cppViewportHandle);
-  },
-
-  CreateWasmViewportFromCpp: function(htmlCanvasId) {
-    return window.CreateWasmViewport(htmlCanvasId);
-  },
-
-  // each time the StoneApplication updates its status, it may signal it 
-  // through this method. i.e, to change the status of a button in the web interface
-  UpdateStoneApplicationStatusFromCppWithString: function(statusUpdateMessage) {
-    var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage);
-    window.UpdateWebApplicationWithString(statusUpdateMessage_);
-  },
-
-  // same, but with a serialized message
-  UpdateStoneApplicationStatusFromCppWithSerializedMessage: function(statusUpdateMessage) {
-    var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage);
-    window.UpdateWebApplicationWithSerializedMessage(statusUpdateMessage_);
-  },
-
-  // These functions are called from C++ (through an extern declaration) 
-  // and call the standard logger that, here, routes to the console.
-
-  stone_console_error : function(message) {
-    var text = UTF8ToString(message);
-    window.errorFromCpp(text);
-  },
-
-  stone_console_warning : function(message) {
-    var text = UTF8ToString(message);
-    window.warningFromCpp(text);
-  },
-
-  stone_console_info: function(message) {
-    var text = UTF8ToString(message);
-    window.infoFromCpp(text);
-  },
-  
-  stone_console_trace : function(message) {
-    var text = UTF8ToString(message);
-    window.debugFromCpp(text);
-  }
-
-});
--- a/Platforms/Wasm/logger.ts	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-export enum LogSource {
-  Cpp,
-  Typescript
-}
-
-export class StandardConsoleLogger {
-  public showSource: boolean = true;
-
-  public debug(...args: any[]): void {
-    this._debug(LogSource.Typescript, ...args);
-  }
-
-  public debugFromCpp(...args: any[]): void {
-    this._debug(LogSource.Cpp, ...args);
-  }
-
-  public info(...args: any[]): void {
-    this._info(LogSource.Typescript, ...args);
-  }
-
-  public infoFromCpp(message: string): void {
-    this._info(LogSource.Cpp, message);
-  }
-
-  public warning(...args: any[]): void {
-    this._warning(LogSource.Typescript, ...args);
-  }
-
-  public warningFromCpp(message: string): void {
-    this._warning(LogSource.Cpp, message);
-  }
-
-  public error(...args: any[]): void {
-    this._error(LogSource.Typescript, ...args);
-  }
-
-  public errorFromCpp(message: string): void {
-    this._error(LogSource.Cpp, message);
-  }
-
-  public _debug(source: LogSource, ...args: any[]): void {
-    if ((<any> window).IsTraceLevelEnabled)
-    {
-      if ((<any> window).IsTraceLevelEnabled())
-      {
-        var output = this.getOutput(source, args);
-        console.debug(...output);
-      }
-    }
-  }
-
-  private _info(source: LogSource, ...args: any[]): void {
-    if ((<any> window).IsInfoLevelEnabled)
-    {
-      if ((<any> window).IsInfoLevelEnabled())
-      {
-        var output = this.getOutput(source, args);
-        console.info(...output);
-      }
-    }
-  }
-
-  public _warning(source: LogSource, ...args: any[]): void {
-    var output = this.getOutput(source, args);
-    console.warn(...output);
-  }
-
-  public _error(source: LogSource, ...args: any[]): void {
-    var output = this.getOutput(source, args);
-    console.error(...output);
-  }
-
-
-  private getOutput(source: LogSource, args: any[]): any[] {
-    var prefix = this.getPrefix();
-    var prefixAndSource = Array<string>();
-
-    if (prefix != null) {
-      prefixAndSource = [prefix];
-    } 
-
-    if (this.showSource) {
-      if (source == LogSource.Typescript) {
-        prefixAndSource = [...prefixAndSource, "TS "];
-      } else if (source == LogSource.Cpp) {
-        prefixAndSource = [...prefixAndSource, "C++"];
-      }
-    }
-
-    if (prefixAndSource.length > 0) {
-      prefixAndSource = [...prefixAndSource, "|"];
-    }
-
-    return [...prefixAndSource, ...args];
-  }
-
-  protected getPrefix(): string | null {
-    return null;
-  }
-}
-
-export class TimeConsoleLogger extends StandardConsoleLogger {
-  protected getPrefix(): string {
-    let now = new Date();
-    let timeString = now.getHours().toString().padStart(2, "0") + ":" + now.getMinutes().toString().padStart(2, "0") + ":" + now.getSeconds().toString().padStart(2, "0") + "." + now.getMilliseconds().toString().padStart(3, "0");
-    return timeString;
-  }
-}
-
-export var defaultLogger: StandardConsoleLogger = new TimeConsoleLogger();
-
--- a/Platforms/Wasm/stone-framework-loader.ts	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-/**
- * This file contains primitives to interface with WebAssembly and
- * with the Stone framework.
- **/
-import * as Logger from './logger'
-
-export declare type InitializationCallback = () => void;
-
-//export declare var StoneFrameworkModule : any;
-export var StoneFrameworkModule : any;
-
-//const ASSETS_FOLDER : string = "assets/lib";
-//const WASM_FILENAME : string = "orthanc-framework";
-
-export class Framework
-{
-  private static singleton_ : Framework = null;
-  private static wasmModuleName_ : string = null;
-
-  public static Configure(wasmModuleName: string) {
-    this.wasmModuleName_ = wasmModuleName;
-  }
-
-  private constructor(verbose : boolean) 
-  {
-    //this.ccall('Initialize', null, [ 'number' ], [ verbose ]);
-  }
-
-  
-  public ccall( name: string,
-                returnType: string,
-                argTypes: Array<string>,
-                argValues: Array<any>) : any
-  {
-    return (<any> window).StoneFrameworkModule.ccall(name, returnType, argTypes, argValues);
-  }
-
-  
-  public cwrap( name: string,
-                returnType: string,
-                argTypes: Array<string>) : any
-  {
-    return (<any> window).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)
-  {
-    Logger.defaultLogger.debug('Initializing WebAssembly Module');
-
-    (<any> window).errorFromCpp = function(text:any) { Logger.defaultLogger.errorFromCpp(text); };
-    (<any> window).warningFromCpp = function(text:any) { Logger.defaultLogger.warningFromCpp(text); };
-    (<any> window).infoFromCpp = function(text:any) { Logger.defaultLogger.infoFromCpp(text); };
-    (<any> window).debugFromCpp = function(text:any) { Logger.defaultLogger.debugFromCpp(text); };
-
-    // (<any> window).
-    (<any> window).StoneFrameworkModule = {
-      preRun: [ 
-        function() {
-          Logger.defaultLogger.debug('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
-          Logger.defaultLogger.debug('WebAssembly is ready');
-          Framework.singleton_ = new Framework(verbose);
-          callback();
-        }
-      ],
-      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.src = this.wasmModuleName_ + ".js";//  "OrthancStoneSimpleViewer.js"; // ASSETS_FOLDER + '/' + WASM_FILENAME + '.js';
-    script.async = true;
-    document.head.appendChild(script);
-  }
-}
--- a/Platforms/Wasm/tsconfig-stone.json	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-{
-    "include" : [
-        "stone-framework-loader.ts",
-        "logger.ts",
-        "wasm-application-runner.ts",
-        "wasm-viewport.ts"
-    ]
-}
--- a/Platforms/Wasm/wasm-application-runner.ts	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,141 +0,0 @@
-import * as Stone from './stone-framework-loader'
-import * as StoneViewport from './wasm-viewport'
-import * as Logger from './logger'
-
-if (!('WebAssembly' in window)) {
-  alert('Sorry, your browser does not support WebAssembly :(');
-}
-
-//var StoneFrameworkModule : Stone.Framework = (<any>window).StoneFrameworkModule;
-//export declare var StoneFrameworkModule : Stone.Framework;
-
-// global functions
-var WasmWebService_NotifyError: Function = null;
-var WasmWebService_NotifySuccess: Function = null;
-var WasmWebService_NotifyCachedSuccess: Function = null;
-var WasmDelayedCallExecutor_ExecuteCallback: Function = null;
-var WasmDoAnimation: Function = null;
-var SetStartupParameter: Function = null;
-var CreateWasmApplication: Function = null;
-export var CreateCppViewport: Function = null;
-var ReleaseCppViewport: Function = null;
-var StartWasmApplication: Function = null;
-export var SendSerializedMessageToStoneApplication: Function = null;
-
-var auxiliaryParameters : Map<string,string>  = null;
-
-export function SetApplicationParameters(params : Map<string,string>) {
-  if (auxiliaryParameters != null) {
-    console.warn("wasm-application-runner.SetApplicationParameters: about to overwrite the existing application parameters!")
-  }
-  auxiliaryParameters = params;
-}
-
-function DoAnimationThread() {
-  if (WasmDoAnimation != null) {
-    WasmDoAnimation();
-  }
-
-  // Update the viewport content every 100ms if need be
-  setTimeout(DoAnimationThread, 100);  
-}
-
-
-function GetUriParameters(): Map<string, string> {
-  var parameters = window.location.search.substr(1);
-
-  if (parameters != null &&
-    parameters != '') {
-    var result = new Map<string, string>();
-    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]);
-      } else if(tmp.length == 1) {
-        // if there is no '=', we treat ot afterwards as a flag-style param
-        result[tmp[0]] = "";
-      }
-    }
-  return result;
-  }
-  else {
-    return new Map<string, string>();
-  }
-}
-
-// function UpdateWebApplication(statusUpdateMessage: string) {
-//   console.log(statusUpdateMessage);
-// }
-
-function _InitializeWasmApplication(orthancBaseUrl: string): void {
-
-  CreateWasmApplication();
-
-  // transmit the API-specified parameters to the app before initializing it
-  for (let key in auxiliaryParameters) {
-    if (auxiliaryParameters.hasOwnProperty(key)) {
-      Logger.defaultLogger.debug(
-        `About to call SetStartupParameter("${key}","${auxiliaryParameters[key]}")`);
-      SetStartupParameter(key, auxiliaryParameters[key]);
-    }
-  }
-
-  // parse uri and transmit the URI parameters to the app before initializing it
-  let parameters = GetUriParameters();
-
-  for (let key in parameters) {
-    if (parameters.hasOwnProperty(key)) {
-      Logger.defaultLogger.debug(
-        `About to call SetStartupParameter("${key}","${parameters[key]}")`);
-      SetStartupParameter(key, parameters[key]);
-    }
-  }
-
-  StartWasmApplication(orthancBaseUrl);
-
-  // trigger a first resize of the canvas that has just been initialized
-  StoneViewport.WasmViewport.ResizeAll();
-
-  DoAnimationThread();
-}
-
-export function InitializeWasmApplication(wasmModuleName: string, orthancBaseUrl: string) {
-  
-  Stone.Framework.Configure(wasmModuleName);
-
-  // 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 () {
-
-    Logger.defaultLogger.debug("Connecting C++ methods to JS methods");
-    
-    SetStartupParameter = (<any> window).StoneFrameworkModule.cwrap('SetStartupParameter', null, ['string', 'string']);
-    CreateWasmApplication = (<any> window).StoneFrameworkModule.cwrap('CreateWasmApplication', null, ['number']);
-    CreateCppViewport = (<any> window).StoneFrameworkModule.cwrap('CreateCppViewport', 'number', []);
-    ReleaseCppViewport = (<any> window).StoneFrameworkModule.cwrap('ReleaseCppViewport', null, ['number']);
-    StartWasmApplication = (<any> window).StoneFrameworkModule.cwrap('StartWasmApplication', null, ['string']);
-    (<any> window).IsTraceLevelEnabled = (<any> window).StoneFrameworkModule.cwrap('WasmIsTraceLevelEnabled', 'boolean', null);
-    (<any> window).IsInfoLevelEnabled = (<any> window).StoneFrameworkModule.cwrap('WasmIsInfoLevelEnabled', 'boolean', null);
-
-    (<any> window).WasmWebService_NotifyCachedSuccess = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifyCachedSuccess', null, ['number']);
-    (<any> window).WasmWebService_NotifySuccess = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifySuccess', null, ['number', 'string', 'array', 'number', 'number']);
-    (<any> window).WasmWebService_NotifyError = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifyError', null, ['number', 'string', 'number', 'number']);
-    (<any> window).WasmDelayedCallExecutor_ExecuteCallback = (<any> window).StoneFrameworkModule.cwrap('WasmDelayedCallExecutor_ExecuteCallback', null, ['number']);
-    // no need to put this into the globals for it's only used in this very module
-    WasmDoAnimation = (<any> window).StoneFrameworkModule.cwrap('WasmDoAnimation', null, []);
-
-    SendSerializedMessageToStoneApplication = (<any> window).StoneFrameworkModule.cwrap('SendSerializedMessageToStoneApplication', 'string', ['string']);
-
-    Logger.defaultLogger.debug("Connecting C++ methods to JS methods - done");
-
-    _InitializeWasmApplication(orthancBaseUrl);
-  });
-}
-
-
-// exports.InitializeWasmApplication = InitializeWasmApplication;
-
-
-
--- a/Platforms/Wasm/wasm-viewport.ts	Wed Apr 29 20:44:31 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,359 +0,0 @@
-import * as wasmApplicationRunner from './wasm-application-runner'
-import * as Logger from './logger'
-
-var isPendingRedraw = false;
-
-function ScheduleWebViewportRedraw(cppViewportHandle: any) : void
-{
-  if (!isPendingRedraw) {
-    isPendingRedraw = true;
-    Logger.defaultLogger.debug('Scheduling a refresh of the viewport, as its content changed');
-    window.requestAnimationFrame(function() {
-      isPendingRedraw = false;
-      let viewport = WasmViewport.GetFromCppViewport(cppViewportHandle);
-      if (viewport) {
-        viewport.Redraw();
-      }
-    });
-  }
-}
-
-(<any>window).ScheduleWebViewportRedraw = ScheduleWebViewportRedraw;
-
-declare function UTF8ToString(v: any): string;
-
-function CreateWasmViewport(htmlCanvasId: string) : any {
-  var cppViewportHandle = wasmApplicationRunner.CreateCppViewport();
-  var canvasId = UTF8ToString(htmlCanvasId);
-  var webViewport = new WasmViewport((<any> window).StoneFrameworkModule, canvasId, cppViewportHandle);  // viewports are stored in a static map in WasmViewport -> won't be deleted
-  webViewport.Initialize();
-
-  return cppViewportHandle;
-}
- 
-(<any>window).CreateWasmViewport = CreateWasmViewport;
-
-export class WasmViewport {
-
-    private static viewportsMapByCppHandle_ : Map<number, WasmViewport> = new Map<number, WasmViewport>(); // key = the C++ handle
-    private static viewportsMapByCanvasId_ : Map<string, WasmViewport> = new Map<string, WasmViewport>(); // key = the canvasId
-
-    private module_ : any;
-    private canvasId_ : string;
-    private htmlCanvas_ : HTMLCanvasElement;
-    private context_ : CanvasRenderingContext2D | null;
-    private imageData_ : any = null;
-    private renderingBuffer_ : any = null;
-    
-    private touchGestureInProgress_: boolean = false;
-    private touchCount_: number = 0;
-    private touchGestureLastCoordinates_: [number, number][] = []; // last x,y coordinates of each touch
-    
-    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 ViewportTouchStart : Function;
-    private ViewportTouchMove : Function;
-    private ViewportTouchEnd : Function;
-
-    private pimpl_ : any; // Private pointer to the underlying WebAssembly C++ object
-
-    public constructor(module: any, canvasId: string, cppViewport: any) {
-      
-      this.pimpl_ = cppViewport;
-      WasmViewport.viewportsMapByCppHandle_[this.pimpl_] = this;
-      WasmViewport.viewportsMapByCanvasId_[canvasId] = this;
-
-      this.module_ = module;
-      this.canvasId_ = canvasId;
-      this.htmlCanvas_ = document.getElementById(this.canvasId_) as HTMLCanvasElement;
-      if (this.htmlCanvas_ == null) {
-        Logger.defaultLogger.error("Can not create WasmViewport, did not find the canvas whose id is '", this.canvasId_, "'");
-      }
-      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', 'number', 'string', 'number', 'number' ]);
-      this.ViewportTouchStart = this.module_.cwrap('ViewportTouchStart', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]);
-      this.ViewportTouchMove = this.module_.cwrap('ViewportTouchMove', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]);
-      this.ViewportTouchEnd = this.module_.cwrap('ViewportTouchEnd', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]);
-    }
-
-    public GetCppViewport() : number {
-      return this.pimpl_;
-    }
-
-    public static GetFromCppViewport(cppViewportHandle: number) : WasmViewport | null {
-      if (WasmViewport.viewportsMapByCppHandle_[cppViewportHandle] !== undefined) {
-        return WasmViewport.viewportsMapByCppHandle_[cppViewportHandle];
-      }
-      Logger.defaultLogger.error("WasmViewport not found !");
-      return null;
-    }
-
-    public static GetFromCanvasId(canvasId: string) : WasmViewport | null {
-      if (WasmViewport.viewportsMapByCanvasId_[canvasId] !== undefined) {
-        return WasmViewport.viewportsMapByCanvasId_[canvasId];
-      }
-      Logger.defaultLogger.error("WasmViewport not found !");
-      return null;
-    }
-
-    public static ResizeAll() {
-      for (let canvasId in WasmViewport.viewportsMapByCanvasId_) {
-        WasmViewport.viewportsMapByCanvasId_[canvasId].Resize();
-      }
-    }
-
-    public Redraw() {
-      if (this.imageData_ === null ||
-          this.renderingBuffer_ === null ||
-          this.ViewportRender(this.pimpl_,
-                         this.imageData_.width,
-                         this.imageData_.height,
-                         this.renderingBuffer_) == 0) {
-        Logger.defaultLogger.error('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_.HEAPU8.buffer,
-          this.renderingBuffer_,
-          this.imageData_.width * this.imageData_.height * 4));
-        
-        if (this.context_) {
-          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 is defined by the parent width/height
-      if (this.htmlCanvas_.parentElement) {
-        this.htmlCanvas_.width = this.htmlCanvas_.parentElement.offsetWidth;  
-        this.htmlCanvas_.height = this.htmlCanvas_.parentElement.offsetHeight;  
-
-        Logger.defaultLogger.debug("resizing WasmViewport: ", this.htmlCanvas_.width, "x", this.htmlCanvas_.height);
-
-        if (this.imageData_ === null && this.context_) {
-          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);
-        } else {
-          this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height);
-        }
-        
-        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 detect modifier keys*/);    
-      });
-    
-      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) {
-        var keyChar: string | null = event.key;
-        var keyCode = event.keyCode
-        if (keyChar.length == 1) {
-          keyCode = 0; // maps to OrthancStone::KeyboardKeys_Generic
-        } else {
-          keyChar = null;
-        }
-//        console.log("key: ", keyCode, keyChar);
-        that.ViewportKeyPressed(that.pimpl_, keyCode, keyChar, 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();
-      }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface
-
-      this.htmlCanvas_.addEventListener('touchstart', function(event: TouchEvent) {
-        // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport)
-        event.preventDefault();
-        event.stopPropagation();
-
-        // TODO: find a way to pass the coordinates as an array between JS and C++
-        var x0 = 0;
-        var y0 = 0;
-        var x1 = 0;
-        var y1 = 0;
-        var x2 = 0;
-        var y2 = 0;
-        if (event.targetTouches.length > 0) {
-          x0 = event.targetTouches[0].pageX;
-          y0 = event.targetTouches[0].pageY;
-        }
-        if (event.targetTouches.length > 1) {
-          x1 = event.targetTouches[1].pageX;
-          y1 = event.targetTouches[1].pageY;
-        }
-        if (event.targetTouches.length > 2) {
-          x2 = event.targetTouches[2].pageX;
-          y2 = event.targetTouches[2].pageY;
-        }
-
-        that.ViewportTouchStart(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2);
-      }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface
-    
-      this.htmlCanvas_.addEventListener('touchend', function(event) {
-        // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport)
-        event.preventDefault();
-        event.stopPropagation();
-
-        // TODO: find a way to pass the coordinates as an array between JS and C++
-        var x0 = 0;
-        var y0 = 0;
-        var x1 = 0;
-        var y1 = 0;
-        var x2 = 0;
-        var y2 = 0;
-        if (event.targetTouches.length > 0) {
-          x0 = event.targetTouches[0].pageX;
-          y0 = event.targetTouches[0].pageY;
-        }
-        if (event.targetTouches.length > 1) {
-          x1 = event.targetTouches[1].pageX;
-          y1 = event.targetTouches[1].pageY;
-        }
-        if (event.targetTouches.length > 2) {
-          x2 = event.targetTouches[2].pageX;
-          y2 = event.targetTouches[2].pageY;
-        }
-
-        that.ViewportTouchEnd(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2);
-      });
-    
-      this.htmlCanvas_.addEventListener('touchmove', function(event: TouchEvent) {
-
-        // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport)
-        event.preventDefault();
-        event.stopPropagation();
-
-
-        // TODO: find a way to pass the coordinates as an array between JS and C++
-        var x0 = 0;
-        var y0 = 0;
-        var x1 = 0;
-        var y1 = 0;
-        var x2 = 0;
-        var y2 = 0;
-        if (event.targetTouches.length > 0) {
-          x0 = event.targetTouches[0].pageX;
-          y0 = event.targetTouches[0].pageY;
-        }
-        if (event.targetTouches.length > 1) {
-          x1 = event.targetTouches[1].pageX;
-          y1 = event.targetTouches[1].pageY;
-        }
-        if (event.targetTouches.length > 2) {
-          x2 = event.targetTouches[2].pageX;
-          y2 = event.targetTouches[2].pageY;
-        }
-
-        that.ViewportTouchMove(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2);
-        return;
-
-      }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface
-    }  
-
-  public ResetTouch() {
-    if (this.touchTranslation_ ||
-        this.touchZoom_) {
-      this.ViewportMouseUp(this.pimpl_);
-    }
-
-    this.touchTranslation_ = false;
-    this.touchZoom_ = false;
-  }
-  
-  public GetTouchTranslation(event: any) {
-    var touch = event.targetTouches[0];
-    return [
-      touch.pageX,
-      touch.pageY
-    ];
-  }
-    
-  public GetTouchZoom(event: any) {
-    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
-    ];
-  }
-   
-}